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