layout_builder.post_update.php

Same filename in other branches
  1. 9 core/modules/layout_builder/layout_builder.post_update.php
  2. 10 core/modules/layout_builder/layout_builder.post_update.php
  3. 11.x core/modules/layout_builder/layout_builder.post_update.php

Post update functions for Layout Builder.

File

core/modules/layout_builder/layout_builder.post_update.php

View source
<?php


/**
 * @file
 * Post update functions for Layout Builder.
 */
use Drupal\Core\Config\Entity\ConfigEntityUpdater;
use Drupal\layout_builder\Entity\LayoutEntityDisplayInterface;
use Drupal\layout_builder\TempStoreIdentifierInterface;
use Drupal\user\Entity\Role;
use Drupal\field\Entity\FieldStorageConfig;
use Drupal\field\Entity\FieldConfig;
use Drupal\layout_builder\Plugin\SectionStorage\OverridesSectionStorage;

/**
 * Rebuild plugin dependencies for all entity view displays.
 */
function layout_builder_post_update_rebuild_plugin_dependencies(&$sandbox = NULL) {
    $storage = \Drupal::entityTypeManager()->getStorage('entity_view_display');
    if (!isset($sandbox['ids'])) {
        $sandbox['ids'] = $storage->getQuery()
            ->accessCheck(FALSE)
            ->execute();
        $sandbox['count'] = count($sandbox['ids']);
    }
    for ($i = 0; $i < 10 && count($sandbox['ids']); $i++) {
        $id = array_shift($sandbox['ids']);
        if ($display = $storage->load($id)) {
            $display->save();
        }
    }
    $sandbox['#finished'] = empty($sandbox['ids']) ? 1 : ($sandbox['count'] - count($sandbox['ids'])) / $sandbox['count'];
}

/**
 * Ensure all extra fields are properly stored on entity view displays.
 *
 * Previously
 * \Drupal\layout_builder\Entity\LayoutBuilderEntityViewDisplay::setComponent()
 * was not correctly setting the configuration for extra fields. This function
 * calls setComponent() for all extra field components to ensure the updated
 * logic is invoked on all extra fields to correct the settings.
 */
function layout_builder_post_update_add_extra_fields(&$sandbox = NULL) {
    $entity_field_manager = \Drupal::service('entity_field.manager');
    \Drupal::classResolver(ConfigEntityUpdater::class)->update($sandbox, 'entity_view_display', function (LayoutEntityDisplayInterface $display) use ($entity_field_manager) {
        if (!$display->isLayoutBuilderEnabled()) {
            return FALSE;
        }
        $extra_fields = $entity_field_manager->getExtraFields($display->getTargetEntityTypeId(), $display->getTargetBundle());
        $components = $display->getComponents();
        // Sort the components to avoid them being reordered by setComponent().
        uasort($components, 'Drupal\\Component\\Utility\\SortArray::sortByWeightElement');
        $result = FALSE;
        foreach ($components as $name => $component) {
            if (isset($extra_fields['display'][$name])) {
                $display->setComponent($name, $component);
                $result = TRUE;
            }
        }
        return $result;
    });
}

/**
 * Clear caches due to changes to section storage annotation changes.
 */
function layout_builder_post_update_section_storage_context_definitions() {
    // Empty post-update hook.
}

/**
 * Clear caches due to changes to annotation changes to the Overrides plugin.
 */
function layout_builder_post_update_overrides_view_mode_annotation() {
    // Empty post-update hook.
}

/**
 * Clear caches due to routing changes for the new discard changes form.
 */
function layout_builder_post_update_cancel_link_to_discard_changes_form() {
    // Empty post-update hook.
}

/**
 * Clear caches due to the removal of the layout_is_rebuilding query string.
 */
function layout_builder_post_update_remove_layout_is_rebuilding() {
    // Empty post-update hook.
}

/**
 * Clear caches due to routing changes to move the Layout Builder UI to forms.
 */
function layout_builder_post_update_routing_entity_form() {
    // Empty post-update hook.
}

/**
 * Clear caches to discover new blank layout plugin.
 */
function layout_builder_post_update_discover_blank_layout_plugin() {
    // Empty post-update hook.
}

/**
 * Clear caches due to routing changes to changing the URLs for defaults.
 */
function layout_builder_post_update_routing_defaults() {
    // Empty post-update hook.
}

/**
 * Clear caches due to new link added to Layout Builder's contextual links.
 */
function layout_builder_post_update_discover_new_contextual_links() {
    // Empty post-update hook.
}

/**
 * Fix Layout Builder tempstore keys of existing entries.
 */
