e99374c20f17b5e63b3b3b78be02fa4724db76a8
[yaffs-website] / web / core / modules / datetime_range / src / Plugin / Field / FieldWidget / DateRangeWidgetBase.php
1 <?php
2
3 namespace Drupal\datetime_range\Plugin\Field\FieldWidget;
4
5 use Drupal\Core\Datetime\DrupalDateTime;
6 use Drupal\Core\Field\FieldItemListInterface;
7 use Drupal\Core\Form\FormStateInterface;
8 use Drupal\datetime\Plugin\Field\FieldType\DateTimeItem;
9 use Drupal\datetime\Plugin\Field\FieldWidget\DateTimeWidgetBase;
10 use Drupal\datetime_range\Plugin\Field\FieldType\DateRangeItem;
11
12 /**
13  * Base class for the 'daterange_*' widgets.
14  */
15 class DateRangeWidgetBase extends DateTimeWidgetBase {
16
17   /**
18    * {@inheritdoc}
19    */
20   public function formElement(FieldItemListInterface $items, $delta, array $element, array &$form, FormStateInterface $form_state) {
21     $element = parent::formElement($items, $delta, $element, $form, $form_state);
22
23     // Wrap all of the select elements with a fieldset.
24     $element['#theme_wrappers'][] = 'fieldset';
25
26     $element['#element_validate'][] = [$this, 'validateStartEnd'];
27     $element['value']['#title'] = $this->t('Start date');
28
29     $element['end_value'] = [
30       '#title' => $this->t('End date'),
31     ] + $element['value'];
32
33     if ($items[$delta]->start_date) {
34       /** @var \Drupal\Core\Datetime\DrupalDateTime $start_date */
35       $start_date = $items[$delta]->start_date;
36       $element['value']['#default_value'] = $this->createDefaultValue($start_date, $element['value']['#date_timezone']);
37     }
38
39     if ($items[$delta]->end_date) {
40       /** @var \Drupal\Core\Datetime\DrupalDateTime $end_date */
41       $end_date = $items[$delta]->end_date;
42       $element['end_value']['#default_value'] = $this->createDefaultValue($end_date, $element['end_value']['#date_timezone']);
43     }
44
45     return $element;
46   }
47
48   /**
49    * {@inheritdoc}
50    */
51   public function massageFormValues(array $values, array $form, FormStateInterface $form_state) {
52     // The widget form element type has transformed the value to a
53     // DrupalDateTime object at this point. We need to convert it back to the
54     // storage timezone and format.
55     foreach ($values as &$item) {
56       if (!empty($item['value']) && $item['value'] instanceof DrupalDateTime) {
57         /** @var \Drupal\Core\Datetime\DrupalDateTime $start_date */
58         $start_date = $item['value'];
59         switch ($this->getFieldSetting('datetime_type')) {
60           case DateRangeItem::DATETIME_TYPE_DATE:
61             // If this is a date-only field, set it to the default time so the
62             // timezone conversion can be reversed.
63             datetime_date_default_time($start_date);
64             $format = DATETIME_DATE_STORAGE_FORMAT;
65             break;
66
67           case DateRangeItem::DATETIME_TYPE_ALLDAY:
68             // All day fields start at midnight on the starting date, but are
69             // stored like datetime fields, so we need to adjust the time.
70             // This function is called twice, so to prevent a double conversion
71             // we need to explicitly set the timezone.
72             $start_date->setTimeZone(timezone_open(drupal_get_user_timezone()));
73             $start_date->setTime(0, 0, 0);
74             $format = DATETIME_DATETIME_STORAGE_FORMAT;
75             break;
76
77           default:
78             $format = DATETIME_DATETIME_STORAGE_FORMAT;
79             break;
80         }
81         // Adjust the date for storage.
82         $start_date->setTimezone(new \DateTimezone(DATETIME_STORAGE_TIMEZONE));
83         $item['value'] = $start_date->format($format);
84       }
85
86       if (!empty($item['end_value']) && $item['end_value'] instanceof DrupalDateTime) {
87         /** @var \Drupal\Core\Datetime\DrupalDateTime $end_date */
88         $end_date = $item['end_value'];
89         switch ($this->getFieldSetting('datetime_type')) {
90           case DateRangeItem::DATETIME_TYPE_DATE:
91             // If this is a date-only field, set it to the default time so the
92             // timezone conversion can be reversed.
93             datetime_date_default_time($end_date);
94             $format = DATETIME_DATE_STORAGE_FORMAT;
95             break;
96
97           case DateRangeItem::DATETIME_TYPE_ALLDAY:
98             // All day fields end at midnight on the end date, but are
99             // stored like datetime fields, so we need to adjust the time.
100             // This function is called twice, so to prevent a double conversion
101             // we need to explicitly set the timezone.
102             $end_date->setTimeZone(timezone_open(drupal_get_user_timezone()));
103             $end_date->setTime(23, 59, 59);
104             $format = DATETIME_DATETIME_STORAGE_FORMAT;
105             break;
106
107           default:
108             $format = DATETIME_DATETIME_STORAGE_FORMAT;
109             break;
110         }
111         // Adjust the date for storage.
112         $end_date->setTimezone(new \DateTimezone(DATETIME_STORAGE_TIMEZONE));
113         $item['end_value'] = $end_date->format($format);
114       }
115     }
116
117     return $values;
118   }
119
120   /**
121    * #element_validate callback to ensure that the start date <= the end date.
122    *
123    * @param array $element
124    *   An associative array containing the properties and children of the
125    *   generic form element.
126    * @param \Drupal\Core\Form\FormStateInterface $form_state
127    *   The current state of the form.
128    * @param array $complete_form
129    *   The complete form structure.
130    */
131   public function validateStartEnd(array &$element, FormStateInterface $form_state, array &$complete_form) {
132     $start_date = $element['value']['#value']['object'];
133     $end_date = $element['end_value']['#value']['object'];
134
135     if ($start_date instanceof DrupalDateTime && $end_date instanceof DrupalDateTime) {
136       if ($start_date->getTimestamp() !== $end_date->getTimestamp()) {
137         $interval = $start_date->diff($end_date);
138         if ($interval->invert === 1) {
139           $form_state->setError($element, $this->t('The @title end date cannot be before the start date', ['@title' => $element['#title']]));
140         }
141       }
142     }
143   }
144
145   /**
146    * Creates a date object for use as a default value.
147    *
148    * This will take a default value, apply the proper timezone for display in
149    * a widget, and set the default time for date-only fields.
150    *
151    * @param \Drupal\Core\Datetime\DrupalDateTime $date
152    *   The UTC default date.
153    * @param string $timezone
154    *   The timezone to apply.
155    *
156    * @return \Drupal\Core\Datetime\DrupalDateTime
157    *   A date object for use as a default value in a field widget.
158    */
159   protected function createDefaultValue($date, $timezone) {
160     // The date was created and verified during field_load(), so it is safe to
161     // use without further inspection.
162     if ($this->getFieldSetting('datetime_type') == DateTimeItem::DATETIME_TYPE_DATE) {
163       // A date without time will pick up the current time, use the default
164       // time.
165       datetime_date_default_time($date);
166     }
167     $date->setTimezone(new \DateTimeZone($timezone));
168     return $date;
169   }
170
171 }