layout_builder.module 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204
  1. <?php
  2. /**
  3. * @file
  4. * Provides hook implementations for Layout Builder.
  5. */
  6. use Drupal\Core\Entity\EntityInterface;
  7. use Drupal\Core\Form\FormStateInterface;
  8. use Drupal\Core\Routing\RouteMatchInterface;
  9. use Drupal\Core\Url;
  10. use Drupal\field\FieldConfigInterface;
  11. use Drupal\layout_builder\Entity\LayoutBuilderEntityViewDisplay;
  12. use Drupal\layout_builder\Entity\LayoutBuilderEntityViewDisplayStorage;
  13. use Drupal\layout_builder\Form\LayoutBuilderEntityViewDisplayForm;
  14. use Drupal\Core\Entity\Display\EntityViewDisplayInterface;
  15. use Drupal\layout_builder\Entity\LayoutEntityDisplayInterface;
  16. use Drupal\layout_builder\Plugin\Block\ExtraFieldBlock;
  17. use Drupal\layout_builder\InlineBlockEntityOperations;
  18. use Drupal\Core\Session\AccountInterface;
  19. use Drupal\Core\Access\AccessResult;
  20. /**
  21. * Implements hook_help().
  22. */
  23. function layout_builder_help($route_name, RouteMatchInterface $route_match) {
  24. // Add help text to the Layout Builder UI.
  25. if ($route_match->getRouteObject()->getOption('_layout_builder')) {
  26. $output = '<p>' . t('This layout builder tool allows you to configure the layout of the main content area.') . '</p>';
  27. if (\Drupal::currentUser()->hasPermission('administer blocks')) {
  28. $output .= '<p>' . t('To manage other areas of the page, use the <a href="@block-ui">block administration page</a>.', ['@block-ui' => Url::fromRoute('block.admin_display')->toString()]) . '</p>';
  29. }
  30. else {
  31. $output .= '<p>' . t('To manage other areas of the page, use the block administration page.') . '</p>';
  32. }
  33. return $output;
  34. }
  35. switch ($route_name) {
  36. case 'help.page.layout_builder':
  37. $output = '<h3>' . t('About') . '</h3>';
  38. $output .= '<p>' . t('Layout Builder provides layout building utility.') . '</p>';
  39. $output .= '<p>' . t('For more information, see the <a href=":layout-builder-documentation">online documentation for the Layout Builder module</a>.', [':layout-builder-documentation' => 'https://www.drupal.org/docs/8/core/modules/layout_builder']) . '</p>';
  40. return $output;
  41. }
  42. }
  43. /**
  44. * Implements hook_entity_type_alter().
  45. */
  46. function layout_builder_entity_type_alter(array &$entity_types) {
  47. /** @var \Drupal\Core\Entity\EntityTypeInterface[] $entity_types */
  48. $entity_types['entity_view_display']
  49. ->setClass(LayoutBuilderEntityViewDisplay::class)
  50. ->setStorageClass(LayoutBuilderEntityViewDisplayStorage::class)
  51. ->setFormClass('edit', LayoutBuilderEntityViewDisplayForm::class);
  52. }
  53. /**
  54. * Implements hook_form_FORM_ID_alter() for \Drupal\field_ui\Form\EntityFormDisplayEditForm.
  55. */
  56. function layout_builder_form_entity_form_display_edit_form_alter(&$form, FormStateInterface $form_state) {
  57. // Hides the Layout Builder field. It is rendered directly in
  58. // \Drupal\layout_builder\Entity\LayoutBuilderEntityViewDisplay::buildMultiple().
  59. unset($form['fields']['layout_builder__layout']);
  60. $key = array_search('layout_builder__layout', $form['#fields']);
  61. if ($key !== FALSE) {
  62. unset($form['#fields'][$key]);
  63. }
  64. }
  65. /**
  66. * Implements hook_field_config_insert().
  67. */
  68. function layout_builder_field_config_insert(FieldConfigInterface $field_config) {
  69. // Clear the sample entity for this entity type and bundle.
  70. $sample_entity_generator = \Drupal::service('layout_builder.sample_entity_generator');
  71. $sample_entity_generator->delete($field_config->getTargetEntityTypeId(), $field_config->getTargetBundle());
  72. \Drupal::service('plugin.manager.block')->clearCachedDefinitions();
  73. }
  74. /**
  75. * Implements hook_field_config_delete().
  76. */
  77. function layout_builder_field_config_delete(FieldConfigInterface $field_config) {
  78. // Clear the sample entity for this entity type and bundle.
  79. $sample_entity_generator = \Drupal::service('layout_builder.sample_entity_generator');
  80. $sample_entity_generator->delete($field_config->getTargetEntityTypeId(), $field_config->getTargetBundle());
  81. \Drupal::service('plugin.manager.block')->clearCachedDefinitions();
  82. }
  83. /**
  84. * Implements hook_entity_view_alter().
  85. *
  86. * ExtraFieldBlock block plugins add placeholders for each extra field which is
  87. * configured to be displayed. Those placeholders are replaced by this hook.
  88. * Modules that implement hook_entity_extra_field_info() use their
  89. * implementations of hook_entity_view_alter() to add the rendered output of
  90. * the extra fields they provide, so we cannot get the rendered output of extra
  91. * fields before this point in the view process.
  92. * layout_builder_module_implements_alter() moves this implementation of
  93. * hook_entity_view_alter() to the end of the list.
  94. *
  95. * @see \Drupal\layout_builder\Plugin\Block\ExtraFieldBlock::build()
  96. * @see layout_builder_module_implements_alter()
  97. */
  98. function layout_builder_entity_view_alter(array &$build, EntityInterface $entity, EntityViewDisplayInterface $display) {
  99. if ($display instanceof LayoutEntityDisplayInterface) {
  100. /** @var \Drupal\Core\Entity\EntityFieldManagerInterface $field_manager */
  101. $field_manager = \Drupal::service('entity_field.manager');
  102. $extra_fields = $field_manager->getExtraFields($entity->getEntityTypeId(), $entity->bundle());
  103. if (!empty($extra_fields['display'])) {
  104. foreach ($extra_fields['display'] as $field_name => $extra_field) {
  105. // If the extra field is not set replace with an empty array to avoid
  106. // the placeholder text from being rendered.
  107. $replacement = isset($build[$field_name]) ? $build[$field_name] : [];
  108. ExtraFieldBlock::replaceFieldPlaceholder($build, $replacement, $field_name);
  109. // After the rendered field in $build has been copied over to the
  110. // ExtraFieldBlock block we must remove it from its original location or
  111. // else it will be rendered twice.
  112. unset($build[$field_name]);
  113. }
  114. }
  115. }
  116. }
  117. /**
  118. * Implements hook_builder_module_implements_alter().
  119. */
  120. function layout_builder_module_implements_alter(&$implementations, $hook) {
  121. if ($hook === 'entity_view_alter') {
  122. // Ensure that this module's implementation of hook_entity_view_alter() runs
  123. // last so that other modules that use this hook to render extra fields will
  124. // run before it.
  125. $group = $implementations['layout_builder'];
  126. unset($implementations['layout_builder']);
  127. $implementations['layout_builder'] = $group;
  128. }
  129. }
  130. /**
  131. * Implements hook_entity_presave().
  132. */
  133. function layout_builder_entity_presave(EntityInterface $entity) {
  134. if (\Drupal::moduleHandler()->moduleExists('block_content')) {
  135. /** @var \Drupal\layout_builder\InlineBlockEntityOperations $entity_operations */
  136. $entity_operations = \Drupal::classResolver(InlineBlockEntityOperations::class);
  137. $entity_operations->handlePreSave($entity);
  138. }
  139. }
  140. /**
  141. * Implements hook_entity_delete().
  142. */
  143. function layout_builder_entity_delete(EntityInterface $entity) {
  144. if (\Drupal::moduleHandler()->moduleExists('block_content')) {
  145. /** @var \Drupal\layout_builder\InlineBlockEntityOperations $entity_operations */
  146. $entity_operations = \Drupal::classResolver(InlineBlockEntityOperations::class);
  147. $entity_operations->handleEntityDelete($entity);
  148. }
  149. }
  150. /**
  151. * Implements hook_cron().
  152. */
  153. function layout_builder_cron() {
  154. if (\Drupal::moduleHandler()->moduleExists('block_content')) {
  155. /** @var \Drupal\layout_builder\InlineBlockEntityOperations $entity_operations */
  156. $entity_operations = \Drupal::classResolver(InlineBlockEntityOperations::class);
  157. $entity_operations->removeUnused();
  158. }
  159. }
  160. /**
  161. * Implements hook_plugin_filter_TYPE_alter().
  162. */
  163. function layout_builder_plugin_filter_block_alter(array &$definitions, array $extra, $consumer) {
  164. // @todo Determine the 'inline_block' blocks should be allowed outside
  165. // of layout_builder https://www.drupal.org/node/2979142.
  166. if ($consumer !== 'layout_builder') {
  167. foreach ($definitions as $id => $definition) {
  168. if ($definition['id'] === 'inline_block') {
  169. unset($definitions[$id]);
  170. }
  171. }
  172. }
  173. }
  174. /**
  175. * Implements hook_ENTITY_TYPE_access().
  176. */
  177. function layout_builder_block_content_access(EntityInterface $entity, $operation, AccountInterface $account) {
  178. /** @var \Drupal\block_content\BlockContentInterface $entity */
  179. if ($operation === 'view' || $entity->isReusable() || empty(\Drupal::service('inline_block.usage')->getUsage($entity->id()))) {
  180. // If the operation is 'view' or this is reusable block or if this is
  181. // non-reusable that isn't used by this module then don't alter the access.
  182. return AccessResult::neutral();
  183. }
  184. if ($account->hasPermission('configure any layout')) {
  185. return AccessResult::allowed();
  186. }
  187. return AccessResult::forbidden();
  188. }