function layout_builder_post_update_fix_tempstore_keys() {
    
    /** @var \Drupal\layout_builder\SectionStorage\SectionStorageManagerInterface $section_storage_manager */
    $section_storage_manager = \Drupal::service('plugin.manager.layout_builder.section_storage');
    
    /** @var \Drupal\Core\KeyValueStore\KeyValueExpirableFactoryInterface $key_value_factory */
    $key_value_factory = \Drupal::service('keyvalue.expirable');
    // Loop through each section storage type.
    foreach (array_keys($section_storage_manager->getDefinitions()) as $section_storage_type) {
        $key_value = $key_value_factory->get("tempstore.shared.layout_builder.section_storage.{$section_storage_type}");
        foreach ($key_value->getAll() as $key => $value) {
            $contexts = $section_storage_manager->loadEmpty($section_storage_type)
                ->deriveContextsFromRoute($key, [], '', []);
            if ($section_storage = $section_storage_manager->load($section_storage_type, $contexts)) {
                // Some overrides were stored with an incorrect view mode value. Update
                // the view mode on the temporary section storage, if necessary.
                if ($section_storage_type === 'overrides') {
                    $view_mode = $value->data['section_storage']
                        ->getContextValue('view_mode');
                    $new_view_mode = $section_storage->getContextValue('view_mode');
                    if ($view_mode !== $new_view_mode) {
                        $value->data['section_storage']
                            ->setContextValue('view_mode', $new_view_mode);
                        $key_value->set($key, $value);
                    }
                }
                // The previous tempstore key names were exact matches with the section
                // storage ID. Attempt to load the corresponding section storage and
                // rename the tempstore entry if the section storage provides a more
                // granular tempstore key.
                if ($section_storage instanceof TempStoreIdentifierInterface) {
                    $new_key = $section_storage->getTempstoreKey();
                    if ($key !== $new_key) {
                        if ($key_value->has($new_key)) {
                            $key_value->delete($new_key);
                        }
                        $key_value->rename($key, $new_key);
                    }
                }
            }
        }
    }
}

/**
 * Clear caches due to config schema additions.
 */
function layout_builder_post_update_section_third_party_settings_schema() {
    // Empty post-update hook.
}

/**
 * Clear caches due to dependency changes in the layout_builder render element.
 */
function layout_builder_post_update_layout_builder_dependency_change() {
    // Empty post-update hook.
}

/**
 * Add new custom block permission to all roles with 'configure any layout'.
 */
function layout_builder_post_update_update_permissions() {
    foreach (Role::loadMultiple() as $role) {
        if ($role->hasPermission('configure any layout')) {
            $role->grantPermission('create and edit custom blocks')
                ->save();
        }
    }
}

/**
 * Clear caches due to addition of service decorator for entity form controller.
 */
function layout_builder_post_update_override_entity_form_controller() {
    // Empty post-update hook.
}

/**
 * Set the layout builder field as non-translatable where possible.
 */
function layout_builder_post_update_make_layout_untranslatable() {
    
    /** @var \Drupal\Core\Entity\EntityFieldManagerInterface $field_manager */
    $field_manager = \Drupal::service('entity_field.manager');
    $field_map = $field_manager->getFieldMap();
    foreach ($field_map as $entity_type_id => $field_infos) {
        if (isset($field_infos[OverridesSectionStorage::FIELD_NAME]['bundles'])) {
            $non_translatable_bundle_count = 0;
            foreach ($field_infos[OverridesSectionStorage::FIELD_NAME]['bundles'] as $bundle) {
                // The field map can contain stale information. If the field does not
                // exist, ignore it. The field map will be rebuilt when the cache is
                // cleared at the end of the update process.
                if (!($field_config = FieldConfig::loadByName($entity_type_id, $bundle, OverridesSectionStorage::FIELD_NAME))) {
                    continue;
                }
                if (!$field_config->isTranslatable()) {
                    $non_translatable_bundle_count++;
                    // The layout field is already configured to be non-translatable so it
                    // does not need to be updated.
                    continue;
                }
                if (_layout_builder_bundle_has_no_translations($entity_type_id, $bundle) || _layout_builder_bundle_has_no_layouts($entity_type_id, $bundle)) {
                    // Either none of the entities have layouts or none of them have
                    // translations. In either case it is safe to set the field to be
                    // non-translatable.
                    $field_config->setTranslatable(FALSE);
                    $field_config->save();
                    $non_translatable_bundle_count++;
                }
            }
            // Set the field storage to untranslatable if the field config for each
            // bundle is now untranslatable. This removes layout fields for the
            // entity type from the Content Translation configuration form.
            if (count($field_infos[OverridesSectionStorage::FIELD_NAME]['bundles']) === $non_translatable_bundle_count) {
                $field_storage = FieldStorageConfig::loadByName($entity_type_id, OverridesSectionStorage::FIELD_NAME);
                $field_storage->setTranslatable(FALSE);
                $field_storage->save();
            }
        }
    }
}

