Chris@14: getStorage('entity_view_display'); Chris@14: if (!isset($sandbox['ids'])) { Chris@14: $sandbox['ids'] = $storage->getQuery()->accessCheck(FALSE)->execute(); Chris@14: $sandbox['count'] = count($sandbox['ids']); Chris@14: } Chris@14: Chris@14: for ($i = 0; $i < 10 && count($sandbox['ids']); $i++) { Chris@14: $id = array_shift($sandbox['ids']); Chris@14: if ($display = $storage->load($id)) { Chris@14: $display->save(); Chris@14: } Chris@14: } Chris@14: Chris@14: $sandbox['#finished'] = empty($sandbox['ids']) ? 1 : ($sandbox['count'] - count($sandbox['ids'])) / $sandbox['count']; Chris@14: } Chris@17: Chris@17: /** Chris@17: * Ensure all extra fields are properly stored on entity view displays. Chris@17: * Chris@17: * Previously Chris@17: * \Drupal\layout_builder\Entity\LayoutBuilderEntityViewDisplay::setComponent() Chris@17: * was not correctly setting the configuration for extra fields. This function Chris@17: * calls setComponent() for all extra field components to ensure the updated Chris@17: * logic is invoked on all extra fields to correct the settings. Chris@17: */ Chris@17: function layout_builder_post_update_add_extra_fields(&$sandbox = NULL) { Chris@17: $entity_field_manager = \Drupal::service('entity_field.manager'); Chris@17: \Drupal::classResolver(ConfigEntityUpdater::class)->update($sandbox, 'entity_view_display', function (LayoutEntityDisplayInterface $display) use ($entity_field_manager) { Chris@17: if (!$display->isLayoutBuilderEnabled()) { Chris@17: return FALSE; Chris@17: } Chris@17: Chris@17: $extra_fields = $entity_field_manager->getExtraFields($display->getTargetEntityTypeId(), $display->getTargetBundle()); Chris@17: $components = $display->getComponents(); Chris@17: // Sort the components to avoid them being reordered by setComponent(). Chris@17: uasort($components, 'Drupal\Component\Utility\SortArray::sortByWeightElement'); Chris@17: $result = FALSE; Chris@17: foreach ($components as $name => $component) { Chris@17: if (isset($extra_fields['display'][$name])) { Chris@17: $display->setComponent($name, $component); Chris@17: $result = TRUE; Chris@17: } Chris@17: } Chris@17: return $result; Chris@17: }); Chris@17: } Chris@18: Chris@18: /** Chris@18: * Clear caches due to changes to section storage annotation changes. Chris@18: */ Chris@18: function layout_builder_post_update_section_storage_context_definitions() { Chris@18: // Empty post-update hook. Chris@18: } Chris@18: Chris@18: /** Chris@18: * Clear caches due to changes to annotation changes to the Overrides plugin. Chris@18: */ Chris@18: function layout_builder_post_update_overrides_view_mode_annotation() { Chris@18: // Empty post-update hook. Chris@18: } Chris@18: Chris@18: /** Chris@18: * Clear caches due to routing changes for the new discard changes form. Chris@18: */ Chris@18: function layout_builder_post_update_cancel_link_to_discard_changes_form() { Chris@18: // Empty post-update hook. Chris@18: } Chris@18: Chris@18: /** Chris@18: * Clear caches due to the removal of the layout_is_rebuilding query string. Chris@18: */ Chris@18: function layout_builder_post_update_remove_layout_is_rebuilding() { Chris@18: // Empty post-update hook. Chris@18: } Chris@18: Chris@18: /** Chris@18: * Clear caches due to routing changes to move the Layout Builder UI to forms. Chris@18: */ Chris@18: function layout_builder_post_update_routing_entity_form() { Chris@18: // Empty post-update hook. Chris@18: } Chris@18: Chris@18: /** Chris@18: * Clear caches to discover new blank layout plugin. Chris@18: */ Chris@18: function layout_builder_post_update_discover_blank_layout_plugin() { Chris@18: // Empty post-update hook. Chris@18: } Chris@18: Chris@18: /** Chris@18: * Clear caches due to routing changes to changing the URLs for defaults. Chris@18: */ Chris@18: function layout_builder_post_update_routing_defaults() { Chris@18: // Empty post-update hook. Chris@18: } Chris@18: Chris@18: /** Chris@18: * Clear caches due to new link added to Layout Builder's contextual links. Chris@18: */ Chris@18: function layout_builder_post_update_discover_new_contextual_links() { Chris@18: // Empty post-update hook. Chris@18: } Chris@18: Chris@18: /** Chris@18: * Fix Layout Builder tempstore keys of existing entries. Chris@18: */ Chris@18: function layout_builder_post_update_fix_tempstore_keys() { Chris@18: /** @var \Drupal\layout_builder\SectionStorage\SectionStorageManagerInterface $section_storage_manager */ Chris@18: $section_storage_manager = \Drupal::service('plugin.manager.layout_builder.section_storage'); Chris@18: /** @var \Drupal\Core\KeyValueStore\KeyValueExpirableFactoryInterface $key_value_factory */ Chris@18: $key_value_factory = \Drupal::service('keyvalue.expirable'); Chris@18: Chris@18: // Loop through each section storage type. Chris@18: foreach (array_keys($section_storage_manager->getDefinitions()) as $section_storage_type) { Chris@18: $key_value = $key_value_factory->get("tempstore.shared.layout_builder.section_storage.$section_storage_type"); Chris@18: foreach ($key_value->getAll() as $key => $value) { Chris@18: $contexts = $section_storage_manager->loadEmpty($section_storage_type)->deriveContextsFromRoute($key, [], '', []); Chris@18: if ($section_storage = $section_storage_manager->load($section_storage_type, $contexts)) { Chris@18: Chris@18: // Some overrides were stored with an incorrect view mode value. Update Chris@18: // the view mode on the temporary section storage, if necessary. Chris@18: if ($section_storage_type === 'overrides') { Chris@18: $view_mode = $value->data['section_storage']->getContextValue('view_mode'); Chris@18: $new_view_mode = $section_storage->getContextValue('view_mode'); Chris@18: if ($view_mode !== $new_view_mode) { Chris@18: $value->data['section_storage']->setContextValue('view_mode', $new_view_mode); Chris@18: $key_value->set($key, $value); Chris@18: } Chris@18: } Chris@18: Chris@18: // The previous tempstore key names were exact matches with the section Chris@18: // storage ID. Attempt to load the corresponding section storage and Chris@18: // rename the tempstore entry if the section storage provides a more Chris@18: // granular tempstore key. Chris@18: if ($section_storage instanceof TempStoreIdentifierInterface) { Chris@18: $new_key = $section_storage->getTempstoreKey(); Chris@18: if ($key !== $new_key) { Chris@18: if ($key_value->has($new_key)) { Chris@18: $key_value->delete($new_key); Chris@18: } Chris@18: $key_value->rename($key, $new_key); Chris@18: } Chris@18: } Chris@18: } Chris@18: } Chris@18: } Chris@18: } Chris@18: Chris@18: /** Chris@18: * Clear caches due to config schema additions. Chris@18: */ Chris@18: function layout_builder_post_update_section_third_party_settings_schema() { Chris@18: // Empty post-update hook. Chris@18: } Chris@18: Chris@18: /** Chris@18: * Clear caches due to dependency changes in the layout_builder render element. Chris@18: */ Chris@18: function layout_builder_post_update_layout_builder_dependency_change() { Chris@18: // Empty post-update hook. Chris@18: } Chris@18: Chris@18: /** Chris@18: * Add new custom block permission to all roles with 'configure any layout'. Chris@18: */ Chris@18: function layout_builder_post_update_update_permissions() { Chris@18: foreach (Role::loadMultiple() as $role) { Chris@18: if ($role->hasPermission('configure any layout')) { Chris@18: $role->grantPermission('create and edit custom blocks')->save(); Chris@18: } Chris@18: } Chris@18: } Chris@18: Chris@18: /** Chris@18: * Set the layout builder field as non-translatable where possible. Chris@18: */ Chris@18: function layout_builder_post_update_make_layout_untranslatable() { Chris@18: /** @var \Drupal\Core\Entity\EntityFieldManagerInterface $field_manager */ Chris@18: $field_manager = \Drupal::service('entity_field.manager'); Chris@18: $field_map = $field_manager->getFieldMap(); Chris@18: foreach ($field_map as $entity_type_id => $field_infos) { Chris@18: if (isset($field_infos[OverridesSectionStorage::FIELD_NAME]['bundles'])) { Chris@18: $non_translatable_bundle_count = 0; Chris@18: foreach ($field_infos[OverridesSectionStorage::FIELD_NAME]['bundles'] as $bundle) { Chris@18: $field_config = FieldConfig::loadByName($entity_type_id, $bundle, OverridesSectionStorage::FIELD_NAME); Chris@18: if (!$field_config->isTranslatable()) { Chris@18: $non_translatable_bundle_count++; Chris@18: // The layout field is already configured to be non-translatable so it Chris@18: // does not need to be updated. Chris@18: continue; Chris@18: } Chris@18: if (_layout_builder_bundle_has_no_translations($entity_type_id, $bundle) || _layout_builder_bundle_has_no_layouts($entity_type_id, $bundle)) { Chris@18: // Either none of the entities have layouts or none of them have Chris@18: // translations. In either case it is safe to set the field to be Chris@18: // non-translatable. Chris@18: $field_config->setTranslatable(FALSE); Chris@18: $field_config->save(); Chris@18: $non_translatable_bundle_count++; Chris@18: } Chris@18: } Chris@18: // Set the field storage to untranslatable if the field config for each Chris@18: // bundle is now untranslatable. This removes layout fields for the Chris@18: // entity type from the Content Translation configuration form. Chris@18: if (count($field_infos[OverridesSectionStorage::FIELD_NAME]['bundles']) === $non_translatable_bundle_count) { Chris@18: $field_storage = FieldStorageConfig::loadByName($entity_type_id, OverridesSectionStorage::FIELD_NAME); Chris@18: $field_storage->setTranslatable(FALSE); Chris@18: $field_storage->save(); Chris@18: } Chris@18: } Chris@18: } Chris@18: } Chris@18: Chris@18: /** Chris@18: * Determines if there are zero layout overrides for the bundle. Chris@18: * Chris@18: * @param string $entity_type_id Chris@18: * The entity type ID. Chris@18: * @param string $bundle Chris@18: * The bundle name. Chris@18: * Chris@18: * @return bool Chris@18: * TRUE if there are zero layout overrides for the bundle, otherwise FALSE. Chris@18: */ Chris@18: function _layout_builder_bundle_has_no_layouts($entity_type_id, $bundle) { Chris@18: $entity_type_manager = \Drupal::entityTypeManager(); Chris@18: $entity_type = $entity_type_manager->getDefinition($entity_type_id); Chris@18: $bundle_key = $entity_type->getKey('bundle'); Chris@18: $query = $entity_type_manager->getStorage($entity_type_id)->getQuery(); Chris@18: if ($bundle_key) { Chris@18: $query->condition($bundle_key, $bundle); Chris@18: } Chris@18: if ($entity_type->isRevisionable()) { Chris@18: $query->allRevisions(); Chris@18: } Chris@18: $query->exists(OverridesSectionStorage::FIELD_NAME) Chris@18: ->accessCheck(FALSE) Chris@18: ->range(0, 1); Chris@18: $results = $query->execute(); Chris@18: return empty($results); Chris@18: } Chris@18: Chris@18: /** Chris@18: * Determines if there are zero translations for the bundle. Chris@18: * Chris@18: * @param string $entity_type_id Chris@18: * The entity type ID. Chris@18: * @param string $bundle Chris@18: * The bundle name. Chris@18: * Chris@18: * @return bool Chris@18: * TRUE if there are zero translations for the bundle, otherwise FALSE. Chris@18: */ Chris@18: function _layout_builder_bundle_has_no_translations($entity_type_id, $bundle) { Chris@18: $entity_type_manager = \Drupal::entityTypeManager(); Chris@18: $entity_type = $entity_type_manager->getDefinition($entity_type_id); Chris@18: if (!$entity_type->isTranslatable()) { Chris@18: return TRUE; Chris@18: } Chris@18: $query = $entity_type_manager->getStorage($entity_type_id)->getQuery(); Chris@18: $bundle_key = $entity_type->getKey('bundle'); Chris@18: if ($entity_type->hasKey('default_langcode')) { Chris@18: if ($bundle_key) { Chris@18: $query->condition($bundle_key, $bundle); Chris@18: } Chris@18: if ($entity_type->isRevisionable()) { Chris@18: $query->allRevisions(); Chris@18: } Chris@18: $query->condition($entity_type->getKey('default_langcode'), 0) Chris@18: ->accessCheck(FALSE) Chris@18: ->range(0, 1); Chris@18: $results = $query->execute(); Chris@18: return empty($results); Chris@18: } Chris@18: // A translatable entity type should always have a default_langcode key. If it Chris@18: // doesn't we have no way to determine if there are translations. Chris@18: return FALSE; Chris@18: }