You are here

layout_builder.post_update.php in Drupal 8

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

Namesort descending Description
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.