View source
<?php
namespace Drupal\views;
use Drupal\Component\Plugin\PluginManagerInterface;
use Drupal\Core\Cache\Cache;
use Drupal\Core\Config\TypedConfigManagerInterface;
use Drupal\Core\DependencyInjection\ContainerInjectionInterface;
use Drupal\Core\Entity\EntityFieldManagerInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Field\Plugin\Field\FieldFormatter\TimestampFormatter;
use Drupal\Core\Language\LanguageInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
class ViewsConfigUpdater implements ContainerInjectionInterface {
protected $entityTypeManager;
protected $entityFieldManager;
protected $typedConfigManager;
protected $viewsData;
protected $formatterPluginManager;
protected $multivalueBaseFieldsUpdateTableInfo;
protected $deprecationsEnabled = TRUE;
protected $triggeredDeprecations = [];
public function __construct(EntityTypeManagerInterface $entity_type_manager, EntityFieldManagerInterface $entity_field_manager, TypedConfigManagerInterface $typed_config_manager, ViewsData $views_data, PluginManagerInterface $formatter_plugin_manager) {
$this->entityTypeManager = $entity_type_manager;
$this->entityFieldManager = $entity_field_manager;
$this->typedConfigManager = $typed_config_manager;
$this->viewsData = $views_data;
$this->formatterPluginManager = $formatter_plugin_manager;
}
public static function create(ContainerInterface $container) {
return new static($container
->get('entity_type.manager'), $container
->get('entity_field.manager'), $container
->get('config.typed'), $container
->get('views.views_data'), $container
->get('plugin.manager.field.formatter'));
}
public function setDeprecationsEnabled($enabled) {
$this->deprecationsEnabled = $enabled;
}
public function updateAll(ViewEntityInterface $view) {
return $this
->processDisplayHandlers($view, FALSE, function (&$handler, $handler_type, $key, $display_id) use ($view) {
$changed = FALSE;
if ($this
->processResponsiveImageLazyLoadFieldHandler($handler, $handler_type, $view)) {
$changed = TRUE;
}
if ($this
->processTimestampFormatterTimeDiffUpdateHandler($handler, $handler_type)) {
$changed = TRUE;
}
if ($this
->processRevisionFieldHyphenFix($view)) {
$changed = TRUE;
}
if ($this
->processDefaultArgumentSkipUrlUpdate($handler, $handler_type)) {
$changed = TRUE;
}
if ($this
->processDefaultPagerHeadingUpdate($handler, $handler_type)) {
$changed = TRUE;
}
if ($this
->addLabelIfMissing($view)) {
$changed = TRUE;
}
return $changed;
});
}
public function addLabelIfMissing(ViewEntityInterface $view) : bool {
if (!$view
->get('label')) {
$view
->set('label', $view
->id());
return TRUE;
}
return FALSE;
}
public function needsResponsiveImageLazyLoadFieldUpdate(ViewEntityInterface $view) : bool {
return $this
->processDisplayHandlers($view, TRUE, function (&$handler, $handler_type) use ($view) {
return $this
->processResponsiveImageLazyLoadFieldHandler($handler, $handler_type, $view);
});
}
protected function processResponsiveImageLazyLoadFieldHandler(array &$handler, string $handler_type, ViewEntityInterface $view) : bool {
$changed = FALSE;
if ($handler_type === 'field' && isset($handler['plugin_id'], $handler['type']) && $handler['plugin_id'] === 'field' && $handler['type'] === 'responsive_image' && !isset($handler['settings']['image_loading'])) {
$handler['settings']['image_loading'] = [
'attribute' => 'eager',
];
$changed = TRUE;
}
return $changed;
}
protected function processDisplayHandlers(ViewEntityInterface $view, $return_on_changed, callable $handler_processor) {
$changed = FALSE;
$displays = $view
->get('display');
$handler_types = [
'field' => 'fields',
'argument' => 'arguments',
'sort' => 'sorts',
'relationship' => 'relationships',
'filter' => 'filters',
'pager' => 'pager',
];
$compound_display_handlers = [
'pager',
];
foreach ($displays as $display_id => &$display) {
foreach ($handler_types as $handler_type => $handler_type_lookup) {
if (!empty($display['display_options'][$handler_type_lookup])) {
if (in_array($handler_type_lookup, $compound_display_handlers)) {
if ($handler_processor($display['display_options'][$handler_type_lookup], $handler_type, NULL, $display_id)) {
$changed = TRUE;
if ($return_on_changed) {
return $changed;
}
}
continue;
}
foreach ($display['display_options'][$handler_type_lookup] as $key => &$handler) {
if (is_array($handler) && $handler_processor($handler, $handler_type, $key, $display_id)) {
$changed = TRUE;
if ($return_on_changed) {
return $changed;
}
}
}
}
}
}
if ($changed) {
$view
->set('display', $displays);
}
return $changed;
}
public function needsOembedEagerLoadFieldUpdate(ViewEntityInterface $view) {
return $this
->processDisplayHandlers($view, TRUE, function (&$handler, $handler_type) use ($view) {
return $this
->processOembedEagerLoadFieldHandler($handler, $handler_type, $view);
});
}
protected function processOembedEagerLoadFieldHandler(array &$handler, string $handler_type, ViewEntityInterface $view) : bool {
$changed = FALSE;
if ($handler_type === 'field' && isset($handler['plugin_id'], $handler['type']) && $handler['plugin_id'] === 'field' && $handler['type'] === 'oembed' && !array_key_exists('loading', $handler['settings'])) {
$handler['settings']['loading'] = [
'attribute' => 'eager',
];
$changed = TRUE;
}
$deprecations_triggered =& $this->triggeredDeprecations['3212351'][$view
->id()];
if ($this->deprecationsEnabled && $changed && !$deprecations_triggered) {
$deprecations_triggered = TRUE;
@trigger_error(sprintf('The oEmbed loading attribute update for view "%s" is deprecated in drupal:10.1.0 and is removed from drupal:11.0.0. Profile, module and theme provided configuration should be updated. See https://www.drupal.org/node/3275103', $view
->id()), E_USER_DEPRECATED);
}
return $changed;
}
public function needsTimestampFormatterTimeDiffUpdate(ViewEntityInterface $view) : bool {
return $this
->processDisplayHandlers($view, TRUE, function (array &$handler, string $handler_type) : bool {
return $this
->processTimestampFormatterTimeDiffUpdateHandler($handler, $handler_type);
});
}
protected function processTimestampFormatterTimeDiffUpdateHandler(array &$handler, string $handler_type) : bool {
if ($handler_type === 'field' && isset($handler['type'])) {
$plugin_definition = $this->formatterPluginManager
->getDefinition($handler['type'], FALSE);
if (!$plugin_definition || !is_a($plugin_definition['class'], TimestampFormatter::class, TRUE)) {
return FALSE;
}
if (!isset($handler['settings']['tooltip']) || !isset($handler['settings']['time_diff'])) {
$handler['settings'] += $plugin_definition['class']::defaultSettings();
$handler['settings']['tooltip'] = [
'date_format' => '',
'custom_date_format' => '',
];
return TRUE;
}
}
return FALSE;
}
public function processRevisionFieldHyphenFix(ViewEntityInterface $view) : bool {
$old_part = '/{{([^}]+)(-revision_id)/';
$new_part = '{{$1__revision_id';
$old_field = '-revision_id';
$new_field = '__revision_id';
$is_update = FALSE;
$displays = $view
->get('display');
foreach ($displays as &$display) {
if (isset($display['display_options']['fields'])) {
foreach ($display['display_options']['fields'] as $field_name => $field) {
if (!empty($field['alter']['text'])) {
$alter_text = $field['alter']['text'];
if (preg_match($old_part, $alter_text) === 1) {
$is_update = TRUE;
$field['alter']['text'] = preg_replace($old_part, $new_part, $alter_text);
}
}
if (!empty($field['alter']['path'])) {
$alter_path = $field['alter']['path'];
if (preg_match($old_part, $alter_path) === 1) {
$is_update = TRUE;
$field['alter']['path'] = preg_replace($old_part, $new_part, $alter_path);
}
}
if (str_contains($field_name, $old_field)) {
$is_update = TRUE;
$field['id'] = str_replace($old_field, $new_field, $field['id']);
$field['field'] = str_replace($old_field, $new_field, $field['field']);
$field_name_update = str_replace($old_field, $new_field, $field_name);
$fields = $display['display_options']['fields'];
$keys = array_keys($fields);
$keys[array_search($field_name, $keys)] = $field_name_update;
$display['display_options']['fields'] = array_combine($keys, $fields);
$display['display_options']['fields'][$field_name_update] = $field;
}
}
}
}
if ($is_update) {
$view
->set('display', $displays);
}
return $is_update;
}
public function needsRevisionFieldHyphenFix(ViewEntityInterface $view) : bool {
return $this
->processDisplayHandlers($view, TRUE, function (&$handler, $handler_type) use ($view) {
return $this
->processRevisionFieldHyphenFix($view);
});
}
public function needsDefaultArgumentSkipUrlUpdate(ViewEntityInterface $view) {
return $this
->processDisplayHandlers($view, TRUE, function (&$handler, $handler_type) {
return $this
->processDefaultArgumentSkipUrlUpdate($handler, $handler_type);
});
}
public function processDefaultArgumentSkipUrlUpdate(array &$handler, string $handler_type) : bool {
if ($handler_type === 'argument' && isset($handler['default_argument_skip_url'])) {
unset($handler['default_argument_skip_url']);
return TRUE;
}
return FALSE;
}
public function needsTaxonomyTermFilterUpdate(ViewEntityInterface $view) : bool {
return $this
->processDisplayHandlers($view, TRUE, function (&$handler, $handler_type) use ($view) {
return $this
->processTaxonomyTermFilterHandler($handler, $handler_type, $view);
});
}
protected function processTaxonomyTermFilterHandler(array &$handler, string $handler_type, ViewEntityInterface $view) : bool {
$changed = FALSE;
$plugin_id = $handler['plugin_id'] ?? '';
if ($handler_type === 'filter' && $plugin_id === 'taxonomy_index_tid') {
$executable = $view
->getExecutable();
$displays = $view
->get('display');
foreach ($displays as $display_id => &$display) {
$executable
->setDisplay($display_id);
$cache_metadata = $executable
->getDisplay()
->calculateCacheMetadata();
$display['cache_metadata']['contexts'] = $cache_metadata
->getCacheContexts();
$display['cache_metadata']['contexts'] = Cache::mergeContexts($display['cache_metadata']['contexts'], [
'languages:' . LanguageInterface::TYPE_INTERFACE,
]);
sort($display['cache_metadata']['contexts']);
}
$view
->set('display', $displays);
$changed = TRUE;
}
return $changed;
}
public function needsPagerHeadingUpdate(ViewEntityInterface $view) : bool {
return $this
->processDisplayHandlers($view, FALSE, function (&$handler, $handler_type) {
return $this
->processDefaultPagerHeadingUpdate($handler, $handler_type);
});
}
public function processDefaultPagerHeadingUpdate(array &$compound_handler, string $handler_type) : bool {
$allow_pager_type_update = [
'mini',
'full',
];
if ($handler_type === 'pager' && in_array($compound_handler['type'], $allow_pager_type_update) && !isset($compound_handler['options']['pagination_heading_level'])) {
$compound_handler['options']['pagination_heading_level'] = 'h4';
return TRUE;
}
return FALSE;
}
public function needsRenderedEntityFieldUpdate(ViewEntityInterface $view) : bool {
return $this
->processDisplayHandlers($view, TRUE, function (&$handler, $handler_type) {
return $this
->processRenderedEntityFieldHandler($handler, $handler_type);
});
}
protected function processRenderedEntityFieldHandler(array &$handler, string $handler_type) : bool {
$plugin_id = $handler['plugin_id'] ?? '';
return $handler_type === 'field' && $plugin_id === 'rendered_entity';
}
}