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 }
|