' . t('About') . ''; $output .= '

' . t('Layout Builder provides layout building utility.') . '

'; $output .= '

' . t('For more information, see the online documentation for the Layout Builder module.', [':layout-builder-documentation' => 'https://www.drupal.org/docs/8/core/modules/layout_builder']) . '

'; return $output; } } /** * Implements hook_entity_type_alter(). */ function layout_builder_entity_type_alter(array &$entity_types) { /** @var \Drupal\Core\Entity\EntityTypeInterface[] $entity_types */ foreach ($entity_types as $entity_type) { if ($entity_type->entityClassImplements(FieldableEntityInterface::class) && $entity_type->hasLinkTemplate('canonical') && $entity_type->hasViewBuilderClass()) { $entity_type->setLinkTemplate('layout-builder', $entity_type->getLinkTemplate('canonical') . '/layout'); } } } /** * Removes the Layout Builder field both visually and from the #fields handling. * * This prevents any interaction with this field. It is rendered directly * in layout_builder_entity_view_alter(). * * @internal */ function _layout_builder_hide_layout_field(array &$form) { unset($form['fields']['layout_builder__layout']); $key = array_search('layout_builder__layout', $form['#fields']); if ($key !== FALSE) { unset($form['#fields'][$key]); } } /** * 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) { _layout_builder_hide_layout_field($form); } /** * Implements hook_form_FORM_ID_alter() for \Drupal\field_ui\Form\EntityViewDisplayEditForm. */ function layout_builder_form_entity_view_display_edit_form_alter(&$form, FormStateInterface $form_state) { /** @var \Drupal\Core\Entity\Display\EntityViewDisplayInterface $display */ $display = $form_state->getFormObject()->getEntity(); $entity_type = \Drupal::entityTypeManager()->getDefinition($display->getTargetEntityTypeId()); _layout_builder_hide_layout_field($form); // @todo Expand to work for all view modes in // https://www.drupal.org/node/2907413. if (!in_array($display->getMode(), ['full', 'default'], TRUE)) { return; } $form['layout'] = [ '#type' => 'details', '#open' => TRUE, '#title' => t('Layout options'), '#tree' => TRUE, ]; // @todo Unchecking this box is a destructive action, this should be made // clear to the user in https://www.drupal.org/node/2914484. $form['layout']['allow_custom'] = [ '#type' => 'checkbox', '#title' => t('Allow each @entity to have its layout customized.', [ '@entity' => $entity_type->getSingularLabel(), ]), '#default_value' => $display->getThirdPartySetting('layout_builder', 'allow_custom', FALSE), ]; $form['#entity_builders'][] = 'layout_builder_form_entity_view_display_edit_entity_builder'; } /** * Entity builder for layout options on the entity view display form. * * @see layout_builder_form_entity_view_display_edit_form_alter() */ function layout_builder_form_entity_view_display_edit_entity_builder($entity_type_id, EntityViewDisplayInterface $display, &$form, FormStateInterface &$form_state) { $new_value = (bool) $form_state->getValue(['layout', 'allow_custom'], FALSE); $display->setThirdPartySetting('layout_builder', 'allow_custom', $new_value); } /** * Implements hook_ENTITY_TYPE_presave(). */ function layout_builder_entity_view_display_presave(EntityViewDisplayInterface $display) { $original_value = isset($display->original) ? $display->original->getThirdPartySetting('layout_builder', 'allow_custom', FALSE) : FALSE; $new_value = $display->getThirdPartySetting('layout_builder', 'allow_custom', FALSE); if ($original_value !== $new_value) { $entity_type_id = $display->getTargetEntityTypeId(); $bundle = $display->getTargetBundle(); if ($new_value) { layout_builder_add_layout_section_field($entity_type_id, $bundle); } elseif ($field = FieldConfig::loadByName($entity_type_id, $bundle, 'layout_builder__layout')) { $field->delete(); } } } /** * Adds a layout section field to a given bundle. * * @param string $entity_type_id * The entity type ID. * @param string $bundle * The bundle. * @param string $field_name * (optional) The name for the layout section field. Defaults to * 'layout_builder__layout'. * * @return \Drupal\field\FieldConfigInterface * A layout section field. */ function layout_builder_add_layout_section_field($entity_type_id, $bundle, $field_name = 'layout_builder__layout') { $field = FieldConfig::loadByName($entity_type_id, $bundle, $field_name); if (!$field) { $field_storage = FieldStorageConfig::loadByName($entity_type_id, $field_name); if (!$field_storage) { $field_storage = FieldStorageConfig::create([ 'entity_type' => $entity_type_id, 'field_name' => $field_name, 'type' => 'layout_section', ]); $field_storage->save(); } $field = FieldConfig::create([ 'field_storage' => $field_storage, 'bundle' => $bundle, 'label' => t('Layout'), ]); $field->save(); } return $field; } /** * Implements hook_entity_view_alter(). */ function layout_builder_entity_view_alter(array &$build, EntityInterface $entity, EntityViewDisplayInterface $display) { if ($display->getThirdPartySetting('layout_builder', 'allow_custom', FALSE) && !$entity->layout_builder__layout->isEmpty()) { $sections = $entity->layout_builder__layout->getSections(); foreach ($sections as $delta => $section) { $build['_layout_builder'][$delta] = $section->toRenderArray(); } // If field layout is active, that is all that needs to be removed. if (\Drupal::moduleHandler()->moduleExists('field_layout') && isset($build['_field_layout'])) { unset($build['_field_layout']); return; } /** @var \Drupal\Core\Field\FieldDefinitionInterface[] $field_definitions */ $field_definitions = \Drupal::service('entity_field.manager')->getFieldDefinitions($display->getTargetEntityTypeId(), $display->getTargetBundle()); // Remove all display-configurable fields. foreach (array_keys($display->getComponents()) as $name) { if ($name !== 'layout_builder__layout' && isset($field_definitions[$name]) && $field_definitions[$name]->isDisplayConfigurable('view')) { unset($build[$name]); } } } }