Chris@0: listAll('views.view.') as $view_config_name) { Chris@0: $view = $config_factory->getEditable($view_config_name); Chris@0: Chris@0: $displays = $view->get('display'); Chris@0: Chris@0: foreach ($displays as $display_name => $display) { Chris@0: if (!empty($display['display_options']['fields'])) { Chris@0: foreach ($display['display_options']['fields'] as $field_name => $field) { Chris@0: if (isset($field['entity_type']) && $field['plugin_id'] === 'date') { Chris@0: $ids[] = $view->get('id'); Chris@0: Chris@0: // Grab the settings we need to move to a different place in the Chris@0: // config schema. Chris@0: $date_format = !empty($field['date_format']) ? $field['date_format'] : 'medium'; Chris@0: $custom_date_format = !empty($field['custom_date_format']) ? $field['custom_date_format'] : ''; Chris@0: $timezone = !empty($field['timezone']) ? $field['timezone'] : ''; Chris@0: Chris@0: // Save off the base part of the config path we are updating. Chris@0: $base = "display.$display_name.display_options.fields.$field_name"; Chris@0: Chris@0: if (in_array($date_format, $ago_formats)) { Chris@0: // Update the field to use the Field API formatter. Chris@0: $view->set($base . '.plugin_id', 'field'); Chris@0: $view->set($base . '.type', 'timestamp_ago'); Chris@0: Chris@0: // Ensure the granularity is an integer, which is defined in the Chris@0: // field.formatter.settings.timestamp_ago schema. Chris@0: $granularity = is_numeric($custom_date_format) ? (int) $custom_date_format : 2; Chris@0: Chris@0: // Add the new settings. Chris@0: if ($date_format === 'time ago' || $date_format === 'time hence' || $date_format === 'time span') { Chris@0: $view->set($base . '.settings.future_format', '@interval hence'); Chris@0: $view->set($base . '.settings.past_format', '@interval ago'); Chris@0: $view->set($base . '.settings.granularity', $granularity); Chris@0: } Chris@0: elseif ($date_format === 'raw time ago' || $date_format === 'raw time hence') { Chris@0: $view->set($base . '.settings.future_format', '@interval'); Chris@0: $view->set($base . '.settings.past_format', '@interval'); Chris@0: $view->set($base . '.settings.granularity', $granularity); Chris@0: } Chris@0: elseif ($date_format === 'raw time span') { Chris@0: $view->set($base . '.settings.future_format', '@interval'); Chris@0: $view->set($base . '.settings.past_format', '-@interval'); Chris@0: $view->set($base . '.settings.granularity', $granularity); Chris@0: } Chris@0: elseif ($date_format === 'inverse time span') { Chris@0: $view->set($base . '.settings.future_format', '-@interval'); Chris@0: $view->set($base . '.settings.past_format', '@interval'); Chris@0: $view->set($base . '.settings.granularity', $granularity); Chris@0: } Chris@0: } Chris@0: else { Chris@0: // Update the field to use the Field API formatter. Chris@0: $view->set($base . '.plugin_id', 'field'); Chris@0: $view->set($base . '.type', 'timestamp'); Chris@0: Chris@0: // Add the new settings, and make sure everything is a string Chris@0: // to conform with the field.formatter.settings.timestamp schema. Chris@0: $view->set($base . '.settings.date_format', (string) $date_format); Chris@0: $view->set($base . '.settings.custom_date_format', (string) $custom_date_format); Chris@0: $view->set($base . '.settings.timezone', (string) $timezone); Chris@0: } Chris@0: Chris@0: // Remove the old settings. Chris@0: $view->clear($base . '.date_format'); Chris@0: $view->clear($base . '.custom_date_format'); Chris@0: $view->clear($base . '.timezone'); Chris@0: } Chris@0: } Chris@0: } Chris@0: } Chris@0: Chris@0: $view->save(TRUE); Chris@0: } Chris@0: Chris@0: if (!empty($ids)) { Chris@0: $message = \Drupal::translation()->translate('Updated field plugins for views: @ids', ['@ids' => implode(', ', array_unique($ids))]); Chris@0: } Chris@0: Chris@0: return $message; Chris@0: } Chris@0: Chris@0: /** Chris@0: * Updates %1 and !1 tokens to argument tokens. Chris@0: */ Chris@0: function views_update_8002() { Chris@0: $config_factory = \Drupal::configFactory(); Chris@0: foreach ($config_factory->listAll('views.view.') as $view_config_name) { Chris@0: $view = $config_factory->getEditable($view_config_name); Chris@0: Chris@0: $displays = $view->get('display'); Chris@0: $argument_map_per_display = _views_update_argument_map($displays); Chris@0: Chris@0: $changed = FALSE; Chris@0: Chris@0: // Update all the field settings, which support tokens. Chris@0: foreach ($displays as $display_name => &$display) { Chris@0: if (!empty($display['display_options']['fields'])) { Chris@0: $token_values = [ Chris@0: 'path', Chris@0: 'alt', Chris@0: 'link_class', Chris@0: 'rel', Chris@0: 'target', Chris@0: 'query', Chris@0: 'fragment', Chris@0: 'prefix', Chris@0: 'suffix', Chris@0: 'more_link_text', Chris@0: 'more_link_path', Chris@0: 'link_attributes', Chris@0: 'text', Chris@0: ]; Chris@0: Chris@0: foreach ($display['display_options']['fields'] as $field_name => &$field) { Chris@0: foreach ($token_values as $token_name) { Chris@0: if (!empty($field['alter'][$token_name])) { Chris@0: if (is_array($field['alter'][$token_name])) { Chris@0: foreach (array_keys($field['alter'][$token_name]) as $key) { Chris@0: $field['alter'][$token_name][$key] = _views_update_8002_token_update($field['alter'][$token_name][$key], $argument_map_per_display[$display_name]); Chris@0: $changed = TRUE; Chris@0: } Chris@0: } Chris@0: else { Chris@0: $field['alter'][$token_name] = _views_update_8002_token_update($field['alter'][$token_name], $argument_map_per_display[$display_name]); Chris@0: $changed = TRUE; Chris@0: } Chris@0: } Chris@0: } Chris@0: } Chris@0: } Chris@0: } Chris@0: Chris@0: // Update the area handlers with tokens. Chris@0: foreach ($displays as $display_name => &$display) { Chris@0: $area_types = ['header', 'footer', 'empty']; Chris@0: foreach ($area_types as $area_type) { Chris@0: if (!empty($display['display_options'][$area_type])) { Chris@0: foreach ($display['display_options'][$area_type] as &$area) { Chris@0: switch ($area['plugin_id']) { Chris@0: case 'title': Chris@0: $area['title'] = _views_update_8002_token_update($area['title'], $argument_map_per_display[$display_name]); Chris@0: $changed = TRUE; Chris@0: break; Chris@0: case 'result': Chris@0: $area['content'] = _views_update_8002_token_update($area['content'], $argument_map_per_display[$display_name]); Chris@0: $changed = TRUE; Chris@0: break; Chris@0: case 'text': Chris@0: $area['content']['value'] = _views_update_8002_token_update($area['content']['value'], $argument_map_per_display[$display_name]); Chris@0: $changed = TRUE; Chris@0: break; Chris@0: case 'text_custom': Chris@0: $area['content'] = _views_update_8002_token_update($area['content'], $argument_map_per_display[$display_name]); Chris@0: $changed = TRUE; Chris@0: break; Chris@0: case 'entity': Chris@0: $area['target'] = _views_update_8002_token_update($area['target'], $argument_map_per_display[$display_name]); Chris@0: $changed = TRUE; Chris@0: break; Chris@0: } Chris@0: } Chris@0: } Chris@0: } Chris@0: } Chris@0: Chris@0: // Update the argument title settings. Chris@0: foreach ($displays as $display_name => &$display) { Chris@0: if (!empty($display['display_options']['arguments'])) { Chris@0: foreach ($display['display_options']['arguments'] as &$argument) { Chris@0: if (isset($argument['exception']['title'])) { Chris@0: $argument['exception']['title'] = _views_update_8002_token_update($argument['exception']['title'], $argument_map_per_display[$display_name]); Chris@0: $changed = TRUE; Chris@0: } Chris@0: if (isset($argument['title'])) { Chris@0: $argument['title'] = _views_update_8002_token_update($argument['title'], $argument_map_per_display[$display_name]); Chris@0: $changed = TRUE; Chris@0: } Chris@0: } Chris@0: } Chris@0: } Chris@0: Chris@0: // Update the display title settings. Chris@0: // Update the more link text and more link URL. Chris@0: foreach ($displays as $display_name => &$display) { Chris@0: if (!empty($display['display_options']['title'])) { Chris@0: $display['display_options']['title'] = _views_update_8002_token_update($display['display_options']['title'], $argument_map_per_display[$display_name]); Chris@0: $changed = TRUE; Chris@0: } Chris@0: if (!empty($display['display_options']['use_more_text'])) { Chris@0: $display['display_options']['use_more_text'] = _views_update_8002_token_update($display['display_options']['use_more_text'], $argument_map_per_display[$display_name]); Chris@0: $changed = TRUE; Chris@0: } Chris@0: if (!empty($display['display_options']['link_url'])) { Chris@0: $display['display_options']['link_url'] = _views_update_8002_token_update($display['display_options']['link_url'], $argument_map_per_display[$display_name]); Chris@0: $changed = TRUE; Chris@0: } Chris@0: } Chris@0: Chris@0: // Update custom classes for row class + grid classes. Chris@0: // Update RSS description field. Chris@0: foreach ($displays as $display_name => &$display) { Chris@0: if (!empty($display['display_options']['style'])) { Chris@0: if (!empty($display['display_options']['style']['options']['row_class_custom'])) { Chris@0: $display['display_options']['style']['options']['row_class_custom'] = _views_update_8002_token_update($display['display_options']['style']['options']['row_class_custom'], $argument_map_per_display[$display_name]); Chris@0: $changed = TRUE; Chris@0: } Chris@0: if (!empty($display['display_options']['style']['options']['col_class_custom'])) { Chris@0: $display['display_options']['style']['options']['col_class_custom'] = _views_update_8002_token_update($display['display_options']['style']['options']['col_class_custom'], $argument_map_per_display[$display_name]); Chris@0: $changed = TRUE; Chris@0: } Chris@0: if (!empty($display['display_options']['style']['options']['description'])) { Chris@0: $display['display_options']['style']['options']['description'] = _views_update_8002_token_update($display['display_options']['style']['options']['description'], $argument_map_per_display[$display_name]); Chris@0: $changed = TRUE; Chris@0: } Chris@0: } Chris@0: } Chris@0: Chris@0: if ($changed) { Chris@0: $view->set('display', $displays); Chris@0: $view->save(TRUE); Chris@0: } Chris@0: } Chris@0: } Chris@0: Chris@0: /** Chris@0: * Updates a views configuration string from using %/! to twig tokens. Chris@0: * Chris@0: * @param string $text Chris@0: * Text in which to search for argument tokens and replace them with their Chris@0: * twig representation. Chris@0: * @param array $argument_map Chris@0: * A map of argument machine names keyed by their previous index. Chris@0: * Chris@0: * @return string Chris@0: * The updated token. Chris@0: */ Chris@0: function _views_update_8002_token_update($text, array $argument_map) { Chris@0: $text = preg_replace_callback('/%(\d)/', function ($match) use ($argument_map) { Chris@0: return "{{ arguments.{$argument_map[$match[1]]} }}"; Chris@0: }, $text); Chris@0: $text = preg_replace_callback('/!(\d)/', function ($match) use ($argument_map) { Chris@0: return "{{ raw_arguments.{$argument_map[$match[1]]} }}"; Chris@0: }, $text); Chris@0: Chris@0: return $text; Chris@0: } Chris@0: Chris@0: /** Chris@0: * Builds an argument map for each Views display. Chris@0: * Chris@0: * @param array $displays Chris@0: * A list of Views displays. Chris@0: * Chris@0: * @return array Chris@0: * The argument map keyed by display id. Chris@0: */ Chris@0: function _views_update_argument_map($displays) { Chris@0: $argument_map = []; Chris@0: foreach ($displays as $display_id => $display) { Chris@0: $argument_map[$display_id] = []; Chris@0: if (isset($display['display_options']['arguments'])) { Chris@0: foreach (array_keys($display['display_options']['arguments']) as $number => $name) { Chris@0: $argument_map[$display_id][$number + 1] = $name; Chris@0: } Chris@0: } Chris@0: elseif (isset($displays['default']['display_options']['arguments'])) { Chris@0: foreach (array_keys($displays['default']['display_options']['arguments']) as $number => $name) { Chris@0: $argument_map[$display_id][$number + 1] = $name; Chris@0: } Chris@0: } Chris@0: } Chris@0: Chris@0: return $argument_map; Chris@0: } Chris@0: Chris@0: /** Chris@0: * Clear caches to fix entity operations field. Chris@0: */ Chris@0: function views_update_8003() { Chris@0: // Empty update to cause a cache flush so that views data is rebuilt. Entity Chris@0: // types that don't implement a list builder cannot have the entity operations Chris@0: // field. Chris@17: Chris@17: // Use hook_post_update_NAME() instead to clear the cache.The use Chris@17: // of hook_update_N to clear the cache has been deprecated see Chris@17: // https://www.drupal.org/node/2960601 for more details. Chris@0: } Chris@0: Chris@0: /** Chris@0: * Clear caches due to updated entity views data. Chris@0: */ Chris@0: function views_update_8004() { Chris@0: // Empty update to cause a cache flush so that views data is rebuilt. Chris@17: Chris@17: // Use hook_post_update_NAME() instead to clear the cache.The use Chris@17: // of hook_update_N to clear the cache has been deprecated see Chris@17: // https://www.drupal.org/node/2960601 for more details. Chris@0: } Chris@0: Chris@0: /** Chris@0: * Clear views data cache. Chris@0: */ Chris@0: function views_update_8005() { Chris@0: // Empty update function to rebuild the views data. Chris@17: Chris@17: // Use hook_post_update_NAME() instead to clear the cache.The use Chris@17: // of hook_update_N to clear the cache has been deprecated see Chris@17: // https://www.drupal.org/node/2960601 for more details. Chris@0: } Chris@0: Chris@0: /** Chris@0: * Clear caches due to updated entity views data. Chris@0: */ Chris@0: function views_update_8100() { Chris@0: // Empty update to cause a cache flush so that views data is rebuilt. Chris@17: Chris@17: // Use hook_post_update_NAME() instead to clear the cache.The use Chris@17: // of hook_update_N to clear the cache has been deprecated see Chris@17: // https://www.drupal.org/node/2960601 for more details. Chris@0: } Chris@0: Chris@0: /** Chris@0: * Set default values for enabled/expanded flag on page displays. Chris@0: */ Chris@0: function views_update_8101() { Chris@0: $config_factory = \Drupal::configFactory(); Chris@0: foreach ($config_factory->listAll('views.view.') as $view_config_name) { Chris@0: $view = $config_factory->getEditable($view_config_name); Chris@0: $save = FALSE; Chris@0: foreach ($view->get('display') as $display_id => $display) { Chris@0: if ($display['display_plugin'] == 'page') { Chris@0: $display['display_options']['menu']['enabled'] = TRUE; Chris@0: $display['display_options']['menu']['expanded'] = FALSE; Chris@0: $view->set("display.$display_id", $display); Chris@0: $save = TRUE; Chris@0: } Chris@0: } Chris@0: if ($save) { Chris@0: $view->save(); Chris@0: } Chris@0: } Chris@0: } Chris@0: Chris@0: /** Chris@0: * Rebuild the container to add a new container parameter. Chris@0: */ Chris@0: function views_update_8200() { Chris@0: // Empty update to cause a cache rebuild so that the container is rebuilt. Chris@17: Chris@17: // Use hook_post_update_NAME() instead to clear the cache.The use Chris@17: // of hook_update_N to clear the cache has been deprecated see Chris@17: // https://www.drupal.org/node/2960601 for more details. Chris@0: } Chris@0: Chris@0: /** Chris@0: * Rebuild cache to refresh the views config schema. Chris@0: */ Chris@0: function views_update_8201() { Chris@0: // Empty update to cause a cache rebuild so that config schema get refreshed. Chris@17: Chris@17: // Use hook_post_update_NAME() instead to clear the cache.The use Chris@17: // of hook_update_N to clear the cache has been deprecated see Chris@17: // https://www.drupal.org/node/2960601 for more details. Chris@0: } Chris@14: Chris@14: /** Chris@14: * Update field names for multi-value base fields. Chris@14: */ Chris@14: function views_update_8500() { Chris@14: // Find all multi-value base fields for content entities. Chris@14: $entity_type_manager = \Drupal::entityTypeManager(); Chris@14: $entity_field_manager = \Drupal::service('entity_field.manager'); Chris@14: $table_update_info = []; Chris@14: Chris@14: foreach ($entity_type_manager->getDefinitions() as $entity_type_id => $entity_type) { Chris@14: if ($entity_type->hasHandlerClass('views_data')) { Chris@14: $base_field_definitions = $entity_field_manager->getBaseFieldDefinitions($entity_type_id); Chris@14: Chris@14: $entity_storage = $entity_type_manager->getStorage($entity_type_id); Chris@14: $table_mapping = $entity_storage->getTableMapping($base_field_definitions); Chris@14: Chris@14: foreach ($base_field_definitions as $field_name => $base_field_definition) { Chris@14: $base_field_storage_definition = $base_field_definition->getFieldStorageDefinition(); Chris@14: Chris@14: // Skip single value and custom storage base fields. Chris@14: if (!$base_field_storage_definition->isMultiple() || $base_field_storage_definition->hasCustomStorage()) { Chris@14: continue; Chris@14: } Chris@14: Chris@14: // Get the actual table, as well as the column for the main property Chris@14: // name so we can perform an update later on the views. Chris@14: $table_name = $table_mapping->getFieldTableName($field_name); Chris@14: $main_property_name = $base_field_storage_definition->getMainPropertyName(); Chris@14: Chris@14: $table_update_info[$table_name][$field_name] = $table_mapping->getFieldColumnName($base_field_storage_definition, $main_property_name); Chris@14: } Chris@14: } Chris@14: } Chris@14: Chris@14: if (empty($table_update_info)) { Chris@14: return; Chris@14: } Chris@14: Chris@14: $config_factory = \Drupal::configFactory(); Chris@14: /** @var \Drupal\Core\Config\TypedConfigManagerInterface $typed_config_manager */ Chris@14: $typed_config_manager = \Drupal::service('config.typed'); Chris@14: $views_data = Views::viewsData(); Chris@14: $handler_types = ['field', 'argument', 'sort', 'relationship', 'filter']; Chris@14: Chris@14: $required_cleanup_handlers = []; Chris@14: foreach ($config_factory->listAll('views.view.') as $id) { Chris@14: $view = $config_factory->getEditable($id); Chris@14: $changed = FALSE; Chris@14: Chris@14: foreach ($view->get('display') as $display_id => &$display) { Chris@14: foreach ($handler_types as $handler_type_singular) { Chris@14: $handler_type_plural = $handler_type_singular . 's'; Chris@14: $handler_data = $view->get("display.$display_id.display_options.$handler_type_plural"); Chris@14: Chris@14: if (empty($handler_data)) { Chris@14: continue; Chris@14: } Chris@14: Chris@14: foreach ($handler_data as $key => $data) { Chris@14: // If this handler has a table we're interested in, update the field Chris@14: // name. Chris@14: $table = $data['table']; Chris@14: if (isset($table_update_info[$table])) { Chris@14: $path_to_handler = "display.$display_id.display_options.$handler_type_plural.$key"; Chris@14: $path_field = "{$path_to_handler}.field"; Chris@14: $path_plugin_id = "{$path_to_handler}.plugin_id"; Chris@14: $original_field_name = $view->get($path_field); Chris@14: Chris@14: // Only if the wrong field name is set do we change the field. It Chris@14: // could already be using the correct field. Like Chris@14: // user__roles/roles_target_id. Chris@14: if (isset($table_update_info[$table][$original_field_name])) { Chris@14: $required_cleanup_handlers[$id][] = $path_to_handler; Chris@14: Chris@14: // Set both the new table field as well as new 'plugin_id' field. Chris@14: $view->set($path_field, $table_update_info[$table][$original_field_name]); Chris@14: $view->set($path_plugin_id, $views_data->get($table)[$table_update_info[$table][$original_field_name]][$handler_type_singular]['id']); Chris@14: Chris@14: $changed = TRUE; Chris@14: } Chris@14: } Chris@14: } Chris@14: } Chris@14: } Chris@14: Chris@14: if ($changed) { Chris@14: $view->save(TRUE); Chris@14: } Chris@14: } Chris@14: Chris@14: // Beside of updating the field and plugin ID we also need to truncate orphan Chris@14: // keys so he configuration applies to the config schema. Chris@14: // We cannot do that inline in the other code, due to caching issues with Chris@14: // typed configuration. Chris@14: foreach ($required_cleanup_handlers as $id => $paths_to_handlers) { Chris@14: $changed = FALSE; Chris@14: $typed_view = $typed_config_manager->get($id); Chris@14: $view = $config_factory->getEditable($id); Chris@14: foreach ($paths_to_handlers as $path_to_handler) { Chris@14: /** @var \Drupal\Core\Config\Schema\TypedConfigInterface $typed_view */ Chris@14: Chris@14: /** @var \Drupal\Core\Config\Schema\ArrayElement $typed_config */ Chris@14: $typed_config = $typed_view->get($path_to_handler); Chris@14: $config = $typed_config->getValue(); Chris@14: Chris@14: // Filter values we want to convert from a string to an array. Chris@14: if (strpos($path_to_handler, 'filters') !== FALSE && $typed_config->get('value') instanceof ArrayElement && is_string($config['value'])) { Chris@14: // An empty string casted to an array is an array with one Chris@14: // element. Chris@14: if ($config['value'] === '') { Chris@14: $config['value'] = []; Chris@14: } Chris@14: else { Chris@14: $config['value'] = (array) $config['value']; Chris@14: } Chris@14: } Chris@14: Chris@14: // For all the other fields we try to determine the fields using Chris@14: // config schema and remove everything which is not needed. Chris@14: foreach (array_keys($config) as $config_key) { Chris@14: if (!isset($typed_config->getDataDefinition()['mapping'][$config_key])) { Chris@14: unset($config[$config_key]); Chris@14: $changed = TRUE; Chris@14: } Chris@14: } Chris@14: $typed_config->setValue($config); Chris@14: $view->set($path_to_handler, $typed_config->getValue()); Chris@14: } Chris@14: Chris@14: if ($changed) { Chris@14: $view->save(); Chris@14: } Chris@14: } Chris@14: }