diff --git a/core/modules/datetime/datetime.views.inc b/core/modules/datetime/datetime.views.inc index d3b0d18617..46d666539a 100644 --- a/core/modules/datetime/datetime.views.inc +++ b/core/modules/datetime/datetime.views.inc @@ -11,18 +11,38 @@ * Implements hook_field_views_data(). */ function datetime_field_views_data(FieldStorageConfigInterface $field_storage) { + return datetime_type_field_views_data($field_storage, [], $field_storage->getMainPropertyName()); +} + +/** + * Helper for datetime based fields. + * + * Override the default Views data for a datetime based fields, + * adding datetime views plugins. + * + * @param FieldStorageConfigInterface $field_storage + * The field storage config entity. + * @param array $data + * Field view data or views_field_default_views_data($field_storage) if empty. + * @param string $column_name + * The schema column name with the datetime value. + * + * @return array + * The array of field views data with the datetime plugin. + */ +function datetime_type_field_views_data(FieldStorageConfigInterface $field_storage, $data, $column_name) { // @todo This code only covers configurable fields, handle base table fields // in https://www.drupal.org/node/2489476. - $data = views_field_default_views_data($field_storage); + $data = (empty($data)) ? views_field_default_views_data($field_storage) : $data; foreach ($data as $table_name => $table_data) { // Set the 'datetime' filter type. - $data[$table_name][$field_storage->getName() . '_value']['filter']['id'] = 'datetime'; + $data[$table_name][$field_storage->getName() . '_' . $column_name]['filter']['id'] = 'datetime'; // Set the 'datetime' argument type. - $data[$table_name][$field_storage->getName() . '_value']['argument']['id'] = 'datetime'; + $data[$table_name][$field_storage->getName() . '_' . $column_name]['argument']['id'] = 'datetime'; // Create year, month, and day arguments. - $group = $data[$table_name][$field_storage->getName() . '_value']['group']; + $group = $data[$table_name][$field_storage->getName() . '_' . $column_name]['group']; $arguments = [ // Argument type => help text. 'year' => t('Date in the form of YYYY.'), @@ -33,11 +53,16 @@ function datetime_field_views_data(FieldStorageConfigInterface $field_storage) { 'full_date' => t('Date in the form of CCYYMMDD.'), ]; foreach ($arguments as $argument_type => $help_text) { - $data[$table_name][$field_storage->getName() . '_value_' . $argument_type] = [ - 'title' => $field_storage->getLabel() . ' (' . $argument_type . ')', + $column_name_text = $column_name !== $field_storage->getMainPropertyName() ? ':' . $column_name : ''; + $data[$table_name][$field_storage->getName() . '_' . $column_name . '_' . $argument_type] = [ + 'title' => t('@label@column (@argument)', [ + '@label' => $field_storage->getLabel(), + '@column' => $column_name_text, + '@argument' => $argument_type, + ]), 'help' => $help_text, 'argument' => [ - 'field' => $field_storage->getName() . '_value', + 'field' => $field_storage->getName() . '_' . $column_name, 'id' => 'datetime_' . $argument_type, ], 'group' => $group, @@ -45,7 +70,7 @@ function datetime_field_views_data(FieldStorageConfigInterface $field_storage) { } // Set the 'datetime' sort handler. - $data[$table_name][$field_storage->getName() . '_value']['sort']['id'] = 'datetime'; + $data[$table_name][$field_storage->getName() . '_' . $column_name]['sort']['id'] = 'datetime'; } return $data; diff --git a/core/modules/datetime_range/datetime_range.module b/core/modules/datetime_range/datetime_range.module index b2b87dae7a..2cc7a6888c 100644 --- a/core/modules/datetime_range/datetime_range.module +++ b/core/modules/datetime_range/datetime_range.module @@ -6,6 +6,8 @@ */ use Drupal\Core\Routing\RouteMatchInterface; +use Drupal\views\Views; +use Drupal\views\ViewEntityInterface; /** * Implements hook_help(). @@ -26,3 +28,129 @@ function datetime_range_help($route_name, RouteMatchInterface $route_match) { return $output; } } + +/** + * Implements hook_view_presave(). + * + * Provides BC layer for modules using datetime_range filter/sort plugins. + * + * @deprecated in Drupal 8.4.x and will be removed before 9.0.0. + * + * @see https://www.drupal.org/node/2857691 + */ +function datetime_range_view_presave(ViewEntityInterface $view) { + $config_factory = \Drupal::configFactory(); + $displays = $view->get('display'); + $changed = FALSE; + + foreach ($displays as $display_name => &$display) { + + // Update datetime_range filters. + if (isset($display['display_options']['filters'])) { + foreach ($display['display_options']['filters'] as $field_name => &$filter) { + if ($filter['plugin_id'] === 'string') { + + // Get field config. + $filter_views_data = Views::viewsData()->get($filter['table'])[$filter['field']]['filter']; + if (!isset($filter_views_data['entity_type']) || !isset($filter_views_data['field_name'])) { + continue; + } + $field_storage_name = 'field.storage.' . $filter_views_data['entity_type'] . '.' . $filter_views_data['field_name']; + $field_configuration = $config_factory->get($field_storage_name); + + if ($field_configuration->get('type') === 'daterange') { + + // Set entity_type and field_name if missing. + if (!isset($filter['entity_type'])) { + $filter['entity_type'] = $filter_views_data['entity_type']; + } + + // Set datetime plugin_id. + $filter['plugin_id'] = 'datetime'; + + // Create datetime value array. + $datetime_value = [ + 'min' => '', + 'max' => '', + 'value' => $filter['value'], + 'type' => 'date', + ]; + + // Map string operator/value to numeric equivalent. + switch ($filter['operator']) { + case '=': + case 'empty': + case 'not empty': + $operator = $filter['operator']; + break; + + case '!=': + case 'not': + $operator = '!='; + break; + + case 'starts': + $operator = 'regular_expression'; + $datetime_value['value'] = '^' . preg_quote($datetime_value['value']); + break; + + case 'ends': + $operator = 'regular_expression'; + $datetime_value['value'] = preg_quote($datetime_value['value']) . '$'; + break; + + default: + $operator = 'regular_expression'; + // Add .* to prevent blank regexes. + if (empty($datetime_value['value'])) { + $datetime_value['value'] = '.*'; + } + else { + $datetime_value['value'] = preg_quote($datetime_value['value']); + } + } + + // Set value and operator. + $filter['value'] = $datetime_value; + $filter['operator'] = $operator; + $changed = TRUE; + @trigger_error('Use of string filters for datetime_range fields is deprecated. Use the datetime filters instead.', E_USER_DEPRECATED); + } + } + } + } + + // Update datetime_range sort handlers. + if (isset($display['display_options']['sorts'])) { + foreach ($display['display_options']['sorts'] as $field_name => &$sort) { + if ($sort['plugin_id'] === 'standard') { + + // Get field config. + $sort_views_data = Views::viewsData()->get($sort['table'])[$sort['field']]['sort']; + if (!isset($sort_views_data['entity_type']) || !isset($sort_views_data['field_name'])) { + continue; + } + $field_storage_name = 'field.storage.' . $sort_views_data['entity_type'] . '.' . $sort_views_data['field_name']; + $field_configuration = $config_factory->get($field_storage_name); + + if ($field_configuration->get('type') === 'daterange') { + + // Set entity_type and field_name if missing. + if (!isset($sort['entity_type'])) { + $sort['entity_type'] = $sort_views_data['entity_type']; + } + + // Set datetime plugin_id. + $sort['plugin_id'] = 'datetime'; + $changed = TRUE; + @trigger_error('Use of daterange sort handlers for datetime_range fields is deprecated. Use the datetime sort handlers instead.', E_USER_DEPRECATED); + } + } + } + } + } + + if ($changed) { + $view->set('display', $displays); + } +} diff --git a/core/modules/datetime_range/datetime_range.post_update.php b/core/modules/datetime_range/datetime_range.post_update.php index b5f3f5d310..8cd82c8670 100644 --- a/core/modules/datetime_range/datetime_range.post_update.php +++ b/core/modules/datetime_range/datetime_range.post_update.php @@ -5,9 +5,88 @@ * Post-update functions for Datetime Range module. */ +use Drupal\views\Views; + /** * Clear caches to ensure schema changes are read. */ function datetime_range_post_update_translatable_separator() { // Empty post-update hook to cause a cache rebuild. } + +/** + * Update existing views using datetime_range fields. + */ +function datetime_range_post_update_views_string_plugin_id() { + + /* @var \Drupal\views\Entity\View[] $views */ + $views = \Drupal::entityTypeManager()->getStorage('view')->loadMultiple(); + $config_factory = \Drupal::configFactory(); + $message = NULL; + $ids = []; + + foreach ($views as $view) { + $displays = $view->get('display'); + $needs_bc_layer_update = FALSE; + + foreach ($displays as $display_name => $display) { + + // Check if datetime_range filters need updates. + if (!$needs_bc_layer_update && isset($display['display_options']['filters'])) { + foreach ($display['display_options']['filters'] as $field_name => $filter) { + if ($filter['plugin_id'] == 'string') { + + // Get field config. + $filter_views_data = Views::viewsData()->get($filter['table'])[$filter['field']]['filter']; + if (!isset($filter_views_data['entity_type']) || !isset($filter_views_data['field_name'])) { + continue; + } + $field_storage_name = 'field.storage.' . $filter_views_data['entity_type'] . '.' . $filter_views_data['field_name']; + $field_configuration = $config_factory->get($field_storage_name); + + if ($field_configuration->get('type') == 'daterange') { + // Trigger the BC layer control. + $needs_bc_layer_update = TRUE; + continue 2; + } + } + } + } + + // Check if datetime_range sort handlers need updates. + if (!$needs_bc_layer_update && isset($display['display_options']['sorts'])) { + foreach ($display['display_options']['sorts'] as $field_name => $sort) { + if ($sort['plugin_id'] == 'standard') { + + // Get field config. + $sort_views_data = Views::viewsData()->get($sort['table'])[$sort['field']]['sort']; + if (!isset($sort_views_data['entity_type']) || !isset($sort_views_data['field_name'])) { + continue; + } + $field_storage_name = 'field.storage.' . $sort_views_data['entity_type'] . '.' . $sort_views_data['field_name']; + $field_configuration = $config_factory->get($field_storage_name); + + if ($field_configuration->get('type') == 'daterange') { + // Trigger the BC layer control. + $needs_bc_layer_update = TRUE; + continue 2; + } + } + } + } + } + + // If current view needs BC layer updates save it and the hook view_presave + // will do the rest. + if ($needs_bc_layer_update) { + $view->save(); + $ids[] = $view->id(); + } + } + + if (!empty($ids)) { + $message = \Drupal::translation()->translate('Updated datetime_range filter/sort plugins for views: @ids', ['@ids' => implode(', ', array_unique($ids))]); + } + + return $message; +} diff --git a/core/modules/datetime_range/datetime_range.views.inc b/core/modules/datetime_range/datetime_range.views.inc new file mode 100644 index 0000000000..0823a84ba3 --- /dev/null +++ b/core/modules/datetime_range/datetime_range.views.inc @@ -0,0 +1,21 @@ +loadInclude('datetime', 'inc', 'datetime.views'); + + // Get datetime field data for value and end_value. + $data = datetime_type_field_views_data($field_storage, [], 'value'); + $data = datetime_type_field_views_data($field_storage, $data, 'end_value'); + + return $data; +}