Updated Drupal to 8.6. This goes with the following updates because it's possible...
[yaffs-website] / web / core / modules / views / views.install
1 <?php
2
3 /**
4  * @file
5  * Contains install and update functions for Views.
6  */
7
8 use Drupal\Core\Config\Schema\ArrayElement;
9 use Drupal\views\Views;
10
11 /**
12  * Implements hook_install().
13  */
14 function views_install() {
15   module_set_weight('views', 10);
16 }
17
18 /**
19  * Update views field plugins.
20  */
21 function views_update_8001(&$sandbox) {
22   $config_factory = \Drupal::configFactory();
23   $ids = [];
24   $message = NULL;
25   $ago_formats = [
26     'time ago',
27     'time hence',
28     'time span',
29     'raw time ago',
30     'raw time hence',
31     'raw time span',
32     'inverse time span',
33   ];
34
35   foreach ($config_factory->listAll('views.view.') as $view_config_name) {
36     $view = $config_factory->getEditable($view_config_name);
37
38     $displays = $view->get('display');
39
40     foreach ($displays as $display_name => $display) {
41       if (!empty($display['display_options']['fields'])) {
42         foreach ($display['display_options']['fields'] as $field_name => $field) {
43           if (isset($field['entity_type']) && $field['plugin_id'] === 'date') {
44             $ids[] = $view->get('id');
45
46             // Grab the settings we need to move to a different place in the
47             // config schema.
48             $date_format = !empty($field['date_format']) ? $field['date_format'] : 'medium';
49             $custom_date_format = !empty($field['custom_date_format']) ? $field['custom_date_format'] : '';
50             $timezone = !empty($field['timezone']) ? $field['timezone'] : '';
51
52             // Save off the base part of the config path we are updating.
53             $base = "display.$display_name.display_options.fields.$field_name";
54
55             if (in_array($date_format, $ago_formats)) {
56               // Update the field to use the Field API formatter.
57               $view->set($base . '.plugin_id', 'field');
58               $view->set($base . '.type', 'timestamp_ago');
59
60               // Ensure the granularity is an integer, which is defined in the
61               // field.formatter.settings.timestamp_ago schema.
62               $granularity = is_numeric($custom_date_format) ? (int) $custom_date_format : 2;
63
64               // Add the new settings.
65               if ($date_format === 'time ago' || $date_format === 'time hence' || $date_format === 'time span') {
66                 $view->set($base . '.settings.future_format', '@interval hence');
67                 $view->set($base . '.settings.past_format', '@interval ago');
68                 $view->set($base . '.settings.granularity', $granularity);
69               }
70               elseif ($date_format === 'raw time ago' || $date_format === 'raw time hence') {
71                 $view->set($base . '.settings.future_format', '@interval');
72                 $view->set($base . '.settings.past_format', '@interval');
73                 $view->set($base . '.settings.granularity', $granularity);
74               }
75               elseif ($date_format === 'raw time span') {
76                 $view->set($base . '.settings.future_format', '@interval');
77                 $view->set($base . '.settings.past_format', '-@interval');
78                 $view->set($base . '.settings.granularity', $granularity);
79               }
80               elseif ($date_format === 'inverse time span') {
81                 $view->set($base . '.settings.future_format', '-@interval');
82                 $view->set($base . '.settings.past_format', '@interval');
83                 $view->set($base . '.settings.granularity', $granularity);
84               }
85             }
86             else {
87               // Update the field to use the Field API formatter.
88               $view->set($base . '.plugin_id', 'field');
89               $view->set($base . '.type', 'timestamp');
90
91               // Add the new settings, and make sure everything is a string
92               // to conform with the field.formatter.settings.timestamp schema.
93               $view->set($base . '.settings.date_format', (string) $date_format);
94               $view->set($base . '.settings.custom_date_format', (string) $custom_date_format);
95               $view->set($base . '.settings.timezone', (string) $timezone);
96             }
97
98             // Remove the old settings.
99             $view->clear($base . '.date_format');
100             $view->clear($base . '.custom_date_format');
101             $view->clear($base . '.timezone');
102           }
103         }
104       }
105     }
106
107     $view->save(TRUE);
108   }
109
110   if (!empty($ids)) {
111     $message = \Drupal::translation()->translate('Updated field plugins for views: @ids', ['@ids' => implode(', ', array_unique($ids))]);
112   }
113
114   return $message;
115 }
116
117 /**
118  * Updates %1 and !1 tokens to argument tokens.
119  */
120 function views_update_8002() {
121   $config_factory = \Drupal::configFactory();
122   foreach ($config_factory->listAll('views.view.') as $view_config_name) {
123     $view = $config_factory->getEditable($view_config_name);
124
125     $displays = $view->get('display');
126     $argument_map_per_display = _views_update_argument_map($displays);
127
128     $changed = FALSE;
129
130     // Update all the field settings, which support tokens.
131     foreach ($displays as $display_name => &$display) {
132       if (!empty($display['display_options']['fields'])) {
133         $token_values = [
134           'path',
135           'alt',
136           'link_class',
137           'rel',
138           'target',
139           'query',
140           'fragment',
141           'prefix',
142           'suffix',
143           'more_link_text',
144           'more_link_path',
145           'link_attributes',
146           'text',
147         ];
148
149         foreach ($display['display_options']['fields'] as $field_name => &$field) {
150           foreach ($token_values as $token_name) {
151             if (!empty($field['alter'][$token_name])) {
152               if (is_array($field['alter'][$token_name])) {
153                 foreach (array_keys($field['alter'][$token_name]) as $key) {
154                   $field['alter'][$token_name][$key] = _views_update_8002_token_update($field['alter'][$token_name][$key], $argument_map_per_display[$display_name]);
155                   $changed = TRUE;
156                 }
157               }
158               else {
159                 $field['alter'][$token_name] = _views_update_8002_token_update($field['alter'][$token_name], $argument_map_per_display[$display_name]);
160                 $changed = TRUE;
161               }
162             }
163           }
164         }
165       }
166     }
167
168     // Update the area handlers with tokens.
169     foreach ($displays as $display_name => &$display) {
170       $area_types = ['header', 'footer', 'empty'];
171       foreach ($area_types as $area_type) {
172         if (!empty($display['display_options'][$area_type])) {
173           foreach ($display['display_options'][$area_type] as &$area) {
174             switch ($area['plugin_id']) {
175               case 'title':
176                 $area['title'] = _views_update_8002_token_update($area['title'], $argument_map_per_display[$display_name]);
177                 $changed = TRUE;
178                 break;
179               case 'result':
180                 $area['content'] = _views_update_8002_token_update($area['content'], $argument_map_per_display[$display_name]);
181                 $changed = TRUE;
182                 break;
183               case 'text':
184                 $area['content']['value'] = _views_update_8002_token_update($area['content']['value'], $argument_map_per_display[$display_name]);
185                 $changed = TRUE;
186                 break;
187               case 'text_custom':
188                 $area['content'] = _views_update_8002_token_update($area['content'], $argument_map_per_display[$display_name]);
189                 $changed = TRUE;
190                 break;
191               case 'entity':
192                 $area['target'] = _views_update_8002_token_update($area['target'], $argument_map_per_display[$display_name]);
193                 $changed = TRUE;
194                 break;
195             }
196           }
197         }
198       }
199     }
200
201     // Update the argument title settings.
202     foreach ($displays as $display_name => &$display) {
203       if (!empty($display['display_options']['arguments'])) {
204         foreach ($display['display_options']['arguments'] as &$argument) {
205           if (isset($argument['exception']['title'])) {
206             $argument['exception']['title'] = _views_update_8002_token_update($argument['exception']['title'], $argument_map_per_display[$display_name]);
207             $changed = TRUE;
208           }
209           if (isset($argument['title'])) {
210             $argument['title'] = _views_update_8002_token_update($argument['title'], $argument_map_per_display[$display_name]);
211             $changed = TRUE;
212           }
213         }
214       }
215     }
216
217     // Update the display title settings.
218     // Update the more link text and more link URL.
219     foreach ($displays as $display_name => &$display) {
220       if (!empty($display['display_options']['title'])) {
221         $display['display_options']['title'] = _views_update_8002_token_update($display['display_options']['title'], $argument_map_per_display[$display_name]);
222         $changed = TRUE;
223       }
224       if (!empty($display['display_options']['use_more_text'])) {
225         $display['display_options']['use_more_text'] = _views_update_8002_token_update($display['display_options']['use_more_text'], $argument_map_per_display[$display_name]);
226         $changed = TRUE;
227       }
228       if (!empty($display['display_options']['link_url'])) {
229         $display['display_options']['link_url'] = _views_update_8002_token_update($display['display_options']['link_url'], $argument_map_per_display[$display_name]);
230         $changed = TRUE;
231       }
232     }
233
234     // Update custom classes for row class + grid classes.
235     // Update RSS description field.
236     foreach ($displays as $display_name => &$display) {
237       if (!empty($display['display_options']['style'])) {
238         if (!empty($display['display_options']['style']['options']['row_class_custom'])) {
239           $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]);
240           $changed = TRUE;
241         }
242         if (!empty($display['display_options']['style']['options']['col_class_custom'])) {
243           $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]);
244           $changed = TRUE;
245         }
246         if (!empty($display['display_options']['style']['options']['description'])) {
247           $display['display_options']['style']['options']['description'] = _views_update_8002_token_update($display['display_options']['style']['options']['description'], $argument_map_per_display[$display_name]);
248           $changed = TRUE;
249         }
250       }
251     }
252
253     if ($changed) {
254       $view->set('display', $displays);
255       $view->save(TRUE);
256     }
257   }
258 }
259
260 /**
261  * Updates a views configuration string from using %/! to twig tokens.
262  *
263  * @param string $text
264  *   Text in which to search for argument tokens and replace them with their
265  *   twig representation.
266  * @param array $argument_map
267  *   A map of argument machine names keyed by their previous index.
268  *
269  * @return string
270  *   The updated token.
271  */
272 function _views_update_8002_token_update($text, array $argument_map) {
273   $text = preg_replace_callback('/%(\d)/', function ($match) use ($argument_map) {
274     return "{{ arguments.{$argument_map[$match[1]]} }}";
275   }, $text);
276   $text = preg_replace_callback('/!(\d)/', function ($match) use ($argument_map) {
277     return "{{ raw_arguments.{$argument_map[$match[1]]} }}";
278   }, $text);
279
280   return $text;
281 }
282
283 /**
284  * Builds an argument map for each Views display.
285  *
286  * @param array $displays
287  *   A list of Views displays.
288  *
289  * @return array
290  *   The argument map keyed by display id.
291  */
292 function _views_update_argument_map($displays) {
293   $argument_map = [];
294   foreach ($displays as $display_id => $display) {
295     $argument_map[$display_id] = [];
296     if (isset($display['display_options']['arguments'])) {
297       foreach (array_keys($display['display_options']['arguments']) as $number => $name) {
298         $argument_map[$display_id][$number + 1] = $name;
299       }
300     }
301     elseif (isset($displays['default']['display_options']['arguments'])) {
302       foreach (array_keys($displays['default']['display_options']['arguments']) as $number => $name) {
303         $argument_map[$display_id][$number + 1] = $name;
304       }
305     }
306   }
307
308   return $argument_map;
309 }
310
311 /**
312  * Clear caches to fix entity operations field.
313  */
314 function views_update_8003() {
315   // Empty update to cause a cache flush so that views data is rebuilt. Entity
316   // types that don't implement a list builder cannot have the entity operations
317   // field.
318
319   // Use hook_post_update_NAME() instead to clear the cache.The use
320   // of hook_update_N to clear the cache has been deprecated see
321   // https://www.drupal.org/node/2960601 for more details.
322 }
323
324 /**
325  * Clear caches due to updated entity views data.
326  */
327 function views_update_8004() {
328   // Empty update to cause a cache flush so that views data is rebuilt.
329
330   // Use hook_post_update_NAME() instead to clear the cache.The use
331   // of hook_update_N to clear the cache has been deprecated see
332   // https://www.drupal.org/node/2960601 for more details.
333 }
334
335 /**
336  * Clear views data cache.
337  */
338 function views_update_8005() {
339   // Empty update function to rebuild the views data.
340
341   // Use hook_post_update_NAME() instead to clear the cache.The use
342   // of hook_update_N to clear the cache has been deprecated see
343   // https://www.drupal.org/node/2960601 for more details.
344 }
345
346 /**
347  * Clear caches due to updated entity views data.
348  */
349 function views_update_8100() {
350   // Empty update to cause a cache flush so that views data is rebuilt.
351
352   // Use hook_post_update_NAME() instead to clear the cache.The use
353   // of hook_update_N to clear the cache has been deprecated see
354   // https://www.drupal.org/node/2960601 for more details.
355 }
356
357 /**
358  * Set default values for enabled/expanded flag on page displays.
359  */
360 function views_update_8101() {
361   $config_factory = \Drupal::configFactory();
362   foreach ($config_factory->listAll('views.view.') as $view_config_name) {
363     $view = $config_factory->getEditable($view_config_name);
364     $save = FALSE;
365     foreach ($view->get('display') as $display_id => $display) {
366       if ($display['display_plugin'] == 'page') {
367         $display['display_options']['menu']['enabled'] = TRUE;
368         $display['display_options']['menu']['expanded'] = FALSE;
369         $view->set("display.$display_id", $display);
370         $save = TRUE;
371       }
372     }
373     if ($save) {
374       $view->save();
375     }
376   }
377 }
378
379 /**
380  * Rebuild the container to add a new container parameter.
381  */
382 function views_update_8200() {
383   // Empty update to cause a cache rebuild so that the container is rebuilt.
384
385   // Use hook_post_update_NAME() instead to clear the cache.The use
386   // of hook_update_N to clear the cache has been deprecated see
387   // https://www.drupal.org/node/2960601 for more details.
388 }
389
390 /**
391  * Rebuild cache to refresh the views config schema.
392  */
393 function views_update_8201() {
394   // Empty update to cause a cache rebuild so that config schema get refreshed.
395
396   // Use hook_post_update_NAME() instead to clear the cache.The use
397   // of hook_update_N to clear the cache has been deprecated see
398   // https://www.drupal.org/node/2960601 for more details.
399 }
400
401 /**
402  * Update field names for multi-value base fields.
403  */
404 function views_update_8500() {
405   // Find all multi-value base fields for content entities.
406   $entity_type_manager = \Drupal::entityTypeManager();
407   $entity_field_manager = \Drupal::service('entity_field.manager');
408   $table_update_info = [];
409
410   foreach ($entity_type_manager->getDefinitions() as $entity_type_id => $entity_type) {
411     if ($entity_type->hasHandlerClass('views_data')) {
412       $base_field_definitions = $entity_field_manager->getBaseFieldDefinitions($entity_type_id);
413
414       $entity_storage = $entity_type_manager->getStorage($entity_type_id);
415       $table_mapping = $entity_storage->getTableMapping($base_field_definitions);
416
417       foreach ($base_field_definitions as $field_name => $base_field_definition) {
418         $base_field_storage_definition = $base_field_definition->getFieldStorageDefinition();
419
420         // Skip single value and custom storage base fields.
421         if (!$base_field_storage_definition->isMultiple() || $base_field_storage_definition->hasCustomStorage()) {
422           continue;
423         }
424
425         // Get the actual table, as well as the column for the main property
426         // name so we can perform an update later on the views.
427         $table_name = $table_mapping->getFieldTableName($field_name);
428         $main_property_name = $base_field_storage_definition->getMainPropertyName();
429
430         $table_update_info[$table_name][$field_name] = $table_mapping->getFieldColumnName($base_field_storage_definition, $main_property_name);
431       }
432     }
433   }
434
435   if (empty($table_update_info)) {
436     return;
437   }
438
439   $config_factory = \Drupal::configFactory();
440   /** @var \Drupal\Core\Config\TypedConfigManagerInterface $typed_config_manager */
441   $typed_config_manager = \Drupal::service('config.typed');
442   $views_data = Views::viewsData();
443   $handler_types = ['field', 'argument', 'sort', 'relationship', 'filter'];
444
445   $required_cleanup_handlers = [];
446   foreach ($config_factory->listAll('views.view.') as $id) {
447     $view = $config_factory->getEditable($id);
448     $changed = FALSE;
449
450     foreach ($view->get('display') as $display_id => &$display) {
451       foreach ($handler_types as $handler_type_singular) {
452         $handler_type_plural = $handler_type_singular . 's';
453         $handler_data = $view->get("display.$display_id.display_options.$handler_type_plural");
454
455         if (empty($handler_data)) {
456           continue;
457         }
458
459         foreach ($handler_data as $key => $data) {
460           // If this handler has a table we're interested in, update the field
461           // name.
462           $table = $data['table'];
463           if (isset($table_update_info[$table])) {
464             $path_to_handler = "display.$display_id.display_options.$handler_type_plural.$key";
465             $path_field = "{$path_to_handler}.field";
466             $path_plugin_id = "{$path_to_handler}.plugin_id";
467             $original_field_name = $view->get($path_field);
468
469             // Only if the wrong field name is set do we change the field. It
470             // could already be using the correct field. Like
471             // user__roles/roles_target_id.
472             if (isset($table_update_info[$table][$original_field_name])) {
473               $required_cleanup_handlers[$id][] = $path_to_handler;
474
475               // Set both the new table field as well as new 'plugin_id' field.
476               $view->set($path_field, $table_update_info[$table][$original_field_name]);
477               $view->set($path_plugin_id, $views_data->get($table)[$table_update_info[$table][$original_field_name]][$handler_type_singular]['id']);
478
479               $changed = TRUE;
480             }
481           }
482         }
483       }
484     }
485
486     if ($changed) {
487       $view->save(TRUE);
488     }
489   }
490
491   // Beside of updating the field and plugin ID we also need to truncate orphan
492   // keys so he configuration applies to the config schema.
493   // We cannot do that inline in the other code, due to caching issues with
494   // typed configuration.
495   foreach ($required_cleanup_handlers as $id => $paths_to_handlers) {
496     $changed = FALSE;
497     $typed_view = $typed_config_manager->get($id);
498     $view = $config_factory->getEditable($id);
499     foreach ($paths_to_handlers as $path_to_handler) {
500       /** @var \Drupal\Core\Config\Schema\TypedConfigInterface $typed_view */
501
502       /** @var \Drupal\Core\Config\Schema\ArrayElement $typed_config */
503       $typed_config = $typed_view->get($path_to_handler);
504       $config = $typed_config->getValue();
505
506       // Filter values we want to convert from a string to an array.
507       if (strpos($path_to_handler, 'filters') !== FALSE && $typed_config->get('value') instanceof ArrayElement && is_string($config['value'])) {
508         // An empty string casted to an array is an array with one
509         // element.
510         if ($config['value'] === '') {
511           $config['value'] = [];
512         }
513         else {
514           $config['value'] = (array) $config['value'];
515         }
516       }
517
518       // For all the other fields we try to determine the fields using
519       // config schema and remove everything which is not needed.
520       foreach (array_keys($config) as $config_key) {
521         if (!isset($typed_config->getDataDefinition()['mapping'][$config_key])) {
522           unset($config[$config_key]);
523           $changed = TRUE;
524         }
525       }
526       $typed_config->setValue($config);
527       $view->set($path_to_handler, $typed_config->getValue());
528     }
529
530     if ($changed) {
531       $view->save();
532     }
533   }
534 }