291 lines
12 KiB
Plaintext
291 lines
12 KiB
Plaintext
<?php
|
|
|
|
/**
|
|
* @file
|
|
* Provides hook implementations for Layout Builder.
|
|
*/
|
|
|
|
use Drupal\Core\Entity\EntityInterface;
|
|
use Drupal\Core\Entity\FieldableEntityInterface;
|
|
use Drupal\Core\Form\FormStateInterface;
|
|
use Drupal\Core\Render\Element;
|
|
use Drupal\Core\Routing\RouteMatchInterface;
|
|
use Drupal\Core\Url;
|
|
use Drupal\field\FieldConfigInterface;
|
|
use Drupal\layout_builder\Entity\LayoutBuilderEntityViewDisplay;
|
|
use Drupal\layout_builder\Entity\LayoutBuilderEntityViewDisplayStorage;
|
|
use Drupal\layout_builder\Form\DefaultsEntityForm;
|
|
use Drupal\layout_builder\Form\LayoutBuilderEntityViewDisplayForm;
|
|
use Drupal\layout_builder\Form\OverridesEntityForm;
|
|
use Drupal\Core\Entity\Display\EntityViewDisplayInterface;
|
|
use Drupal\layout_builder\Plugin\Block\ExtraFieldBlock;
|
|
use Drupal\layout_builder\InlineBlockEntityOperations;
|
|
use Drupal\Core\Session\AccountInterface;
|
|
use Drupal\Core\Access\AccessResult;
|
|
use Drupal\layout_builder\Plugin\SectionStorage\OverridesSectionStorage;
|
|
|
|
/**
|
|
* Implements hook_help().
|
|
*/
|
|
function layout_builder_help($route_name, RouteMatchInterface $route_match) {
|
|
// Add help text to the Layout Builder UI.
|
|
if ($route_match->getRouteObject()->getOption('_layout_builder')) {
|
|
$output = '<p>' . t('This layout builder tool allows you to configure the layout of the main content area.') . '</p>';
|
|
if (\Drupal::currentUser()->hasPermission('administer blocks')) {
|
|
$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>';
|
|
}
|
|
else {
|
|
$output .= '<p>' . t('To manage other areas of the page, use the block administration page.') . '</p>';
|
|
}
|
|
return $output;
|
|
}
|
|
|
|
switch ($route_name) {
|
|
case 'help.page.layout_builder':
|
|
$output = '<h3>' . t('About') . '</h3>';
|
|
$output .= '<p>' . t('Layout Builder provides layout building utility.') . '</p>';
|
|
$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>';
|
|
return $output;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Implements hook_entity_type_alter().
|
|
*/
|
|
function layout_builder_entity_type_alter(array &$entity_types) {
|
|
/** @var \Drupal\Core\Entity\EntityTypeInterface[] $entity_types */
|
|
$entity_types['entity_view_display']
|
|
->setClass(LayoutBuilderEntityViewDisplay::class)
|
|
->setStorageClass(LayoutBuilderEntityViewDisplayStorage::class)
|
|
->setFormClass('layout_builder', DefaultsEntityForm::class)
|
|
->setFormClass('edit', LayoutBuilderEntityViewDisplayForm::class);
|
|
|
|
// Ensure every fieldable entity type has a layout form.
|
|
foreach ($entity_types as $entity_type) {
|
|
if ($entity_type->entityClassImplements(FieldableEntityInterface::class)) {
|
|
$entity_type->setFormClass('layout_builder', OverridesEntityForm::class);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Implements hook_form_FORM_ID_alter() for \Drupal\field_ui\Form\EntityFormDisplayEditForm.
|
|
*/
|
|
function layout_builder_form_entity_form_display_edit_form_alter(&$form, FormStateInterface $form_state) {
|
|
// Hides the Layout Builder field. It is rendered directly in
|
|
// \Drupal\layout_builder\Entity\LayoutBuilderEntityViewDisplay::buildMultiple().
|
|
unset($form['fields'][OverridesSectionStorage::FIELD_NAME]);
|
|
$key = array_search(OverridesSectionStorage::FIELD_NAME, $form['#fields']);
|
|
if ($key !== FALSE) {
|
|
unset($form['#fields'][$key]);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Implements hook_field_config_insert().
|
|
*/
|
|
function layout_builder_field_config_insert(FieldConfigInterface $field_config) {
|
|
// Clear the sample entity for this entity type and bundle.
|
|
$sample_entity_generator = \Drupal::service('layout_builder.sample_entity_generator');
|
|
$sample_entity_generator->delete($field_config->getTargetEntityTypeId(), $field_config->getTargetBundle());
|
|
\Drupal::service('plugin.manager.block')->clearCachedDefinitions();
|
|
}
|
|
|
|
/**
|
|
* Implements hook_field_config_delete().
|
|
*/
|
|
function layout_builder_field_config_delete(FieldConfigInterface $field_config) {
|
|
// Clear the sample entity for this entity type and bundle.
|
|
$sample_entity_generator = \Drupal::service('layout_builder.sample_entity_generator');
|
|
$sample_entity_generator->delete($field_config->getTargetEntityTypeId(), $field_config->getTargetBundle());
|
|
\Drupal::service('plugin.manager.block')->clearCachedDefinitions();
|
|
}
|
|
|
|
/**
|
|
* Implements hook_entity_view_alter().
|
|
*
|
|
* ExtraFieldBlock block plugins add placeholders for each extra field which is
|
|
* configured to be displayed. Those placeholders are replaced by this hook.
|
|
* Modules that implement hook_entity_extra_field_info() use their
|
|
* implementations of hook_entity_view_alter() to add the rendered output of
|
|
* the extra fields they provide, so we cannot get the rendered output of extra
|
|
* fields before this point in the view process.
|
|
* layout_builder_module_implements_alter() moves this implementation of
|
|
* hook_entity_view_alter() to the end of the list.
|
|
*
|
|
* @see \Drupal\layout_builder\Plugin\Block\ExtraFieldBlock::build()
|
|
* @see layout_builder_module_implements_alter()
|
|
*/
|
|
function layout_builder_entity_view_alter(array &$build, EntityInterface $entity, EntityViewDisplayInterface $display) {
|
|
// Only replace extra fields when Layout Builder has been used to alter the
|
|
// build. See \Drupal\layout_builder\Entity\LayoutBuilderEntityViewDisplay::buildMultiple().
|
|
if (isset($build['_layout_builder']) && !Element::isEmpty($build['_layout_builder'])) {
|
|
/** @var \Drupal\Core\Entity\EntityFieldManagerInterface $field_manager */
|
|
$field_manager = \Drupal::service('entity_field.manager');
|
|
$extra_fields = $field_manager->getExtraFields($entity->getEntityTypeId(), $entity->bundle());
|
|
if (!empty($extra_fields['display'])) {
|
|
foreach ($extra_fields['display'] as $field_name => $extra_field) {
|
|
// If the extra field is not set replace with an empty array to avoid
|
|
// the placeholder text from being rendered.
|
|
$replacement = isset($build[$field_name]) ? $build[$field_name] : [];
|
|
ExtraFieldBlock::replaceFieldPlaceholder($build, $replacement, $field_name);
|
|
// After the rendered field in $build has been copied over to the
|
|
// ExtraFieldBlock block we must remove it from its original location or
|
|
// else it will be rendered twice.
|
|
unset($build[$field_name]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Implements hook_builder_module_implements_alter().
|
|
*/
|
|
function layout_builder_module_implements_alter(&$implementations, $hook) {
|
|
if ($hook === 'entity_view_alter') {
|
|
// Ensure that this module's implementation of hook_entity_view_alter() runs
|
|
// last so that other modules that use this hook to render extra fields will
|
|
// run before it.
|
|
$group = $implementations['layout_builder'];
|
|
unset($implementations['layout_builder']);
|
|
$implementations['layout_builder'] = $group;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Implements hook_entity_presave().
|
|
*/
|
|
function layout_builder_entity_presave(EntityInterface $entity) {
|
|
if (\Drupal::moduleHandler()->moduleExists('block_content')) {
|
|
/** @var \Drupal\layout_builder\InlineBlockEntityOperations $entity_operations */
|
|
$entity_operations = \Drupal::classResolver(InlineBlockEntityOperations::class);
|
|
$entity_operations->handlePreSave($entity);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Implements hook_entity_delete().
|
|
*/
|
|
function layout_builder_entity_delete(EntityInterface $entity) {
|
|
if (\Drupal::moduleHandler()->moduleExists('block_content')) {
|
|
/** @var \Drupal\layout_builder\InlineBlockEntityOperations $entity_operations */
|
|
$entity_operations = \Drupal::classResolver(InlineBlockEntityOperations::class);
|
|
$entity_operations->handleEntityDelete($entity);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Implements hook_cron().
|
|
*/
|
|
function layout_builder_cron() {
|
|
if (\Drupal::moduleHandler()->moduleExists('block_content')) {
|
|
/** @var \Drupal\layout_builder\InlineBlockEntityOperations $entity_operations */
|
|
$entity_operations = \Drupal::classResolver(InlineBlockEntityOperations::class);
|
|
$entity_operations->removeUnused();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Implements hook_plugin_filter_TYPE__CONSUMER_alter().
|
|
*/
|
|
function layout_builder_plugin_filter_block__layout_builder_alter(array &$definitions, array $extra) {
|
|
// @todo Restore the page title block in https://www.drupal.org/node/2938129.
|
|
unset($definitions['page_title_block']);
|
|
}
|
|
|
|
/**
|
|
* Implements hook_plugin_filter_TYPE_alter().
|
|
*/
|
|
function layout_builder_plugin_filter_block_alter(array &$definitions, array $extra, $consumer) {
|
|
// @todo Determine the 'inline_block' blocks should be allowed outside
|
|
// of layout_builder https://www.drupal.org/node/2979142.
|
|
if ($consumer !== 'layout_builder' || !isset($extra['list']) || $extra['list'] !== 'inline_blocks') {
|
|
foreach ($definitions as $id => $definition) {
|
|
if ($definition['id'] === 'inline_block') {
|
|
unset($definitions[$id]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Implements hook_ENTITY_TYPE_access().
|
|
*/
|
|
function layout_builder_block_content_access(EntityInterface $entity, $operation, AccountInterface $account) {
|
|
/** @var \Drupal\block_content\BlockContentInterface $entity */
|
|
if ($operation === 'view' || $entity->isReusable() || empty(\Drupal::service('inline_block.usage')->getUsage($entity->id()))) {
|
|
// If the operation is 'view' or this is reusable block or if this is
|
|
// non-reusable that isn't used by this module then don't alter the access.
|
|
return AccessResult::neutral();
|
|
}
|
|
|
|
if ($account->hasPermission('configure any layout')) {
|
|
return AccessResult::allowed();
|
|
}
|
|
return AccessResult::forbidden();
|
|
}
|
|
|
|
/**
|
|
* Implements hook_plugin_filter_TYPE__CONSUMER_alter().
|
|
*/
|
|
function layout_builder_plugin_filter_block__block_ui_alter(array &$definitions, array $extra) {
|
|
foreach ($definitions as $id => $definition) {
|
|
// Filter out any layout_builder-provided block that has required context
|
|
// definitions.
|
|
if ($definition['provider'] === 'layout_builder' && !empty($definition['context_definitions'])) {
|
|
/** @var \Drupal\Core\Plugin\Context\ContextDefinitionInterface $context_definition */
|
|
foreach ($definition['context_definitions'] as $context_definition) {
|
|
if ($context_definition->isRequired()) {
|
|
unset($definitions[$id]);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Implements hook_layout_builder_section_storage_alter().
|
|
*/
|
|
function layout_builder_layout_builder_section_storage_alter(array &$definitions) {
|
|
// @todo Until https://www.drupal.org/node/3016420 is resolved, context
|
|
// definition annotations cannot specify any constraints. Alter
|
|
// \Drupal\layout_builder\Plugin\SectionStorage\OverridesSectionStorage to
|
|
// add the constraint of having the required layout field.
|
|
/** @var \Drupal\layout_builder\SectionStorage\SectionStorageDefinition[] $definitions */
|
|
$definitions['overrides']->getContextDefinition('entity')
|
|
->addConstraint('EntityHasField', OverridesSectionStorage::FIELD_NAME);
|
|
}
|
|
|
|
/**
|
|
* Implements hook_plugin_filter_TYPE__CONSUMER_alter().
|
|
*/
|
|
function layout_builder_plugin_filter_layout__layout_builder_alter(array &$definitions, array $extra) {
|
|
// Remove layouts provide by layout discovery that are not needed because of
|
|
// layouts provided by this module.
|
|
$duplicate_layouts = [
|
|
'layout_twocol',
|
|
'layout_twocol_bricks',
|
|
'layout_threecol_25_50_25',
|
|
'layout_threecol_33_34_33',
|
|
];
|
|
|
|
foreach ($duplicate_layouts as $duplicate_layout) {
|
|
/** @var \Drupal\Core\Layout\LayoutDefinition[] $definitions */
|
|
if (isset($definitions[$duplicate_layout])) {
|
|
if ($definitions[$duplicate_layout]->getProvider() === 'layout_discovery') {
|
|
unset($definitions[$duplicate_layout]);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Move the one column layout to the top.
|
|
if (isset($definitions['layout_onecol']) && $definitions['layout_onecol']->getProvider() === 'layout_discovery') {
|
|
$one_col = $definitions['layout_onecol'];
|
|
unset($definitions['layout_onecol']);
|
|
$definitions = [
|
|
'layout_onecol' => $one_col,
|
|
] + $definitions;
|
|
}
|
|
}
|