annotate core/modules/layout_builder/layout_builder.post_update.php @ 19:fa3358dc1485 tip

Add ndrum files
author Chris Cannam
date Wed, 28 Aug 2019 13:14:47 +0100
parents af1871eacc83
children
rev   line source
Chris@14 1 <?php
Chris@14 2
Chris@14 3 /**
Chris@14 4 * @file
Chris@14 5 * Post update functions for Layout Builder.
Chris@14 6 */
Chris@14 7
Chris@17 8 use Drupal\Core\Config\Entity\ConfigEntityUpdater;
Chris@17 9 use Drupal\layout_builder\Entity\LayoutEntityDisplayInterface;
Chris@18 10 use Drupal\layout_builder\TempStoreIdentifierInterface;
Chris@18 11 use Drupal\user\Entity\Role;
Chris@18 12 use Drupal\field\Entity\FieldStorageConfig;
Chris@18 13 use Drupal\field\Entity\FieldConfig;
Chris@18 14 use Drupal\layout_builder\Plugin\SectionStorage\OverridesSectionStorage;
Chris@17 15
Chris@14 16 /**
Chris@14 17 * Rebuild plugin dependencies for all entity view displays.
Chris@14 18 */
Chris@14 19 function layout_builder_post_update_rebuild_plugin_dependencies(&$sandbox = NULL) {
Chris@14 20 $storage = \Drupal::entityTypeManager()->getStorage('entity_view_display');
Chris@14 21 if (!isset($sandbox['ids'])) {
Chris@14 22 $sandbox['ids'] = $storage->getQuery()->accessCheck(FALSE)->execute();
Chris@14 23 $sandbox['count'] = count($sandbox['ids']);
Chris@14 24 }
Chris@14 25
Chris@14 26 for ($i = 0; $i < 10 && count($sandbox['ids']); $i++) {
Chris@14 27 $id = array_shift($sandbox['ids']);
Chris@14 28 if ($display = $storage->load($id)) {
Chris@14 29 $display->save();
Chris@14 30 }
Chris@14 31 }
Chris@14 32
Chris@14 33 $sandbox['#finished'] = empty($sandbox['ids']) ? 1 : ($sandbox['count'] - count($sandbox['ids'])) / $sandbox['count'];
Chris@14 34 }
Chris@17 35
Chris@17 36 /**
Chris@17 37 * Ensure all extra fields are properly stored on entity view displays.
Chris@17 38 *
Chris@17 39 * Previously
Chris@17 40 * \Drupal\layout_builder\Entity\LayoutBuilderEntityViewDisplay::setComponent()
Chris@17 41 * was not correctly setting the configuration for extra fields. This function
Chris@17 42 * calls setComponent() for all extra field components to ensure the updated
Chris@17 43 * logic is invoked on all extra fields to correct the settings.
Chris@17 44 */
Chris@17 45 function layout_builder_post_update_add_extra_fields(&$sandbox = NULL) {
Chris@17 46 $entity_field_manager = \Drupal::service('entity_field.manager');
Chris@17 47 \Drupal::classResolver(ConfigEntityUpdater::class)->update($sandbox, 'entity_view_display', function (LayoutEntityDisplayInterface $display) use ($entity_field_manager) {
Chris@17 48 if (!$display->isLayoutBuilderEnabled()) {
Chris@17 49 return FALSE;
Chris@17 50 }
Chris@17 51
Chris@17 52 $extra_fields = $entity_field_manager->getExtraFields($display->getTargetEntityTypeId(), $display->getTargetBundle());
Chris@17 53 $components = $display->getComponents();
Chris@17 54 // Sort the components to avoid them being reordered by setComponent().
Chris@17 55 uasort($components, 'Drupal\Component\Utility\SortArray::sortByWeightElement');
Chris@17 56 $result = FALSE;
Chris@17 57 foreach ($components as $name => $component) {
Chris@17 58 if (isset($extra_fields['display'][$name])) {
Chris@17 59 $display->setComponent($name, $component);
Chris@17 60 $result = TRUE;
Chris@17 61 }
Chris@17 62 }
Chris@17 63 return $result;
Chris@17 64 });
Chris@17 65 }
Chris@18 66
Chris@18 67 /**
Chris@18 68 * Clear caches due to changes to section storage annotation changes.
Chris@18 69 */
Chris@18 70 function layout_builder_post_update_section_storage_context_definitions() {
Chris@18 71 // Empty post-update hook.
Chris@18 72 }
Chris@18 73
Chris@18 74 /**
Chris@18 75 * Clear caches due to changes to annotation changes to the Overrides plugin.
Chris@18 76 */
Chris@18 77 function layout_builder_post_update_overrides_view_mode_annotation() {
Chris@18 78 // Empty post-update hook.
Chris@18 79 }
Chris@18 80
Chris@18 81 /**
Chris@18 82 * Clear caches due to routing changes for the new discard changes form.
Chris@18 83 */
Chris@18 84 function layout_builder_post_update_cancel_link_to_discard_changes_form() {
Chris@18 85 // Empty post-update hook.
Chris@18 86 }
Chris@18 87
Chris@18 88 /**
Chris@18 89 * Clear caches due to the removal of the layout_is_rebuilding query string.
Chris@18 90 */
Chris@18 91 function layout_builder_post_update_remove_layout_is_rebuilding() {
Chris@18 92 // Empty post-update hook.
Chris@18 93 }
Chris@18 94
Chris@18 95 /**
Chris@18 96 * Clear caches due to routing changes to move the Layout Builder UI to forms.
Chris@18 97 */
Chris@18 98 function layout_builder_post_update_routing_entity_form() {
Chris@18 99 // Empty post-update hook.
Chris@18 100 }
Chris@18 101
Chris@18 102 /**
Chris@18 103 * Clear caches to discover new blank layout plugin.
Chris@18 104 */
Chris@18 105 function layout_builder_post_update_discover_blank_layout_plugin() {
Chris@18 106 // Empty post-update hook.
Chris@18 107 }
Chris@18 108
Chris@18 109 /**
Chris@18 110 * Clear caches due to routing changes to changing the URLs for defaults.
Chris@18 111 */
Chris@18 112 function layout_builder_post_update_routing_defaults() {
Chris@18 113 // Empty post-update hook.
Chris@18 114 }
Chris@18 115
Chris@18 116 /**
Chris@18 117 * Clear caches due to new link added to Layout Builder's contextual links.
Chris@18 118 */
Chris@18 119 function layout_builder_post_update_discover_new_contextual_links() {
Chris@18 120 // Empty post-update hook.
Chris@18 121 }
Chris@18 122
Chris@18 123 /**
Chris@18 124 * Fix Layout Builder tempstore keys of existing entries.
Chris@18 125 */
Chris@18 126 function layout_builder_post_update_fix_tempstore_keys() {
Chris@18 127 /** @var \Drupal\layout_builder\SectionStorage\SectionStorageManagerInterface $section_storage_manager */
Chris@18 128 $section_storage_manager = \Drupal::service('plugin.manager.layout_builder.section_storage');
Chris@18 129 /** @var \Drupal\Core\KeyValueStore\KeyValueExpirableFactoryInterface $key_value_factory */
Chris@18 130 $key_value_factory = \Drupal::service('keyvalue.expirable');
Chris@18 131
Chris@18 132 // Loop through each section storage type.
Chris@18 133 foreach (array_keys($section_storage_manager->getDefinitions()) as $section_storage_type) {
Chris@18 134 $key_value = $key_value_factory->get("tempstore.shared.layout_builder.section_storage.$section_storage_type");
Chris@18 135 foreach ($key_value->getAll() as $key => $value) {
Chris@18 136 $contexts = $section_storage_manager->loadEmpty($section_storage_type)->deriveContextsFromRoute($key, [], '', []);
Chris@18 137 if ($section_storage = $section_storage_manager->load($section_storage_type, $contexts)) {
Chris@18 138
Chris@18 139 // Some overrides were stored with an incorrect view mode value. Update
Chris@18 140 // the view mode on the temporary section storage, if necessary.
Chris@18 141 if ($section_storage_type === 'overrides') {
Chris@18 142 $view_mode = $value->data['section_storage']->getContextValue('view_mode');
Chris@18 143 $new_view_mode = $section_storage->getContextValue('view_mode');
Chris@18 144 if ($view_mode !== $new_view_mode) {
Chris@18 145 $value->data['section_storage']->setContextValue('view_mode', $new_view_mode);
Chris@18 146 $key_value->set($key, $value);
Chris@18 147 }
Chris@18 148 }
Chris@18 149
Chris@18 150 // The previous tempstore key names were exact matches with the section
Chris@18 151 // storage ID. Attempt to load the corresponding section storage and
Chris@18 152 // rename the tempstore entry if the section storage provides a more
Chris@18 153 // granular tempstore key.
Chris@18 154 if ($section_storage instanceof TempStoreIdentifierInterface) {
Chris@18 155 $new_key = $section_storage->getTempstoreKey();
Chris@18 156 if ($key !== $new_key) {
Chris@18 157 if ($key_value->has($new_key)) {
Chris@18 158 $key_value->delete($new_key);
Chris@18 159 }
Chris@18 160 $key_value->rename($key, $new_key);
Chris@18 161 }
Chris@18 162 }
Chris@18 163 }
Chris@18 164 }
Chris@18 165 }
Chris@18 166 }
Chris@18 167
Chris@18 168 /**
Chris@18 169 * Clear caches due to config schema additions.
Chris@18 170 */
Chris@18 171 function layout_builder_post_update_section_third_party_settings_schema() {
Chris@18 172 // Empty post-update hook.
Chris@18 173 }
Chris@18 174
Chris@18 175 /**
Chris@18 176 * Clear caches due to dependency changes in the layout_builder render element.
Chris@18 177 */
Chris@18 178 function layout_builder_post_update_layout_builder_dependency_change() {
Chris@18 179 // Empty post-update hook.
Chris@18 180 }
Chris@18 181
Chris@18 182 /**
Chris@18 183 * Add new custom block permission to all roles with 'configure any layout'.
Chris@18 184 */
Chris@18 185 function layout_builder_post_update_update_permissions() {
Chris@18 186 foreach (Role::loadMultiple() as $role) {
Chris@18 187 if ($role->hasPermission('configure any layout')) {
Chris@18 188 $role->grantPermission('create and edit custom blocks')->save();
Chris@18 189 }
Chris@18 190 }
Chris@18 191 }
Chris@18 192
Chris@18 193 /**
Chris@18 194 * Set the layout builder field as non-translatable where possible.
Chris@18 195 */
Chris@18 196 function layout_builder_post_update_make_layout_untranslatable() {
Chris@18 197 /** @var \Drupal\Core\Entity\EntityFieldManagerInterface $field_manager */
Chris@18 198 $field_manager = \Drupal::service('entity_field.manager');
Chris@18 199 $field_map = $field_manager->getFieldMap();
Chris@18 200 foreach ($field_map as $entity_type_id => $field_infos) {
Chris@18 201 if (isset($field_infos[OverridesSectionStorage::FIELD_NAME]['bundles'])) {
Chris@18 202 $non_translatable_bundle_count = 0;
Chris@18 203 foreach ($field_infos[OverridesSectionStorage::FIELD_NAME]['bundles'] as $bundle) {
Chris@18 204 $field_config = FieldConfig::loadByName($entity_type_id, $bundle, OverridesSectionStorage::FIELD_NAME);
Chris@18 205 if (!$field_config->isTranslatable()) {
Chris@18 206 $non_translatable_bundle_count++;
Chris@18 207 // The layout field is already configured to be non-translatable so it
Chris@18 208 // does not need to be updated.
Chris@18 209 continue;
Chris@18 210 }
Chris@18 211 if (_layout_builder_bundle_has_no_translations($entity_type_id, $bundle) || _layout_builder_bundle_has_no_layouts($entity_type_id, $bundle)) {
Chris@18 212 // Either none of the entities have layouts or none of them have
Chris@18 213 // translations. In either case it is safe to set the field to be
Chris@18 214 // non-translatable.
Chris@18 215 $field_config->setTranslatable(FALSE);
Chris@18 216 $field_config->save();
Chris@18 217 $non_translatable_bundle_count++;
Chris@18 218 }
Chris@18 219 }
Chris@18 220 // Set the field storage to untranslatable if the field config for each
Chris@18 221 // bundle is now untranslatable. This removes layout fields for the
Chris@18 222 // entity type from the Content Translation configuration form.
Chris@18 223 if (count($field_infos[OverridesSectionStorage::FIELD_NAME]['bundles']) === $non_translatable_bundle_count) {
Chris@18 224 $field_storage = FieldStorageConfig::loadByName($entity_type_id, OverridesSectionStorage::FIELD_NAME);
Chris@18 225 $field_storage->setTranslatable(FALSE);
Chris@18 226 $field_storage->save();
Chris@18 227 }
Chris@18 228 }
Chris@18 229 }
Chris@18 230 }
Chris@18 231
Chris@18 232 /**
Chris@18 233 * Determines if there are zero layout overrides for the bundle.
Chris@18 234 *
Chris@18 235 * @param string $entity_type_id
Chris@18 236 * The entity type ID.
Chris@18 237 * @param string $bundle
Chris@18 238 * The bundle name.
Chris@18 239 *
Chris@18 240 * @return bool
Chris@18 241 * TRUE if there are zero layout overrides for the bundle, otherwise FALSE.
Chris@18 242 */
Chris@18 243 function _layout_builder_bundle_has_no_layouts($entity_type_id, $bundle) {
Chris@18 244 $entity_type_manager = \Drupal::entityTypeManager();
Chris@18 245 $entity_type = $entity_type_manager->getDefinition($entity_type_id);
Chris@18 246 $bundle_key = $entity_type->getKey('bundle');
Chris@18 247 $query = $entity_type_manager->getStorage($entity_type_id)->getQuery();
Chris@18 248 if ($bundle_key) {
Chris@18 249 $query->condition($bundle_key, $bundle);
Chris@18 250 }
Chris@18 251 if ($entity_type->isRevisionable()) {
Chris@18 252 $query->allRevisions();
Chris@18 253 }
Chris@18 254 $query->exists(OverridesSectionStorage::FIELD_NAME)
Chris@18 255 ->accessCheck(FALSE)
Chris@18 256 ->range(0, 1);
Chris@18 257 $results = $query->execute();
Chris@18 258 return empty($results);
Chris@18 259 }
Chris@18 260
Chris@18 261 /**
Chris@18 262 * Determines if there are zero translations for the bundle.
Chris@18 263 *
Chris@18 264 * @param string $entity_type_id
Chris@18 265 * The entity type ID.
Chris@18 266 * @param string $bundle
Chris@18 267 * The bundle name.
Chris@18 268 *
Chris@18 269 * @return bool
Chris@18 270 * TRUE if there are zero translations for the bundle, otherwise FALSE.
Chris@18 271 */
Chris@18 272 function _layout_builder_bundle_has_no_translations($entity_type_id, $bundle) {
Chris@18 273 $entity_type_manager = \Drupal::entityTypeManager();
Chris@18 274 $entity_type = $entity_type_manager->getDefinition($entity_type_id);
Chris@18 275 if (!$entity_type->isTranslatable()) {
Chris@18 276 return TRUE;
Chris@18 277 }
Chris@18 278 $query = $entity_type_manager->getStorage($entity_type_id)->getQuery();
Chris@18 279 $bundle_key = $entity_type->getKey('bundle');
Chris@18 280 if ($entity_type->hasKey('default_langcode')) {
Chris@18 281 if ($bundle_key) {
Chris@18 282 $query->condition($bundle_key, $bundle);
Chris@18 283 }
Chris@18 284 if ($entity_type->isRevisionable()) {
Chris@18 285 $query->allRevisions();
Chris@18 286 }
Chris@18 287 $query->condition($entity_type->getKey('default_langcode'), 0)
Chris@18 288 ->accessCheck(FALSE)
Chris@18 289 ->range(0, 1);
Chris@18 290 $results = $query->execute();
Chris@18 291 return empty($results);
Chris@18 292 }
Chris@18 293 // A translatable entity type should always have a default_langcode key. If it
Chris@18 294 // doesn't we have no way to determine if there are translations.
Chris@18 295 return FALSE;
Chris@18 296 }