View source
<?php
declare (strict_types=1);
namespace Drupal\date_recur\Plugin\Field\FieldWidget;
use Drupal\Component\Utility\NestedArray;
use Drupal\Core\Datetime\DrupalDateTime;
use Drupal\Core\Datetime\Element\Datetime;
use Drupal\Core\Field\FieldItemListInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\date_recur\DateRecurHelper;
use Drupal\date_recur\Plugin\Field\FieldType\DateRecurItem;
use Drupal\datetime\Plugin\Field\FieldType\DateTimeItem;
use Drupal\datetime_range\Plugin\Field\FieldWidget\DateRangeDefaultWidget;
class DateRecurBasicWidget extends DateRangeDefaultWidget {
public function formElement(FieldItemListInterface $items, $delta, array $element, array &$form, FormStateInterface $form_state) : array {
$element = parent::formElement($items, $delta, $element, $form, $form_state);
$element['#theme'] = 'date_recur_basic_widget';
$element['#element_validate'][] = [
$this,
'validateRrule',
];
$element['value']['#default_value'] = $element['end_value']['#default_value'] = NULL;
$element['value']['#date_timezone'] = $element['end_value']['#date_timezone'] = NULL;
$this
->createDateRecurDefaultValue($element, $items[$delta]);
$element['first_occurrence'] = [
'#type' => 'fieldset',
'#title' => $this
->t('First occurrence'),
'#weight' => 0,
];
$firstOccurrenceParents = array_merge($element['#field_parents'], [
$this->fieldDefinition
->getName(),
$delta,
'first_occurrence',
]);
$element['value']['#title'] = $this
->t('Start');
$element['end_value']['#title'] = $this
->t('End');
$element['end_value']['#description'] = $this
->t('Leave end empty to copy start date; the occurrence will therefore not have any duration.');
$element['end_value']['#required'] = FALSE;
$element['value']['#group'] = $element['end_value']['#group'] = implode('][', $firstOccurrenceParents);
$element['value']['#value_callback'] = $element['end_value']['#value_callback'] = [
$this,
'dateValueCallback',
];
$timeZone = $items[$delta]->timezone ?? NULL;
$zones = $this
->getTimeZoneOptions();
$element['timezone'] = [
'#type' => 'select',
'#title' => t('Time zone'),
'#default_value' => $timeZone,
'#options' => $zones,
];
$element['rrule'] = [
'#type' => 'textarea',
'#default_value' => $items[$delta]->rrule ?? NULL,
'#title' => $this
->t('Repeat rule'),
'#description' => $this
->t('Repeat rule in <a href=":link">iCalendar Recurrence Rule</a> (RRULE) format.', [
':link' => 'https://icalendar.org/iCalendar-RFC-5545/3-8-5-3-recurrence-rule.html',
]),
];
return $element;
}
public function dateValueCallback(array $element, $input, FormStateInterface $form_state) : array {
if ($input !== FALSE) {
$timeZonePath = array_slice($element['#parents'], 0, -1);
$timeZonePath[] = 'timezone';
$submittedTimeZone = NestedArray::getValue($form_state
->getUserInput(), $timeZonePath);
if (!isset($submittedTimeZone)) {
$timeZoneFieldPath = array_slice($element['#array_parents'], 0, -1);
$timeZoneFieldPath[] = 'timezone';
$timeZoneField = NestedArray::getValue($form_state
->getCompleteForm(), $timeZoneFieldPath);
$submittedTimeZone = isset($timeZoneField['#value']) ? $timeZoneField['#value'] : $timeZoneField['#default_value'] ?? NULL;
}
$allTimeZones = \DateTimeZone::listIdentifiers();
if (!in_array($submittedTimeZone, $allTimeZones)) {
return [
'date' => '',
'time' => '',
'object' => NULL,
];
}
$element['#date_timezone'] = $submittedTimeZone;
}
return Datetime::valueCallback($element, $input, $form_state);
}
public function validateRrule(array &$element, FormStateInterface $form_state, array &$complete_form) : void {
$input = NestedArray::getValue($form_state
->getValues(), $element['#parents']);
$startDate = $input['value'];
$startDateEnd = $input['end_value'];
if (is_array($startDate) || is_array($startDateEnd)) {
return;
}
$rrule = $input['rrule'];
if ($startDateEnd && !isset($startDate)) {
$form_state
->setError($element['value'], (string) $this
->t('Start date must be set if end date is set.'));
}
if (!isset($startDateEnd) && $startDate) {
$form_state
->setValueForElement($element['end_value'], $startDate);
$startDateEnd = $startDate;
}
if (strlen($rrule) > 0 && $startDate) {
try {
DateRecurHelper::create($rrule, $startDate
->getPhpDateTime(), $startDateEnd ? $startDateEnd
->getPhpDateTime() : NULL);
} catch (\Exception $e) {
$form_state
->setError($element['rrule'], (string) $this
->t('Repeat rule is formatted incorrectly.'));
}
}
}
protected function getTimeZoneOptions() : array {
return \system_time_zones(TRUE, TRUE);
}
protected function getCurrentUserTimeZone() : string {
return \date_default_timezone_get();
}
protected function createDefaultValue($date, $timezone) : DrupalDateTime {
assert($date instanceof DrupalDateTime);
assert(is_string($timezone));
if ($this
->getFieldSetting('datetime_type') == DateTimeItem::DATETIME_TYPE_DATE) {
$date
->setDefaultDateTime();
}
return $date;
}
protected function createDateRecurDefaultValue(array &$element, DateRecurItem $item) : void {
$startDate = $item->start_date;
$startDateEnd = $item->end_date;
$timeZone = isset($item->timezone) ? new \DateTimeZone($item->timezone) : NULL;
if ($timeZone) {
$element['value']['#date_timezone'] = $element['end_value']['#date_timezone'] = $timeZone
->getName();
if ($startDate) {
$startDate
->setTimezone($timeZone);
$element['value']['#default_value'] = $this
->createDefaultValue($startDate, $timeZone
->getName());
}
if ($startDateEnd) {
$startDateEnd
->setTimezone($timeZone);
$element['end_value']['#default_value'] = $this
->createDefaultValue($startDateEnd, $timeZone
->getName());
}
}
}
}