535 lines
20 KiB
Plaintext
535 lines
20 KiB
Plaintext
<?php
|
|
|
|
/**
|
|
* @file
|
|
* Contains install and update functions for Views.
|
|
*/
|
|
|
|
use Drupal\Core\Config\Schema\ArrayElement;
|
|
use Drupal\views\Views;
|
|
|
|
/**
|
|
* Implements hook_install().
|
|
*/
|
|
function views_install() {
|
|
module_set_weight('views', 10);
|
|
}
|
|
|
|
/**
|
|
* Update views field plugins.
|
|
*/
|
|
function views_update_8001(&$sandbox) {
|
|
$config_factory = \Drupal::configFactory();
|
|
$ids = [];
|
|
$message = NULL;
|
|
$ago_formats = [
|
|
'time ago',
|
|
'time hence',
|
|
'time span',
|
|
'raw time ago',
|
|
'raw time hence',
|
|
'raw time span',
|
|
'inverse time span',
|
|
];
|
|
|
|
foreach ($config_factory->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();
|
|
}
|
|
}
|
|
}
|