diff --git a/core/modules/datetime_range/config/schema/datetime_range.schema.yml b/core/modules/datetime_range/config/schema/datetime_range.schema.yml index f0f93259d3..f0f2ca1edd 100644 --- a/core/modules/datetime_range/config/schema/datetime_range.schema.yml +++ b/core/modules/datetime_range/config/schema/datetime_range.schema.yml @@ -31,6 +31,9 @@ field.formatter.settings.daterange_default: type: field.formatter.settings.datetime_default label: 'Date range default display format settings' mapping: + from_to: + type: string + label: 'Display' separator: type: label label: 'Separator' @@ -40,6 +43,9 @@ field.formatter.settings.daterange_plain: type: field.formatter.settings.datetime_plain label: 'Date range plain display format settings' mapping: + from_to: + type: string + label: 'Display' separator: type: label label: 'Separator' @@ -49,6 +55,9 @@ field.formatter.settings.daterange_custom: type: field.formatter.settings.datetime_custom label: 'Date range custom display format settings' mapping: + from_to: + type: string + label: 'Display' separator: type: label label: 'Separator' diff --git a/core/modules/datetime_range/datetime_range.post_update.php b/core/modules/datetime_range/datetime_range.post_update.php index 83df8ca9b1..4bdf4f1664 100644 --- a/core/modules/datetime_range/datetime_range.post_update.php +++ b/core/modules/datetime_range/datetime_range.post_update.php @@ -14,3 +14,10 @@ function datetime_range_removed_post_updates() { 'datetime_range_post_update_views_string_plugin_id' => '9.0.0', ]; } + +/** + * Update formatter configuration. + */ +function datetime_range_post_update_from_to_configuration() { + // Empty update to set the "from_to" setting. +} diff --git a/core/modules/datetime_range/src/DateTimeRangeTrait.php b/core/modules/datetime_range/src/DateTimeRangeTrait.php index 3f05b82189..d576456f53 100644 --- a/core/modules/datetime_range/src/DateTimeRangeTrait.php +++ b/core/modules/datetime_range/src/DateTimeRangeTrait.php @@ -2,13 +2,25 @@ namespace Drupal\datetime_range; +use Drupal\Core\Datetime\DrupalDateTime; use Drupal\Core\Field\FieldItemListInterface; +use Drupal\Core\Form\FormStateInterface; /** * Provides friendly methods for datetime range. */ trait DateTimeRangeTrait { + /** + * {@inheritdoc} + */ + public static function dateTimeRangeDefaultSettings() { + return [ + 'from_to' => 'both', + 'separator' => '-', + ]; + } + /** * {@inheritdoc} */ @@ -24,11 +36,7 @@ public function viewElements(FieldItemListInterface $items, $langcode) { $end_date = $item->end_date; if ($start_date->getTimestamp() !== $end_date->getTimestamp()) { - $elements[$delta] = [ - 'start_date' => $this->buildDateWithIsoAttribute($start_date), - 'separator' => ['#plain_text' => ' ' . $separator . ' '], - 'end_date' => $this->buildDateWithIsoAttribute($end_date), - ]; + $elements[$delta] = $this->renderStartEndWithIsoAttribute($start_date, $separator, $end_date); } else { $elements[$delta] = $this->buildDateWithIsoAttribute($start_date); @@ -46,4 +54,149 @@ public function viewElements(FieldItemListInterface $items, $langcode) { return $elements; } + /** + * {@inheritdoc} + */ + public function dateTimeRangeSettingsForm(array $form, FormStateInterface $form_state) { + $form['from_to'] = [ + '#type' => 'select', + '#title' => $this->t('Display'), + '#options' => $this->getFromToOptions(), + '#default_value' => $this->getSetting('from_to'), + ]; + + $form['separator'] = [ + '#type' => 'textfield', + '#title' => $this->t('Date separator'), + '#description' => $this->t('The string to separate the start and end dates'), + '#default_value' => $this->getSetting('separator'), + '#states' => [ + 'visible' => [ + ':input[name="options[settings][from_to]"]' => ['value' => 'both'], + ], + ], + ]; + + return $form; + } + + /** + * {@inheritdoc} + */ + public function dateTimeRangeSettingsSummary() { + $summary = []; + if ($from_to = $this->getSetting('from_to')) { + $from_to_options = $this->getFromToOptions(); + if (isset($from_to_options[$from_to])) { + $summary[] = $from_to_options[$from_to]; + } + } + + if (($separator = $this->getSetting('separator')) && $this->getSetting('from_to') === 'both') { + $summary[] = $this->t('Separator: %separator', ['%separator' => $separator]); + } + + return $summary; + } + + /** + * Returns a list of possible values for the 'from_to' setting. + * + * @return array + * A list of 'from_to' options. + */ + protected function getFromToOptions() { + return [ + 'both' => $this->t('Display both Start and End dates'), + 'start_date' => $this->t('Display Start date only'), + 'end_date' => $this->t('Display End date only'), + ]; + } + + /** + * Returns whether or not the start date should be displayed. + * + * @return bool + * True if the start date should be displayed. False otherwise. + */ + public function startDateIsDisplayed() { + switch ($this->getSetting('from_to')) { + case 'both': + case 'start_date': + return TRUE; + } + + return FALSE; + } + + /** + * Returns whether or not the end date should be displayed. + * + * @return bool + * True if the end date should be displayed. False otherwise. + */ + public function endDateIsDisplayed() { + switch ($this->getSetting('from_to')) { + case 'both': + case 'end_date': + return TRUE; + } + + return FALSE; + } + + /** + * Creates a render array given start/end dates. + * + * @param \Drupal\Core\Datetime\DrupalDateTime $start_date + * The start date to be rendered. + * @param string $separator + * The separator string. + * @param \Drupal\Core\Datetime\DrupalDateTime $end_date + * The end date to be rendered. + * + * @return array + * A renderable array for a single date time range. + */ + protected function renderStartEnd(DrupalDateTime $start_date, string $separator, DrupalDateTime $end_date): array { + $element = []; + if ($this->startDateIsDisplayed()) { + $element['start_date'] = $this->buildDate($start_date); + } + if ($this->startDateIsDisplayed() && $this->endDateIsDisplayed()) { + $element['separator'] = ['#plain_text' => ' ' . $separator . ' ']; + } + if ($this->endDateIsDisplayed()) { + $element['end_date'] = $this->buildDate($end_date); + } + return $element; + } + + /** + * Creates a render array with ISO attributes given start/end dates. + * + * @param \Drupal\Core\Datetime\DrupalDateTime $start_date + * The start date to be rendered. + * @param string $separator + * The separator string. + * @param \Drupal\Core\Datetime\DrupalDateTime $end_date + * The end date to be rendered. + * + * @return array + * A renderable array for a single date time range. + */ + protected function renderStartEndWithIsoAttribute(DrupalDateTime $start_date, string $separator, DrupalDateTime $end_date): array { + $element = []; + if ($this->startDateIsDisplayed()) { + $element['start_date'] = $this->buildDateWithIsoAttribute($start_date); + } + if ($this->startDateIsDisplayed() && $this->endDateIsDisplayed()) { + $element['separator'] = ['#plain_text' => ' ' . $separator . ' ']; + } + if ($this->endDateIsDisplayed()) { + $element['end_date'] = $this->buildDateWithIsoAttribute($end_date); + } + return $element; + } + } diff --git a/core/modules/datetime_range/src/Plugin/Field/FieldFormatter/DateRangeCustomFormatter.php b/core/modules/datetime_range/src/Plugin/Field/FieldFormatter/DateRangeCustomFormatter.php index 78aa8aafe2..5578d1b8b0 100644 --- a/core/modules/datetime_range/src/Plugin/Field/FieldFormatter/DateRangeCustomFormatter.php +++ b/core/modules/datetime_range/src/Plugin/Field/FieldFormatter/DateRangeCustomFormatter.php @@ -29,9 +29,7 @@ class DateRangeCustomFormatter extends DateTimeCustomFormatter { * {@inheritdoc} */ public static function defaultSettings() { - return [ - 'separator' => '-', - ] + parent::defaultSettings(); + return static::dateTimeRangeDefaultSettings() + parent::defaultSettings(); } /** @@ -52,11 +50,7 @@ public function viewElements(FieldItemListInterface $items, $langcode) { $end_date = $item->end_date; if ($start_date->getTimestamp() !== $end_date->getTimestamp()) { - $elements[$delta] = [ - 'start_date' => $this->buildDate($start_date), - 'separator' => ['#plain_text' => ' ' . $separator . ' '], - 'end_date' => $this->buildDate($end_date), - ]; + $elements[$delta] = $this->renderStartEnd($start_date, $separator, $end_date); } else { $elements[$delta] = $this->buildDate($start_date); @@ -72,14 +66,7 @@ public function viewElements(FieldItemListInterface $items, $langcode) { */ public function settingsForm(array $form, FormStateInterface $form_state) { $form = parent::settingsForm($form, $form_state); - - $form['separator'] = [ - '#type' => 'textfield', - '#title' => $this->t('Date separator'), - '#description' => $this->t('The string to separate the start and end dates'), - '#default_value' => $this->getSetting('separator'), - ]; - + $form = $this->dateTimeRangeSettingsForm($form, $form_state); return $form; } @@ -87,13 +74,7 @@ public function settingsForm(array $form, FormStateInterface $form_state) { * {@inheritdoc} */ public function settingsSummary() { - $summary = parent::settingsSummary(); - - if ($separator = $this->getSetting('separator')) { - $summary[] = $this->t('Separator: %separator', ['%separator' => $separator]); - } - - return $summary; + return array_merge(parent::settingsSummary(), $this->dateTimeRangeSettingsSummary()); } } diff --git a/core/modules/datetime_range/src/Plugin/Field/FieldFormatter/DateRangeDefaultFormatter.php b/core/modules/datetime_range/src/Plugin/Field/FieldFormatter/DateRangeDefaultFormatter.php index b93851bbd0..367fd25ad7 100644 --- a/core/modules/datetime_range/src/Plugin/Field/FieldFormatter/DateRangeDefaultFormatter.php +++ b/core/modules/datetime_range/src/Plugin/Field/FieldFormatter/DateRangeDefaultFormatter.php @@ -29,9 +29,7 @@ class DateRangeDefaultFormatter extends DateTimeDefaultFormatter { * {@inheritdoc} */ public static function defaultSettings() { - return [ - 'separator' => '-', - ] + parent::defaultSettings(); + return static::dateTimeRangeDefaultSettings() + parent::defaultSettings(); } /** @@ -39,13 +37,7 @@ public static function defaultSettings() { */ public function settingsForm(array $form, FormStateInterface $form_state) { $form = parent::settingsForm($form, $form_state); - - $form['separator'] = [ - '#type' => 'textfield', - '#title' => $this->t('Date separator'), - '#description' => $this->t('The string to separate the start and end dates'), - '#default_value' => $this->getSetting('separator'), - ]; + $form = $this->dateTimeRangeSettingsForm($form, $form_state); return $form; } @@ -54,13 +46,7 @@ public function settingsForm(array $form, FormStateInterface $form_state) { * {@inheritdoc} */ public function settingsSummary() { - $summary = parent::settingsSummary(); - - if ($separator = $this->getSetting('separator')) { - $summary[] = $this->t('Separator: %separator', ['%separator' => $separator]); - } - - return $summary; + return array_merge(parent::settingsSummary(), $this->dateTimeRangeSettingsSummary()); } } diff --git a/core/modules/datetime_range/src/Plugin/Field/FieldFormatter/DateRangePlainFormatter.php b/core/modules/datetime_range/src/Plugin/Field/FieldFormatter/DateRangePlainFormatter.php index 0f74fba644..ca63461da6 100644 --- a/core/modules/datetime_range/src/Plugin/Field/FieldFormatter/DateRangePlainFormatter.php +++ b/core/modules/datetime_range/src/Plugin/Field/FieldFormatter/DateRangePlainFormatter.php @@ -29,9 +29,7 @@ class DateRangePlainFormatter extends DateTimePlainFormatter { * {@inheritdoc} */ public static function defaultSettings() { - return [ - 'separator' => '-', - ] + parent::defaultSettings(); + return static::dateTimeRangeDefaultSettings() + parent::defaultSettings(); } /** @@ -49,11 +47,7 @@ public function viewElements(FieldItemListInterface $items, $langcode) { $end_date = $item->end_date; if ($start_date->getTimestamp() !== $end_date->getTimestamp()) { - $elements[$delta] = [ - 'start_date' => $this->buildDate($start_date), - 'separator' => ['#plain_text' => ' ' . $separator . ' '], - 'end_date' => $this->buildDate($end_date), - ]; + $elements[$delta] = $this->renderStartEnd($start_date, $separator, $end_date); } else { $elements[$delta] = $this->buildDate($start_date); @@ -76,14 +70,7 @@ public function viewElements(FieldItemListInterface $items, $langcode) { */ public function settingsForm(array $form, FormStateInterface $form_state) { $form = parent::settingsForm($form, $form_state); - - $form['separator'] = [ - '#type' => 'textfield', - '#title' => $this->t('Date separator'), - '#description' => $this->t('The string to separate the start and end dates'), - '#default_value' => $this->getSetting('separator'), - ]; - + $form = $this->dateTimeRangeSettingsForm($form, $form_state); return $form; } @@ -91,13 +78,7 @@ public function settingsForm(array $form, FormStateInterface $form_state) { * {@inheritdoc} */ public function settingsSummary() { - $summary = parent::settingsSummary(); - - if ($separator = $this->getSetting('separator')) { - $summary[] = $this->t('Separator: %separator', ['%separator' => $separator]); - } - - return $summary; + return array_merge(parent::settingsSummary(), $this->dateTimeRangeSettingsSummary()); } } diff --git a/core/modules/datetime_range/tests/src/Functional/DateRangeFieldTest.php b/core/modules/datetime_range/tests/src/Functional/DateRangeFieldTest.php index 7d477be4fc..2d84615584 100644 --- a/core/modules/datetime_range/tests/src/Functional/DateRangeFieldTest.php +++ b/core/modules/datetime_range/tests/src/Functional/DateRangeFieldTest.php @@ -37,7 +37,7 @@ class DateRangeFieldTest extends DateTestBase { * * @var array */ - protected $defaultSettings = ['timezone_override' => '', 'separator' => '-']; + protected $defaultSettings = ['timezone_override' => '', 'separator' => '-', 'from_to' => 'both']; /** * {@inheritdoc} @@ -1426,4 +1426,170 @@ public function testDateStorageSettings() { $this->assertSession()->elementsCount('xpath', "//*[@id='edit-settings-datetime-type' and contains(@disabled, 'disabled')]", 1); } + /** + * Tests displaying dates with the 'from_to' setting. + * + * @dataProvider fromToSettingDataProvider + */ + public function testFromToSetting(array $expected, $datetime_type, $field_formatter_type, array $display_settings = []) { + $field_name = $this->fieldStorage->getName(); + + // Create a test content type. + $this->drupalCreateContentType(['type' => 'date_content']); + + // Ensure the field to a datetime field. + $this->fieldStorage->setSetting('datetime_type', $datetime_type); + $this->fieldStorage->save(); + + // Build up dates in the UTC timezone. + $value = '2012-12-31 00:00:00'; + $start_date = new DrupalDateTime($value, 'UTC'); + $end_value = '2013-06-06 00:00:00'; + $end_date = new DrupalDateTime($end_value, 'UTC'); + + // Submit a valid date and ensure it is accepted. + $date_format = DateFormat::load('html_date')->getPattern(); + + $edit = [ + "{$field_name}[0][value][date]" => $start_date->format($date_format), + "{$field_name}[0][end_value][date]" => $end_date->format($date_format), + ]; + + // Supply time as well when field is a datetime field. + if ($datetime_type === DateRangeItem::DATETIME_TYPE_DATETIME) { + $time_format = DateFormat::load('html_time')->getPattern(); + $edit["{$field_name}[0][value][time]"] = $start_date->format($time_format); + $edit["{$field_name}[0][end_value][time]"] = $end_date->format($time_format); + } + + $this->drupalGet('entity_test/add'); + $this->submitForm($edit, t('Save')); + preg_match('|entity_test/manage/(\d+)|', $this->getUrl(), $match); + $id = $match[1]; + $this->assertSession()->pageTextContains(t('entity_test @id has been created.', ['@id' => $id])); + + // Now set display options. + $this->displayOptions = [ + 'type' => $field_formatter_type, + 'label' => 'hidden', + 'settings' => $display_settings + [ + 'format_type' => 'short', + 'separator' => 'THESEPARATOR', + ] + $this->defaultSettings, + ]; + + \Drupal::service('entity_display.repository')->getViewDisplay( + $this->field->getTargetEntityTypeId(), + $this->field->getTargetBundle(), + 'full') + ->setComponent($field_name, $this->displayOptions) + ->save(); + + $output = $this->renderTestEntity($id); + foreach ($expected as $content => $is_expected) { + if ($is_expected) { + $this->assertStringContainsString($content, $output); + } + else { + $this->assertStringNotContainsString($content, $output); + } + } + } + + /** + * The data provider for testing the 'from_to' setting. + * + * @return array + * An array of different date settings to test the behavior of the 'from_to' setting. + */ + public function fromToSettingDataProvider() { + $datetime_types = [ + DateRangeItem::DATETIME_TYPE_DATE => [ + 'daterange_default' => [ + 'start_date' => '12/31/2012', + 'end_date' => '06/06/2013', + ], + 'daterange_plain' => [ + 'start_date' => '2012-12-31', + 'end_date' => '2013-06-06', + ], + 'daterange_custom' => [ + 'start_date' => '2012-12-31', + 'end_date' => '2013-06-06', + ], + ], + DateRangeItem::DATETIME_TYPE_DATETIME => [ + 'daterange_default' => [ + 'start_date' => '12/31/2012 - 00:00', + 'end_date' => '06/06/2013 - 00:00', + ], + 'daterange_plain' => [ + 'start_date' => '2012-12-31T00:00:00', + 'end_date' => '2013-06-06T00:00:00', + ], + 'daterange_custom' => [ + 'start_date' => '2012-12-31T00:00:00', + 'end_date' => '2013-06-06T00:00:00', + ], + ], + DateRangeItem::DATETIME_TYPE_ALLDAY => [ + 'daterange_default' => [ + 'start_date' => '12/31/2012', + 'end_date' => '06/06/2013', + ], + 'daterange_plain' => [ + 'start_date' => '2012-12-31', + 'end_date' => '2013-06-06', + ], + 'daterange_custom' => [ + 'start_date' => '2012-12-31', + 'end_date' => '2013-06-06', + ], + ], + ]; + + $return = []; + $separator = ' THESEPARATOR '; + foreach ($datetime_types as $datetime_type => $field_formatters) { + foreach ($field_formatters as $field_formatter_type => $dates) { + // Both start and end date. + $return[$datetime_type . '-' . $field_formatter_type . '-both'] = [ + 'expected' => [ + $dates['start_date'] => TRUE, + $separator => TRUE, + $dates['end_date'] => TRUE, + ], + 'datetime_type' => $datetime_type, + 'field_formatter_type' => $field_formatter_type, + ]; + + // Only start date. + $return[$datetime_type . '-' . $field_formatter_type . '-start_date'] = [ + 'expected' => [ + $dates['start_date'] => TRUE, + $separator => FALSE, + $dates['end_date'] => FALSE, + ], + 'datetime_type' => $datetime_type, + 'field_formatter_type' => $field_formatter_type, + ['from_to' => 'start_date'], + ]; + + // Only end date. + $return[$datetime_type . '-' . $field_formatter_type . '-end_date'] = [ + 'expected' => [ + $dates['start_date'] => FALSE, + $separator => FALSE, + $dates['end_date'] => TRUE, + ], + 'datetime_type' => $datetime_type, + 'field_formatter_type' => $field_formatter_type, + ['from_to' => 'end_date'], + ]; + } + } + + return $return; + } + }