/**
 * Determines if there are zero layout overrides for the bundle.
 *
 * @param string $entity_type_id
 *   The entity type ID.
 * @param string $bundle
 *   The bundle name.
 *
 * @return bool
 *   TRUE if there are zero layout overrides for the bundle, otherwise FALSE.
 */
function _layout_builder_bundle_has_no_layouts($entity_type_id, $bundle) {
    $entity_update_manager = \Drupal::entityDefinitionUpdateManager();
    $entity_type = $entity_update_manager->getEntityType($entity_type_id);
    $bundle_key = $entity_type->getKey('bundle');
    $query = \Drupal::entityTypeManager()->getStorage($entity_type_id)
        ->getQuery();
    if ($bundle_key) {
        $query->condition($bundle_key, $bundle);
    }
    if ($entity_type->isRevisionable()) {
        $query->allRevisions();
    }
    $query->exists(OverridesSectionStorage::FIELD_NAME)
        ->accessCheck(FALSE)
        ->range(0, 1);
    $results = $query->execute();
    return empty($results);
}

/**
 * Determines if there are zero translations for the bundle.
 *
 * @param string $entity_type_id
 *   The entity type ID.
 * @param string $bundle
 *   The bundle name.
 *
 * @return bool
 *   TRUE if there are zero translations for the bundle, otherwise FALSE.
 */
function _layout_builder_bundle_has_no_translations($entity_type_id, $bundle) {
    $entity_update_manager = \Drupal::entityDefinitionUpdateManager();
    $entity_type = $entity_update_manager->getEntityType($entity_type_id);
    if (!$entity_type->isTranslatable()) {
        return TRUE;
    }
    $query = \Drupal::entityTypeManager()->getStorage($entity_type_id)
        ->getQuery();
    $bundle_key = $entity_type->getKey('bundle');
    if ($entity_type->hasKey('default_langcode')) {
        if ($bundle_key) {
            $query->condition($bundle_key, $bundle);
        }
        if ($entity_type->isRevisionable()) {
            $query->allRevisions();
        }
        $query->condition($entity_type->getKey('default_langcode'), 0)
            ->accessCheck(FALSE)
            ->range(0, 1);
        $results = $query->execute();
        return empty($results);
    }
    // A translatable entity type should always have a default_langcode key. If it
    // doesn't we have no way to determine if there are translations.
    return FALSE;
}

Functions

Title Deprecated Summary
layout_builder_post_update_add_extra_fields Ensure all extra fields are properly stored on entity view displays.
layout_builder_post_update_cancel_link_to_discard_changes_form Clear caches due to routing changes for the new discard changes form.
layout_builder_post_update_discover_blank_layout_plugin Clear caches to discover new blank layout plugin.
layout_builder_post_update_discover_new_contextual_links Clear caches due to new link added to Layout Builder's contextual links.
layout_builder_post_update_fix_tempstore_keys Fix Layout Builder tempstore keys of existing entries.
layout_builder_post_update_layout_builder_dependency_change Clear caches due to dependency changes in the layout_builder render element.
layout_builder_post_update_make_layout_untranslatable Set the layout builder field as non-translatable where possible.
layout_builder_post_update_overrides_view_mode_annotation Clear caches due to changes to annotation changes to the Overrides plugin.
layout_builder_post_update_override_entity_form_controller Clear caches due to addition of service decorator for entity form controller.
layout_builder_post_update_rebuild_plugin_dependencies Rebuild plugin dependencies for all entity view displays.
layout_builder_post_update_remove_layout_is_rebuilding Clear caches due to the removal of the layout_is_rebuilding query string.
layout_builder_post_update_routing_defaults Clear caches due to routing changes to changing the URLs for defaults.
layout_builder_post_update_routing_entity_form Clear caches due to routing changes to move the Layout Builder UI to forms.
layout_builder_post_update_section_storage_context_definitions Clear caches due to changes to section storage annotation changes.
layout_builder_post_update_section_third_party_settings_schema Clear caches due to config schema additions.
layout_builder_post_update_update_permissions Add new custom block permission to all roles with 'configure any layout'.
_layout_builder_bundle_has_no_layouts Determines if there are zero layout overrides for the bundle.
_layout_builder_bundle_has_no_translations Determines if there are zero translations for the bundle.

Buggy or inaccurate documentation? Please file an issue. Need support? Need help programming? Connect with the Drupal community.