View source
<?php
namespace Drupal\calendar\Plugin\views\row;
use Drupal\calendar\CalendarEvent;
use Drupal\calendar\CalendarHelper;
use Drupal\calendar\CalendarViewsTrait;
use Drupal\Core\Entity\EntityFieldManagerInterface;
use Drupal\Core\Field\BaseFieldDefinition;
use Drupal\datetime\Plugin\Field\FieldType\DateTimeItem;
use Drupal\taxonomy\Entity\Term;
use Drupal\views\Plugin\views\argument\Date;
use Drupal\Core\Datetime\DateFormatter;
use Drupal\Core\Form\FormStateInterface;
use Drupal\views\Plugin\views\display\DisplayPluginBase;
use Drupal\views\Plugin\views\row\RowPluginBase;
use Drupal\views\ViewExecutable;
use Drupal\views\Views;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Drupal\datetime\Plugin\Field\FieldType\DateTimeItemInterface;
use Drupal\Core\StringTranslation\StringTranslationTrait;
class Calendar extends RowPluginBase {
use CalendarViewsTrait;
use StringTranslationTrait;
protected $dateFormatter;
protected $entityType;
protected $entities = [];
protected $dateFields = [];
protected $dateArgument;
protected $fieldManager;
public function init(ViewExecutable $view, DisplayPluginBase $display, array &$options = NULL) {
parent::init($view, $display, $options);
}
public function __construct(array $configuration, $plugin_id, $plugin_definition, DateFormatter $date_formatter, EntityFieldManagerInterface $field_manager) {
parent::__construct($configuration, $plugin_id, $plugin_definition);
$this->dateFormatter = $date_formatter;
$this->fieldManager = $field_manager;
}
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
return new static($configuration, $plugin_id, $plugin_definition, $container
->get('date.formatter'), $container
->get('entity_field.manager'));
}
protected function defineOptions() {
$options = parent::defineOptions();
$options['date_fields'] = [
'default' => [],
];
$options['colors'] = [
'contains' => [
'legend' => [
'default' => '',
],
'calendar_colors_type' => [
'default' => [],
],
'taxonomy_field' => [
'default' => '',
],
'calendar_colors_vocabulary' => [
'default' => [],
],
'calendar_colors_taxonomy' => [
'default' => [],
],
'calendar_colors_group' => [
'default' => [],
],
],
];
return $options;
}
public function buildOptionsForm(&$form, FormStateInterface $form_state) {
parent::buildOptionsForm($form, $form_state);
$form['markup'] = [
'#markup' => $this
->t("The calendar row plugin will format view results as calendar items. Make sure this display has a 'Calendar' format and uses a 'Date' contextual filter, or this plugin will not work correctly."),
];
$form['colors'] = [
'#type' => 'fieldset',
'#title' => $this
->t('Legend Colors'),
'#description' => $this
->t('Set a hex color value (like #ffffff) to use in the calendar legend for each content type. Items with empty values will have no stripe in the calendar and will not be added to the legend.'),
];
$options = [];
if ($this->view
->getBaseTables()['node_field_data']) {
$options['type'] = $this
->t('Based on Content Type');
}
if (\Drupal::moduleHandler()
->moduleExists('taxonomy')) {
$options['taxonomy'] = $this
->t('Based on Taxonomy');
}
if (empty($options)) {
return;
}
$form['colors']['legend'] = [
'#title' => $this
->t('Stripes'),
'#description' => $this
->t('Add stripes to calendar items.'),
'#type' => 'select',
'#options' => $options,
'#empty_value' => (string) $this
->t('None'),
'#default_value' => $this->options['colors']['legend'],
];
if ($this->view
->getBaseTables()['node_field_data']) {
$colors = $this->options['colors']['calendar_colors_type'];
$type_names = node_type_get_names();
foreach ($type_names as $key => $name) {
$form['colors']['calendar_colors_type'][$key] = [
'#title' => $name,
'#default_value' => isset($colors[$key]) ? $colors[$key] : CALENDAR_EMPTY_STRIPE,
'#dependency' => [
'edit-row-options-colors-legend' => [
'type',
],
],
'#type' => 'textfield',
'#size' => 7,
'#maxlength' => 7,
'#element_validate' => [
[
$this,
'validateHexColor',
],
],
'#prefix' => '<div class="calendar-colorpicker-wrapper">',
'#suffix' => '<div class="calendar-colorpicker"></div></div>',
'#attributes' => [
'class' => [
'edit-calendar-colorpicker',
],
],
'#attached' => [
'library' => [
'calendar/calendar.colorpicker',
],
],
] + $this
->visibleOnLegendState('type');
}
}
if (\Drupal::moduleHandler()
->moduleExists('taxonomy')) {
$vocabulary_field_options = [];
$fields = $this->displayHandler
->getOption('fields');
foreach ($fields as $name => $field_info) {
if ($this
->isTermReferenceField($field_info, $this->fieldManager)) {
$vocabulary_field_options[$name] = $field_info['label'] ?: $name;
}
}
if (empty($vocabulary_field_options)) {
return;
}
$form['colors']['taxonomy_field'] = [
'#title' => $this
->t('Term field'),
'#type' => !empty($vocabulary_field_options) ? 'select' : 'hidden',
'#default_value' => $this->options['colors']['taxonomy_field'],
'#empty_value' => (string) $this
->t('None'),
'#description' => $this
->t("Select the taxonomy term field to use when setting stripe colors. This works best for vocabularies with only a limited number of possible terms."),
'#options' => $vocabulary_field_options,
'#dependency' => [
'edit-row-options-colors-legend' => [
'taxonomy',
],
],
] + $this
->visibleOnLegendState('taxonomy');
if (empty($vocabulary_field_options)) {
$form['colors']['taxonomy_field']['#options'] = [
'' => '',
];
$form['colors']['taxonomy_field']['#suffix'] = $this
->t('You must add a term field to this view to use taxonomy stripe values. This works best for vocabularies with only a limited number of possible terms.');
}
$vocab_vids = [];
foreach ($vocabulary_field_options as $field_name => $label) {
$field_config = \Drupal::entityTypeManager()
->getStorage('field_config')
->loadByProperties([
'field_name' => $field_name,
]);
reset($field_config);
$key = key($field_config);
$data = \Drupal::config('field.field.' . $field_config[$key]
->getOriginalId())
->getRawData();
if ($target_bundles = $data['settings']['handler_settings']['target_bundles']) {
reset($target_bundles);
$vocab_vids[$field_name] = key($target_bundles);
}
}
if (empty($vocab_vids)) {
return;
}
$this->options['colors']['calendar_colors_vocabulary'] = $vocab_vids;
$form['colors']['calendar_colors_vocabulary'] = [
'#title' => $this
->t('Vocabulary Legend Types'),
'#type' => 'value',
'#value' => $vocab_vids,
] + $this
->visibleOnLegendState('taxonomy');
$term_colors = $this->options['colors']['calendar_colors_taxonomy'];
foreach ($vocab_vids as $field_name => $vid) {
$vocab = \Drupal::entityTypeManager()
->getStorage("taxonomy_term")
->loadTree($vid);
foreach ($vocab as $key => $term) {
$form['colors']['calendar_colors_taxonomy'][$term->tid] = [
'#title' => $this
->t('@term_name', [
'@term_name' => $term->name,
]),
'#default_value' => isset($term_colors[$term->tid]) ? $term_colors[$term->tid] : CALENDAR_EMPTY_STRIPE,
'#access' => !empty($vocabulary_field_options),
'#dependency_count' => 2,
'#dependency' => [
'edit-row-options-colors-legend' => [
'taxonomy',
],
'edit-row-options-colors-taxonomy-field' => [
$field_name,
],
],
'#type' => 'textfield',
'#size' => 7,
'#maxlength' => 7,
'#element_validate' => [
[
$this,
'validateHexColor',
],
],
'#prefix' => '<div class="calendar-colorpicker-wrapper">',
'#suffix' => '<div class="calendar-colorpicker"></div></div>',
'#attributes' => [
'class' => [
'edit-calendar-colorpicker',
],
],
'#attached' => [
'library' => [
'calendar/calendar.colorpicker',
],
],
] + $this
->visibleOnLegendState('taxonomy');
}
}
}
}
public function validateHexColor(array $element, FormStateInterface $form_state) {
if (!$element['#required'] && empty($element['#value'])) {
return;
}
if (!preg_match('/^#(?:(?:[a-f\\d]{3}){1,2})$/i', $element['#value'])) {
$form_state
->setError($element, $this
->t("'@color' is not a valid hex color", [
'@color' => $element['#value'],
]));
}
else {
$form_state
->setValueForElement($element, $element['#value']);
}
}
public function preRender($result) {
$ids = [];
foreach ($result as $row) {
$entity = $row->_entity;
if (isset($this->view
->getBaseTables()['node_revision'])) {
$this->entities[$entity
->id()] = \Drupal::entityTypeManager()
->getStorage('node')
->loadRevision($entity
->id());
}
else {
$ids[$entity
->id()] = $entity
->id();
}
}
$base_tables = $this->view
->getBaseTables();
$base_table = key($base_tables);
$table_data = Views::viewsData()
->get($base_table);
$this->entityType = $table_data['table']['entity type'];
if (!empty($ids)) {
$this->entities = \Drupal::entityTypeManager()
->getStorage($this->entityType)
->loadMultiple($ids);
}
$data = CalendarHelper::dateViewFields($this->entityType);
$data = $data['name'];
$date_fields = [];
foreach ($this->view
->getDisplay()
->getHandlers('argument') as $handler) {
if ($handler instanceof Date) {
$fieldName = $handler->realField;
$alias = $handler->table . '.' . $fieldName;
$info = $data[$alias];
$field_name = str_replace([
'_value2',
'_value',
], '', $info['real_field_name']);
$date_fields[$field_name] = $info;
$this->dateArgument = $handler;
$this->dateFields = $date_fields;
}
}
}
public function render($row) {
$dateInfo = $this->dateArgument->view->dateInfo;
$id = $row->_entity
->id();
$rows = [];
if (!is_numeric($id)) {
return [];
}
static $used = '';
if ($id != $used) {
$used = $id;
}
else {
return [];
}
foreach ($this->dateFields as $field_name => $info) {
$entity = clone $this->entities[$id];
if (empty($entity)) {
return [];
}
$event = new CalendarEvent($entity);
$entity->date_id = [];
$item_start_date = NULL;
$item_end_date = NULL;
$granularity = 'month';
$increment = 1;
$entity_field_name = str_replace('_value', '', $dateInfo
->getDateArgument()->realField);
$field_definition = $entity
->getFieldDefinition($entity_field_name);
if ($field_definition instanceof BaseFieldDefinition) {
$storage_format = 'U';
}
else {
$datetime_type = $field_definition
->getSetting('datetime_type');
if ($datetime_type === DateTimeItem::DATETIME_TYPE_DATE) {
$storage_format = DateTimeItemInterface::DATE_STORAGE_FORMAT;
}
else {
$storage_format = DateTimeItemInterface::DATETIME_STORAGE_FORMAT;
}
}
$items = $entity
->get($field_name)
->getValue();
$timezone = new \DateTimeZone(DateTimeItemInterface::STORAGE_TIMEZONE);
$event_date_value = isset($row->{$info['query_name']}) ? $row->{$info['query_name']} : $row->_entity
->get($entity_field_name)
->getString();
foreach ($items as $item) {
$event = clone $event;
if (isset($item)) {
$item_start_date = \DateTime::createFromFormat($storage_format, $item['value'], $timezone);
}
if (isset($item) && !empty($item['end_value'])) {
$item_end_date = \DateTime::createFromFormat($storage_format, $item['end_value'], $timezone);
}
else {
$item_end_date = $item_start_date;
}
$entity->date_id = [
'calendar.' . $id . '.' . $field_name . '.0',
];
if (empty($item_start_date)) {
continue;
}
$event
->setStartDate($item_start_date);
$event
->setEndDate($item_end_date);
$event
->setTimezone(new \DateTimeZone(timezone_name_get($dateInfo
->getTimezone())));
$event
->setGranularity($granularity);
$event->date_id = $entity->date_id[0];
$events = $this
->explodeValues($event);
foreach ($events as $event) {
switch ($this->options['colors']['legend']) {
case 'type':
if ($event
->getEntityTypeId() == 'node') {
$this
->nodeTypeStripe($event);
}
break;
case 'taxonomy':
$this
->calendarTaxonomyStripe($event);
break;
}
$rows[] = $event;
}
}
}
return $rows;
}
public function explodeValues(CalendarEvent $event) {
$rows = [];
$dateInfo = $this->dateArgument->view->dateInfo;
$item_start_date = $event
->getStartDate()
->getTimestamp();
$item_end_date = $event
->getEndDate()
->getTimestamp();
$now = $event
->getStartDate()
->format('Y-m-d');
$to = $event
->getEndDate()
->format('Y-m-d');
$next = new \DateTime();
$next
->setTimestamp($event
->getStartDate()
->getTimestamp());
if (timezone_name_get($this->dateArgument->view->dateInfo
->getTimezone()) != $event
->getTimezone()
->getName()) {
$next
->setTimezone($event
->getTimezone());
}
if (empty($to) || $now > $to) {
$to = $now;
}
$position = 0;
while (!empty($now) && $now <= $to) {
$entity = clone $event;
$start = $this->dateFormatter
->format($next
->getTimestamp(), 'custom', 'Y-m-d H:i:s');
$next
->setTimestamp(strtotime(' +1 day -1 second', $next
->getTimestamp()));
$end = $this->dateFormatter
->format($next
->getTimestamp(), 'custom', 'Y-m-d H:i:s');
$item_start = $this->dateFormatter
->format($item_start_date, 'custom', 'Y-m-d H:i:s');
$item_end = $this->dateFormatter
->format($item_end_date, 'custom', 'Y-m-d H:i:s');
$start_string = $item_start < $start ? $start : $item_start;
$end_string = !empty($item_end) ? $item_end > $end ? $end : $item_end : NULL;
$entity->calendar_start_date = new \DateTime($start_string);
$entity->calendar_end_date = new \DateTime($end_string);
$granularity = 'day';
$increment = 1;
$entity
->setAllDay(CalendarHelper::dateIsAllDay($entity
->getStartDate()
->format('Y-m-d H:i:s'), $entity
->getEndDate()
->format('Y-m-d H:i:s'), $granularity, $increment));
$calendar_start = $this->dateFormatter
->format($entity->calendar_start_date
->getTimestamp(), 'custom', 'Y-m-d H:i:s');
$calendar_end = $this->dateFormatter
->format($entity->calendar_end_date
->getTimestamp(), 'custom', 'Y-m-d H:i:s');
if (isset($entity) && empty($calendar_start)) {
unset($entity);
}
else {
$entity->date_id .= '.' . $position;
$rows[] = $entity;
unset($entity);
}
$next
->setTimestamp(strtotime('+1 second', $next
->getTimestamp()));
$now = $this->dateFormatter
->format($next
->getTimestamp(), 'custom', 'Y-m-d');
$position++;
}
return $rows;
}
public function nodeTypeStripe(CalendarEvent &$event) {
$colors = isset($this->options['colors']['calendar_colors_type']) ? $this->options['colors']['calendar_colors_type'] : [];
if (empty($colors)) {
return;
}
$type_names = node_type_get_names();
$bundle = $event
->getBundle();
$label = '';
$stripeHex = '';
if (array_key_exists($bundle, $type_names) || $colors[$bundle] == CALENDAR_EMPTY_STRIPE) {
$label = $type_names[$bundle];
}
if (array_key_exists($bundle, $colors)) {
$stripeHex = $colors[$bundle];
}
$event
->addStripeLabel($label);
$event
->addStripeHex($stripeHex);
}
public function calendarTaxonomyStripe(CalendarEvent &$event) {
$colors = isset($this->options['colors']['calendar_colors_taxonomy']) ? $this->options['colors']['calendar_colors_taxonomy'] : [];
if (empty($colors)) {
return;
}
$entity = $event
->getEntity();
$term_field_name = $this->options['colors']['taxonomy_field'];
if ($entity
->hasField($term_field_name) && ($terms_for_entity = $entity
->get($term_field_name))) {
foreach ($terms_for_entity as $item) {
$tid = $item
->getValue()['target_id'];
$term = Term::load($tid);
if (!array_key_exists($tid, $colors) || $colors[$tid] == CALENDAR_EMPTY_STRIPE) {
continue;
}
$event
->addStripeLabel($term->name->value);
$event
->addStripeHex($colors[$tid]);
}
}
return;
}
protected function visibleOnLegendState($mode) {
return [
'#states' => [
'visible' => [
':input[name="row_options[colors][legend]"]' => [
'value' => $mode,
],
],
],
];
}
}