Issue #3004536 by tim.plunkett, tedbow, Sam152, phenaproxima, mark_fullmer: Move the Layout Builder UI into an entity form for better integration with other content authoring modules and core features
parent
1cc42e056b
commit
a9a53828a3
|
@ -0,0 +1,32 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Hooks provided by the Layout Builder module.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @addtogroup hooks
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* Allows customization of the Layout Builder UI for per-entity overrides.
|
||||
*
|
||||
* The Layout Builder widget will be added with a weight of -10 after this hook
|
||||
* is invoked.
|
||||
*
|
||||
* @see hook_entity_form_display_alter()
|
||||
* @see \Drupal\layout_builder\Form\OverridesEntityForm::init()
|
||||
*/
|
||||
function hook_layout_builder_overrides_entity_form_display_alter(\Drupal\Core\Entity\Display\EntityFormDisplayInterface $display) {
|
||||
$display->setComponent('moderation_state', [
|
||||
'type' => 'moderation_state_default',
|
||||
'weight' => 2,
|
||||
'settings' => [],
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @} End of "addtogroup hooks".
|
||||
*/
|
|
@ -6,6 +6,7 @@
|
|||
*/
|
||||
|
||||
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;
|
||||
|
@ -13,7 +14,9 @@ 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;
|
||||
|
@ -54,7 +57,15 @@ function layout_builder_entity_type_alter(array &$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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -86,3 +86,10 @@ function layout_builder_post_update_cancel_link_to_discard_changes_form() {
|
|||
function layout_builder_post_update_remove_layout_is_rebuilding() {
|
||||
// Empty post-update hook.
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear caches due to routing changes to move the Layout Builder UI to forms.
|
||||
*/
|
||||
function layout_builder_post_update_routing_entity_form() {
|
||||
// Empty post-update hook.
|
||||
}
|
||||
|
|
|
@ -2,64 +2,17 @@
|
|||
|
||||
namespace Drupal\layout_builder\Controller;
|
||||
|
||||
use Drupal\Core\Ajax\AjaxHelperTrait;
|
||||
use Drupal\Core\DependencyInjection\ContainerInjectionInterface;
|
||||
use Drupal\Core\Messenger\MessengerInterface;
|
||||
use Drupal\Core\StringTranslation\StringTranslationTrait;
|
||||
use Drupal\layout_builder\Context\LayoutBuilderContextTrait;
|
||||
use Drupal\layout_builder\LayoutTempstoreRepositoryInterface;
|
||||
use Drupal\layout_builder\OverridesSectionStorageInterface;
|
||||
use Drupal\layout_builder\SectionStorageInterface;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
use Symfony\Component\HttpFoundation\RedirectResponse;
|
||||
|
||||
/**
|
||||
* Defines a controller to provide the Layout Builder admin UI.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
class LayoutBuilderController implements ContainerInjectionInterface {
|
||||
class LayoutBuilderController {
|
||||
|
||||
use LayoutBuilderContextTrait;
|
||||
use StringTranslationTrait;
|
||||
use AjaxHelperTrait;
|
||||
|
||||
/**
|
||||
* The layout tempstore repository.
|
||||
*
|
||||
* @var \Drupal\layout_builder\LayoutTempstoreRepositoryInterface
|
||||
*/
|
||||
protected $layoutTempstoreRepository;
|
||||
|
||||
/**
|
||||
* The messenger service.
|
||||
*
|
||||
* @var \Drupal\Core\Messenger\MessengerInterface
|
||||
*/
|
||||
protected $messenger;
|
||||
|
||||
/**
|
||||
* LayoutBuilderController constructor.
|
||||
*
|
||||
* @param \Drupal\layout_builder\LayoutTempstoreRepositoryInterface $layout_tempstore_repository
|
||||
* The layout tempstore repository.
|
||||
* @param \Drupal\Core\Messenger\MessengerInterface $messenger
|
||||
* The messenger service.
|
||||
*/
|
||||
public function __construct(LayoutTempstoreRepositoryInterface $layout_tempstore_repository, MessengerInterface $messenger) {
|
||||
$this->layoutTempstoreRepository = $layout_tempstore_repository;
|
||||
$this->messenger = $messenger;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function create(ContainerInterface $container) {
|
||||
return new static(
|
||||
$container->get('layout_builder.tempstore_repository'),
|
||||
$container->get('messenger')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides a title callback.
|
||||
|
@ -90,27 +43,4 @@ class LayoutBuilderController implements ContainerInjectionInterface {
|
|||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves the layout.
|
||||
*
|
||||
* @param \Drupal\layout_builder\SectionStorageInterface $section_storage
|
||||
* The section storage.
|
||||
*
|
||||
* @return \Symfony\Component\HttpFoundation\RedirectResponse
|
||||
* A redirect response.
|
||||
*/
|
||||
public function saveLayout(SectionStorageInterface $section_storage) {
|
||||
$section_storage->save();
|
||||
$this->layoutTempstoreRepository->delete($section_storage);
|
||||
|
||||
if ($section_storage instanceof OverridesSectionStorageInterface) {
|
||||
$this->messenger->addMessage($this->t('The layout override has been saved.'));
|
||||
}
|
||||
else {
|
||||
$this->messenger->addMessage($this->t('The layout has been saved.'));
|
||||
}
|
||||
|
||||
return new RedirectResponse($section_storage->getRedirectUrl()->setAbsolute()->toString());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,130 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\layout_builder\Form;
|
||||
|
||||
use Drupal\Core\Entity\EntityForm;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\Core\Routing\RouteMatchInterface;
|
||||
use Drupal\layout_builder\LayoutTempstoreRepositoryInterface;
|
||||
use Drupal\layout_builder\SectionStorageInterface;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
/**
|
||||
* Provides a form containing the Layout Builder UI for defaults.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
class DefaultsEntityForm extends EntityForm {
|
||||
|
||||
/**
|
||||
* Layout tempstore repository.
|
||||
*
|
||||
* @var \Drupal\layout_builder\LayoutTempstoreRepositoryInterface
|
||||
*/
|
||||
protected $layoutTempstoreRepository;
|
||||
|
||||
/**
|
||||
* The section storage.
|
||||
*
|
||||
* @var \Drupal\layout_builder\SectionStorageInterface
|
||||
*/
|
||||
protected $sectionStorage;
|
||||
|
||||
/**
|
||||
* Constructs a new DefaultsEntityForm.
|
||||
*
|
||||
* @param \Drupal\layout_builder\LayoutTempstoreRepositoryInterface $layout_tempstore_repository
|
||||
* The layout tempstore repository.
|
||||
*/
|
||||
public function __construct(LayoutTempstoreRepositoryInterface $layout_tempstore_repository) {
|
||||
$this->layoutTempstoreRepository = $layout_tempstore_repository;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function create(ContainerInterface $container) {
|
||||
return new static(
|
||||
$container->get('layout_builder.tempstore_repository')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getBaseFormId() {
|
||||
return $this->getEntity()->getEntityTypeId() . '_layout_builder_form';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildForm(array $form, FormStateInterface $form_state, SectionStorageInterface $section_storage = NULL) {
|
||||
$form['layout_builder'] = [
|
||||
'#type' => 'layout_builder',
|
||||
'#section_storage' => $section_storage,
|
||||
];
|
||||
$this->sectionStorage = $section_storage;
|
||||
return parent::buildForm($form, $form_state);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildEntity(array $form, FormStateInterface $form_state) {
|
||||
// \Drupal\Core\Entity\EntityForm::buildEntity() clones the entity object.
|
||||
// Keep it in sync with the one used by the section storage.
|
||||
$this->setEntity($this->sectionStorage->getContextValue('display'));
|
||||
$entity = parent::buildEntity($form, $form_state);
|
||||
$this->sectionStorage->setContextValue('display', $entity);
|
||||
return $entity;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getEntityFromRouteMatch(RouteMatchInterface $route_match, $entity_type_id) {
|
||||
$route_parameters = $route_match->getParameters()->all();
|
||||
|
||||
return $this->entityTypeManager->getStorage('entity_view_display')->load($route_parameters['entity_type_id'] . '.' . $route_parameters['bundle'] . '.' . $route_parameters['view_mode_name']);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function actions(array $form, FormStateInterface $form_state) {
|
||||
$actions = parent::actions($form, $form_state);
|
||||
$actions['submit']['#value'] = $this->t('Save layout');
|
||||
$actions['#weight'] = -1000;
|
||||
|
||||
$actions['discard_changes'] = [
|
||||
'#type' => 'link',
|
||||
'#title' => $this->t('Discard changes'),
|
||||
'#attributes' => ['class' => ['button']],
|
||||
'#url' => $this->sectionStorage->getLayoutBuilderUrl('discard_changes'),
|
||||
];
|
||||
return $actions;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function save(array $form, FormStateInterface $form_state) {
|
||||
$return = $this->sectionStorage->save();
|
||||
$this->layoutTempstoreRepository->delete($this->sectionStorage);
|
||||
$this->messenger()->addMessage($this->t('The layout has been saved.'));
|
||||
$form_state->setRedirectUrl($this->sectionStorage->getRedirectUrl());
|
||||
return $return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the section storage object.
|
||||
*
|
||||
* @return \Drupal\layout_builder\SectionStorageInterface
|
||||
* The section storage for the current form.
|
||||
*/
|
||||
public function getSectionStorage() {
|
||||
return $this->sectionStorage;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,173 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\layout_builder\Form;
|
||||
|
||||
use Drupal\Component\Datetime\TimeInterface;
|
||||
use Drupal\Core\Entity\ContentEntityForm;
|
||||
use Drupal\Core\Entity\Entity\EntityFormDisplay;
|
||||
use Drupal\Core\Entity\EntityRepositoryInterface;
|
||||
use Drupal\Core\Entity\EntityTypeBundleInfoInterface;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\layout_builder\LayoutTempstoreRepositoryInterface;
|
||||
use Drupal\layout_builder\Plugin\SectionStorage\OverridesSectionStorage;
|
||||
use Drupal\layout_builder\SectionStorageInterface;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
/**
|
||||
* Provides a form containing the Layout Builder UI for overrides.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
class OverridesEntityForm extends ContentEntityForm {
|
||||
|
||||
/**
|
||||
* Layout tempstore repository.
|
||||
*
|
||||
* @var \Drupal\layout_builder\LayoutTempstoreRepositoryInterface
|
||||
*/
|
||||
protected $layoutTempstoreRepository;
|
||||
|
||||
/**
|
||||
* The section storage.
|
||||
*
|
||||
* @var \Drupal\layout_builder\SectionStorageInterface
|
||||
*/
|
||||
protected $sectionStorage;
|
||||
|
||||
/**
|
||||
* Constructs a new OverridesEntityForm.
|
||||
*
|
||||
* @param \Drupal\Core\Entity\EntityRepositoryInterface $entity_repository
|
||||
* The entity repository service.
|
||||
* @param \Drupal\Core\Entity\EntityTypeBundleInfoInterface $entity_type_bundle_info
|
||||
* The entity type bundle service.
|
||||
* @param \Drupal\Component\Datetime\TimeInterface $time
|
||||
* The time service.
|
||||
* @param \Drupal\layout_builder\LayoutTempstoreRepositoryInterface $layout_tempstore_repository
|
||||
* The layout tempstore repository.
|
||||
*/
|
||||
public function __construct(EntityRepositoryInterface $entity_repository, EntityTypeBundleInfoInterface $entity_type_bundle_info, TimeInterface $time, LayoutTempstoreRepositoryInterface $layout_tempstore_repository) {
|
||||
parent::__construct($entity_repository, $entity_type_bundle_info, $time);
|
||||
$this->layoutTempstoreRepository = $layout_tempstore_repository;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function create(ContainerInterface $container) {
|
||||
return new static(
|
||||
$container->get('entity.repository'),
|
||||
$container->get('entity_type.bundle.info'),
|
||||
$container->get('datetime.time'),
|
||||
$container->get('layout_builder.tempstore_repository')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getBaseFormId() {
|
||||
return $this->getEntity()->getEntityTypeId() . '_layout_builder_form';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function init(FormStateInterface $form_state) {
|
||||
parent::init($form_state);
|
||||
|
||||
// Create a transient display that is not persisted, but used only for
|
||||
// building the components required for the layout form.
|
||||
$display = EntityFormDisplay::create([
|
||||
'targetEntityType' => $this->getEntity()->getEntityTypeId(),
|
||||
'bundle' => $this->getEntity()->bundle(),
|
||||
]);
|
||||
|
||||
// Allow modules to choose if they are relevant to the layout form.
|
||||
$this->moduleHandler->alter('layout_builder_overrides_entity_form_display', $display);
|
||||
|
||||
// Add the widget for Layout Builder after the alter.
|
||||
$display->setComponent(OverridesSectionStorage::FIELD_NAME, [
|
||||
'type' => 'layout_builder_widget',
|
||||
'weight' => -10,
|
||||
'settings' => [],
|
||||
]);
|
||||
|
||||
$this->setFormDisplay($display, $form_state);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildForm(array $form, FormStateInterface $form_state, SectionStorageInterface $section_storage = NULL) {
|
||||
$this->sectionStorage = $section_storage;
|
||||
$form = parent::buildForm($form, $form_state);
|
||||
|
||||
// @todo \Drupal\layout_builder\Field\LayoutSectionItemList::defaultAccess()
|
||||
// restricts all access to the field, explicitly allow access here until
|
||||
// https://www.drupal.org/node/2942975 is resolved.
|
||||
$form[OverridesSectionStorage::FIELD_NAME]['#access'] = TRUE;
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildEntity(array $form, FormStateInterface $form_state) {
|
||||
// \Drupal\Core\Entity\EntityForm::buildEntity() clones the entity object.
|
||||
// Keep it in sync with the one used by the section storage.
|
||||
$this->setEntity($this->sectionStorage->getContextValue('entity'));
|
||||
$entity = parent::buildEntity($form, $form_state);
|
||||
$this->sectionStorage->setContextValue('entity', $entity);
|
||||
return $entity;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function save(array $form, FormStateInterface $form_state) {
|
||||
$return = parent::save($form, $form_state);
|
||||
|
||||
$this->layoutTempstoreRepository->delete($this->sectionStorage);
|
||||
$this->messenger()->addStatus($this->t('The layout override has been saved.'));
|
||||
$form_state->setRedirectUrl($this->sectionStorage->getRedirectUrl());
|
||||
return $return;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function actions(array $form, FormStateInterface $form_state) {
|
||||
$actions = parent::actions($form, $form_state);
|
||||
$actions['submit']['#value'] = $this->t('Save layout');
|
||||
$actions['delete']['#access'] = FALSE;
|
||||
$actions['#weight'] = -1000;
|
||||
|
||||
$actions['discard_changes'] = [
|
||||
'#type' => 'link',
|
||||
'#title' => $this->t('Discard changes'),
|
||||
'#attributes' => ['class' => ['button']],
|
||||
'#url' => $this->sectionStorage->getLayoutBuilderUrl('discard_changes'),
|
||||
];
|
||||
// @todo This link should be conditionally displayed, see
|
||||
// https://www.drupal.org/node/2917777.
|
||||
$actions['revert'] = [
|
||||
'#type' => 'link',
|
||||
'#title' => $this->t('Revert to defaults'),
|
||||
'#attributes' => ['class' => ['button']],
|
||||
'#url' => $this->sectionStorage->getLayoutBuilderUrl('revert'),
|
||||
];
|
||||
return $actions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the section storage object.
|
||||
*
|
||||
* @return \Drupal\layout_builder\SectionStorageInterface
|
||||
* The section storage for the current form.
|
||||
*/
|
||||
public function getSectionStorage() {
|
||||
return $this->sectionStorage;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,66 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\layout_builder\Plugin\Field\FieldWidget;
|
||||
|
||||
use Drupal\Core\Field\FieldItemListInterface;
|
||||
use Drupal\Core\Field\WidgetBase;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
|
||||
/**
|
||||
* A widget to display the layout form.
|
||||
*
|
||||
* @FieldWidget(
|
||||
* id = "layout_builder_widget",
|
||||
* label = @Translation("Layout Builder Widget"),
|
||||
* description = @Translation("A field widget for Layout Builder."),
|
||||
* field_types = {
|
||||
* "layout_section",
|
||||
* },
|
||||
* multiple_values = TRUE,
|
||||
* )
|
||||
*
|
||||
* @internal
|
||||
* Layout Builder is currently experimental and should only be leveraged by
|
||||
* experimental modules and development releases of contributed modules.
|
||||
* See https://www.drupal.org/core/experimental for more information.
|
||||
*/
|
||||
class LayoutBuilderWidget extends WidgetBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function formElement(FieldItemListInterface $items, $delta, array $element, array &$form, FormStateInterface $form_state) {
|
||||
$element += [
|
||||
'#type' => 'layout_builder',
|
||||
'#section_storage' => $this->getSectionStorage($form_state),
|
||||
];
|
||||
return $element;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function extractFormValues(FieldItemListInterface $items, array $form, FormStateInterface $form_state) {
|
||||
// @todo This isn't resilient to being set twice, during validation and
|
||||
// save https://www.drupal.org/project/drupal/issues/2833682.
|
||||
if (!$form_state->isValidationComplete()) {
|
||||
return;
|
||||
}
|
||||
|
||||
$items->setValue($this->getSectionStorage($form_state)->getSections());
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the section storage.
|
||||
*
|
||||
* @param \Drupal\Core\Form\FormStateInterface $form_state
|
||||
* The form state.
|
||||
*
|
||||
* @return \Drupal\layout_builder\SectionStorageInterface
|
||||
* The section storage loaded from the tempstore.
|
||||
*/
|
||||
private function getSectionStorage(FormStateInterface $form_state) {
|
||||
return $form_state->getFormObject()->getSectionStorage();
|
||||
}
|
||||
|
||||
}
|
|
@ -41,7 +41,7 @@ use Symfony\Component\Routing\RouteCollection;
|
|||
* experimental modules and development releases of contributed modules.
|
||||
* See https://www.drupal.org/core/experimental for more information.
|
||||
*/
|
||||
class DefaultsSectionStorage extends SectionStorageBase implements ContainerFactoryPluginInterface, DefaultsSectionStorageInterface, SectionStorageLocalTaskProviderInterface {
|
||||
class DefaultsSectionStorage extends SectionStorageBase implements ContainerFactoryPluginInterface, DefaultsSectionStorageInterface {
|
||||
|
||||
/**
|
||||
* The entity type manager.
|
||||
|
@ -172,7 +172,14 @@ class DefaultsSectionStorage extends SectionStorageBase implements ContainerFact
|
|||
$options = $entity_route->getOptions();
|
||||
$options['_admin_route'] = FALSE;
|
||||
|
||||
$this->buildLayoutRoutes($collection, $this->getPluginDefinition(), $path, $defaults, $requirements, $options, $entity_type_id);
|
||||
$this->buildLayoutRoutes($collection, $this->getPluginDefinition(), $path, $defaults, $requirements, $options, $entity_type_id, 'entity_view_display');
|
||||
|
||||
// Set field_ui.route_enhancer to run on the manage layout form.
|
||||
if (isset($defaults['bundle_key'])) {
|
||||
$collection->get("layout_builder.defaults.$entity_type_id.view")
|
||||
->setOption('_field_ui', TRUE)
|
||||
->setDefault('bundle', '');
|
||||
}
|
||||
|
||||
$route_names = [
|
||||
"entity.entity_view_display.{$entity_type_id}.default",
|
||||
|
@ -194,32 +201,6 @@ class DefaultsSectionStorage extends SectionStorageBase implements ContainerFact
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildLocalTasks($base_plugin_definition) {
|
||||
$local_tasks = [];
|
||||
foreach ($this->getEntityTypes() as $entity_type_id => $entity_type) {
|
||||
$local_tasks["layout_builder.defaults.$entity_type_id.view"] = $base_plugin_definition + [
|
||||
'route_name' => "layout_builder.defaults.$entity_type_id.view",
|
||||
'title' => $this->t('Manage layout'),
|
||||
'base_route' => "layout_builder.defaults.$entity_type_id.view",
|
||||
];
|
||||
$local_tasks["layout_builder.defaults.$entity_type_id.save"] = $base_plugin_definition + [
|
||||
'route_name' => "layout_builder.defaults.$entity_type_id.save",
|
||||
'title' => $this->t('Save Layout'),
|
||||
'parent_id' => "layout_builder_ui:layout_builder.defaults.$entity_type_id.view",
|
||||
];
|
||||
$local_tasks["layout_builder.defaults.$entity_type_id.discard_changes"] = $base_plugin_definition + [
|
||||
'route_name' => "layout_builder.defaults.$entity_type_id.discard_changes",
|
||||
'title' => $this->t('Discard changes'),
|
||||
'weight' => 5,
|
||||
'parent_id' => "layout_builder_ui:layout_builder.defaults.$entity_type_id.view",
|
||||
];
|
||||
}
|
||||
return $local_tasks;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of relevant entity types.
|
||||
*
|
||||
|
@ -228,7 +209,7 @@ class DefaultsSectionStorage extends SectionStorageBase implements ContainerFact
|
|||
*/
|
||||
protected function getEntityTypes() {
|
||||
return array_filter($this->entityTypeManager->getDefinitions(), function (EntityTypeInterface $entity_type) {
|
||||
return $entity_type->entityClassImplements(FieldableEntityInterface::class) && $entity_type->hasViewBuilderClass() && $entity_type->get('field_ui_base_route');
|
||||
return $entity_type->entityClassImplements(FieldableEntityInterface::class) && $entity_type->hasHandlerClass('form', 'layout_builder') && $entity_type->hasViewBuilderClass() && $entity_type->get('field_ui_base_route');
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -220,7 +220,7 @@ class OverridesSectionStorage extends SectionStorageBase implements ContainerFac
|
|||
$options['parameters'][$entity_type_id]['type'] = 'entity:' . $entity_type_id;
|
||||
|
||||
$template = $entity_type->getLinkTemplate('canonical') . '/layout';
|
||||
$this->buildLayoutRoutes($collection, $this->getPluginDefinition(), $template, $defaults, $requirements, $options, $entity_type_id);
|
||||
$this->buildLayoutRoutes($collection, $this->getPluginDefinition(), $template, $defaults, $requirements, $options, $entity_type_id, $entity_type_id);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -237,28 +237,6 @@ class OverridesSectionStorage extends SectionStorageBase implements ContainerFac
|
|||
'base_route' => "entity.$entity_type_id.canonical",
|
||||
'cache_contexts' => ['layout_builder_is_active:' . $entity_type_id],
|
||||
];
|
||||
$local_tasks["layout_builder.overrides.$entity_type_id.save"] = $base_plugin_definition + [
|
||||
'route_name' => "layout_builder.overrides.$entity_type_id.save",
|
||||
'title' => $this->t('Save Layout'),
|
||||
'parent_id' => "layout_builder_ui:layout_builder.overrides.$entity_type_id.view",
|
||||
'cache_contexts' => ['layout_builder_is_active:' . $entity_type_id],
|
||||
];
|
||||
$local_tasks["layout_builder.overrides.$entity_type_id.discard_changes"] = $base_plugin_definition + [
|
||||
'route_name' => "layout_builder.overrides.$entity_type_id.discard_changes",
|
||||
'title' => $this->t('Discard changes'),
|
||||
'parent_id' => "layout_builder_ui:layout_builder.overrides.$entity_type_id.view",
|
||||
'weight' => 5,
|
||||
'cache_contexts' => ['layout_builder_is_active:' . $entity_type_id],
|
||||
];
|
||||
// @todo This link should be conditionally displayed, see
|
||||
// https://www.drupal.org/node/2917777.
|
||||
$local_tasks["layout_builder.overrides.$entity_type_id.revert"] = $base_plugin_definition + [
|
||||
'route_name' => "layout_builder.overrides.$entity_type_id.revert",
|
||||
'title' => $this->t('Revert to defaults'),
|
||||
'parent_id' => "layout_builder_ui:layout_builder.overrides.$entity_type_id.view",
|
||||
'weight' => 10,
|
||||
'cache_contexts' => ['layout_builder_is_active:' . $entity_type_id],
|
||||
];
|
||||
}
|
||||
return $local_tasks;
|
||||
}
|
||||
|
@ -285,7 +263,7 @@ class OverridesSectionStorage extends SectionStorageBase implements ContainerFac
|
|||
*/
|
||||
protected function getEntityTypes() {
|
||||
return array_filter($this->entityTypeManager->getDefinitions(), function (EntityTypeInterface $entity_type) {
|
||||
return $entity_type->entityClassImplements(FieldableEntityInterface::class) && $entity_type->hasViewBuilderClass() && $entity_type->hasLinkTemplate('canonical');
|
||||
return $entity_type->entityClassImplements(FieldableEntityInterface::class) && $entity_type->hasHandlerClass('form', 'layout_builder') && $entity_type->hasViewBuilderClass() && $entity_type->hasLinkTemplate('canonical');
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -36,8 +36,10 @@ trait LayoutBuilderRoutesTrait {
|
|||
* (optional) An array of options.
|
||||
* @param string $route_name_prefix
|
||||
* (optional) The prefix to use for the route name.
|
||||
* @param string $entity_type_id
|
||||
* (optional) The entity type ID, if available.
|
||||
*/
|
||||
protected function buildLayoutRoutes(RouteCollection $collection, SectionStorageDefinition $definition, $path, array $defaults = [], array $requirements = [], array $options = [], $route_name_prefix = '') {
|
||||
protected function buildLayoutRoutes(RouteCollection $collection, SectionStorageDefinition $definition, $path, array $defaults = [], array $requirements = [], array $options = [], $route_name_prefix = '', $entity_type_id = '') {
|
||||
$type = $definition->id();
|
||||
$defaults['section_storage_type'] = $type;
|
||||
// Provide an empty value to allow the section storage to be upcast.
|
||||
|
@ -60,22 +62,20 @@ trait LayoutBuilderRoutesTrait {
|
|||
}
|
||||
|
||||
$main_defaults = $defaults;
|
||||
$main_defaults['_controller'] = '\Drupal\layout_builder\Controller\LayoutBuilderController::layout';
|
||||
$main_options = $options;
|
||||
if ($entity_type_id) {
|
||||
$main_defaults['_entity_form'] = "$entity_type_id.layout_builder";
|
||||
}
|
||||
else {
|
||||
$main_defaults['_controller'] = '\Drupal\layout_builder\Controller\LayoutBuilderController::layout';
|
||||
}
|
||||
$main_defaults['_title_callback'] = '\Drupal\layout_builder\Controller\LayoutBuilderController::title';
|
||||
$route = (new Route($path))
|
||||
->setDefaults($main_defaults)
|
||||
->setRequirements($requirements)
|
||||
->setOptions($options);
|
||||
->setOptions($main_options);
|
||||
$collection->add("$route_name_prefix.view", $route);
|
||||
|
||||
$save_defaults = $defaults;
|
||||
$save_defaults['_controller'] = '\Drupal\layout_builder\Controller\LayoutBuilderController::saveLayout';
|
||||
$route = (new Route("$path/save"))
|
||||
->setDefaults($save_defaults)
|
||||
->setRequirements($requirements)
|
||||
->setOptions($options);
|
||||
$collection->add("$route_name_prefix.save", $route);
|
||||
|
||||
$discard_changes_defaults = $defaults;
|
||||
$discard_changes_defaults['_form'] = '\Drupal\layout_builder\Form\DiscardLayoutChangesForm';
|
||||
$route = (new Route("$path/discard-changes"))
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
* Provides hook implementations for Layout Builder tests.
|
||||
*/
|
||||
|
||||
use Drupal\Core\Entity\Display\EntityFormDisplayInterface;
|
||||
use Drupal\Core\Entity\Display\EntityViewDisplayInterface;
|
||||
use Drupal\Core\Entity\EntityInterface;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
|
@ -80,3 +81,15 @@ function layout_builder_test_form_layout_builder_configure_block_alter(&$form, F
|
|||
'#title' => 'Layout Builder Component: ' . $form_object->getCurrentComponent()->getPluginId(),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_layout_builder_overrides_entity_form_display_alter().
|
||||
*/
|
||||
function layout_builder_test_layout_builder_overrides_entity_form_display_alter(EntityFormDisplayInterface $display) {
|
||||
$display->setComponent('status', [
|
||||
'type' => 'boolean_checkbox',
|
||||
'settings' => [
|
||||
'display_label' => TRUE,
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
|
|
@ -155,9 +155,9 @@ class SimpleConfigSectionStorage extends ContextAwarePluginBase implements Secti
|
|||
'title' => $this->t('Layout'),
|
||||
'base_route' => "layout_builder.$type.view",
|
||||
];
|
||||
$local_tasks["layout_builder.$type.save"] = $base_plugin_definition + [
|
||||
'route_name' => "layout_builder.$type.save",
|
||||
'title' => $this->t('Save Layout'),
|
||||
$local_tasks["layout_builder.$type.view__child"] = $base_plugin_definition + [
|
||||
'route_name' => "layout_builder.$type.view",
|
||||
'title' => $this->t('Layout'),
|
||||
'parent_id' => "layout_builder_ui:layout_builder.$type.view",
|
||||
];
|
||||
$local_tasks["layout_builder.$type.discard_changes"] = $base_plugin_definition + [
|
||||
|
|
|
@ -71,7 +71,7 @@ class LayoutBuilderSectionStorageTest extends BrowserTestBase {
|
|||
$page->fillField('settings[label]', 'Defaults block title');
|
||||
$page->checkField('settings[label_display]');
|
||||
$page->pressButton('Add Block');
|
||||
$page->clickLink('Save Layout');
|
||||
$page->pressButton('Save layout');
|
||||
|
||||
$this->drupalGet('node/1');
|
||||
$assert_session->pageTextContains('Defaults block title');
|
||||
|
|
|
@ -87,7 +87,7 @@ class LayoutBuilderTest extends BrowserTestBase {
|
|||
$page->fillField('settings[label]', 'This is an override');
|
||||
$page->checkField('settings[label_display]');
|
||||
$page->pressButton('Add Block');
|
||||
$page->clickLink('Save Layout');
|
||||
$page->pressButton('Save layout');
|
||||
$assert_session->pageTextContains('This is an override');
|
||||
|
||||
// Get the UUID of the component.
|
||||
|
@ -99,7 +99,7 @@ class LayoutBuilderTest extends BrowserTestBase {
|
|||
$page->uncheckField('settings[label_display]');
|
||||
$page->pressButton('Update');
|
||||
$assert_session->pageTextNotContains('This is an override');
|
||||
$page->clickLink('Save Layout');
|
||||
$page->pressButton('Save layout');
|
||||
$assert_session->pageTextNotContains('This is an override');
|
||||
}
|
||||
|
||||
|
@ -137,8 +137,7 @@ class LayoutBuilderTest extends BrowserTestBase {
|
|||
// The extra field is only present once.
|
||||
$assert_session->pageTextContainsOnce('Placeholder for the "Extra label" field');
|
||||
// Save the defaults.
|
||||
$assert_session->linkExists('Save Layout');
|
||||
$this->clickLink('Save Layout');
|
||||
$page->pressButton('Save layout');
|
||||
$assert_session->addressEquals("$field_ui_prefix/display/default");
|
||||
|
||||
// Load the default layouts again after saving to confirm fields are only
|
||||
|
@ -165,8 +164,7 @@ class LayoutBuilderTest extends BrowserTestBase {
|
|||
$assert_session->addressEquals("$field_ui_prefix/display-layout/default");
|
||||
|
||||
// Save the defaults.
|
||||
$assert_session->linkExists('Save Layout');
|
||||
$this->clickLink('Save Layout');
|
||||
$page->pressButton('Save layout');
|
||||
$assert_session->pageTextContains('The layout has been saved.');
|
||||
$assert_session->addressEquals("$field_ui_prefix/display/default");
|
||||
|
||||
|
@ -197,8 +195,7 @@ class LayoutBuilderTest extends BrowserTestBase {
|
|||
$this->clickLink('Two column');
|
||||
$assert_session->buttonExists('Add section');
|
||||
$page->pressButton('Add section');
|
||||
$assert_session->linkExists('Save Layout');
|
||||
$this->clickLink('Save Layout');
|
||||
$page->pressButton('Save');
|
||||
$assert_session->pageTextNotContains('The first node body');
|
||||
$assert_session->pageTextNotContains('Powered by Drupal');
|
||||
$assert_session->pageTextNotContains('Extra, Extra read all about it.');
|
||||
|
@ -218,7 +215,7 @@ class LayoutBuilderTest extends BrowserTestBase {
|
|||
$page->pressButton('Add Block');
|
||||
// The title field is present.
|
||||
$assert_session->elementExists('css', '.field--name-title');
|
||||
$this->clickLink('Save Layout');
|
||||
$page->pressButton('Save layout');
|
||||
|
||||
// View the other node, which is still using the defaults.
|
||||
$this->drupalGet('node/2');
|
||||
|
@ -380,8 +377,7 @@ class LayoutBuilderTest extends BrowserTestBase {
|
|||
$this->clickLink('Layout plugin (with dependencies)');
|
||||
$assert_session->elementExists('css', '.layout--layout-test-dependencies-plugin');
|
||||
$assert_session->elementExists('css', '.field--name-body');
|
||||
$assert_session->linkExists('Save Layout');
|
||||
$this->clickLink('Save Layout');
|
||||
$page->pressButton('Save layout');
|
||||
$this->drupalPostForm('admin/structure/menu/manage/myothermenu/delete', [], 'Delete');
|
||||
$this->drupalGet('admin/structure/types/manage/bundle_with_section_field/display-layout/default');
|
||||
$assert_session->elementNotExists('css', '.layout--layout-test-dependencies-plugin');
|
||||
|
@ -405,8 +401,7 @@ class LayoutBuilderTest extends BrowserTestBase {
|
|||
$assert_session->pageTextContains('Powered by Drupal');
|
||||
$assert_session->pageTextContains('My Menu');
|
||||
$assert_session->elementExists('css', '.block.menu--mymenu');
|
||||
$assert_session->linkExists('Save Layout');
|
||||
$this->clickLink('Save Layout');
|
||||
$page->pressButton('Save layout');
|
||||
|
||||
// Delete the menu.
|
||||
$this->drupalPostForm('admin/structure/menu/manage/mymenu/delete', [], 'Delete');
|
||||
|
@ -446,7 +441,7 @@ class LayoutBuilderTest extends BrowserTestBase {
|
|||
$page->checkField('settings[label_display]');
|
||||
$page->pressButton('Add Block');
|
||||
$assert_session->pageTextContains('This is the default view mode');
|
||||
$this->clickLink('Save Layout');
|
||||
$page->pressButton('Save layout');
|
||||
|
||||
// The default view mode is used for both the node display and layout UI.
|
||||
$this->drupalGet('node/1');
|
||||
|
@ -466,7 +461,7 @@ class LayoutBuilderTest extends BrowserTestBase {
|
|||
$page->checkField('settings[label_display]');
|
||||
$page->pressButton('Add Block');
|
||||
$assert_session->pageTextContains('This is the full view mode');
|
||||
$this->clickLink('Save Layout');
|
||||
$page->pressButton('Save layout');
|
||||
|
||||
// The full view mode is now used for both the node display and layout UI.
|
||||
$this->drupalGet('node/1');
|
||||
|
@ -579,7 +574,7 @@ class LayoutBuilderTest extends BrowserTestBase {
|
|||
|
||||
$assert_session->pageTextContains('Test Block View');
|
||||
$assert_session->elementExists('css', '.block-views-blocktest-block-view-block-1');
|
||||
$this->clickLink('Save Layout');
|
||||
$page->pressButton('Save');
|
||||
$assert_session->pageTextContains('Test Block View');
|
||||
$assert_session->elementExists('css', '.block-views-blocktest-block-view-block-1');
|
||||
|
||||
|
@ -679,6 +674,39 @@ class LayoutBuilderTest extends BrowserTestBase {
|
|||
$assert_session->pageTextContains($block_content);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests a custom alter of the overrides form.
|
||||
*/
|
||||
public function testOverridesFormAlter() {
|
||||
$assert_session = $this->assertSession();
|
||||
$page = $this->getSession()->getPage();
|
||||
|
||||
$this->drupalLogin($this->drupalCreateUser([
|
||||
'configure any layout',
|
||||
'administer node display',
|
||||
'administer nodes',
|
||||
]));
|
||||
|
||||
$field_ui_prefix = 'admin/structure/types/manage/bundle_with_section_field';
|
||||
// Enable overrides.
|
||||
$this->drupalPostForm("$field_ui_prefix/display/default", ['layout[enabled]' => TRUE], 'Save');
|
||||
$this->drupalPostForm("$field_ui_prefix/display/default", ['layout[allow_custom]' => TRUE], 'Save');
|
||||
$this->drupalGet('node/1');
|
||||
|
||||
// The status checkbox should be checked by default.
|
||||
$page->clickLink('Layout');
|
||||
$assert_session->checkboxChecked('status[value]');
|
||||
$page->pressButton('Save layout');
|
||||
$assert_session->pageTextContains('The layout override has been saved.');
|
||||
|
||||
// Unchecking the status checkbox will unpublish the entity.
|
||||
$page->clickLink('Layout');
|
||||
$page->uncheckField('status[value]');
|
||||
$page->pressButton('Save layout');
|
||||
$assert_session->statusCodeEquals(403);
|
||||
$assert_session->pageTextContains('The layout override has been saved.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the Block UI when Layout Builder is installed.
|
||||
*/
|
||||
|
|
|
@ -63,8 +63,7 @@ class LayoutDisplayTest extends BrowserTestBase {
|
|||
$assert_session->linkExists('Powered by Drupal');
|
||||
$this->clickLink('Powered by Drupal');
|
||||
$page->pressButton('Add Block');
|
||||
$assert_session->linkExists('Save Layout');
|
||||
$this->clickLink('Save Layout');
|
||||
$page->pressButton('Save');
|
||||
$assert_session->pageTextContains('Powered by Drupal');
|
||||
|
||||
// Add a new view mode.
|
||||
|
|
|
@ -84,7 +84,7 @@ abstract class LayoutRestTestBase extends ResourceTestBase {
|
|||
$page->fillField('settings[label]', 'This is an override');
|
||||
$page->checkField('settings[label_display]');
|
||||
$page->pressButton('Add Block');
|
||||
$page->clickLink('Save Layout');
|
||||
$page->pressButton('Save layout');
|
||||
$assert_session->pageTextContains('This is an override');
|
||||
|
||||
$this->nodeStorage = $this->container->get('entity_type.manager')->getStorage('node');
|
||||
|
|
|
@ -79,13 +79,11 @@ abstract class InlineBlockTestBase extends WebDriverTestBase {
|
|||
*/
|
||||
protected function assertSaveLayout() {
|
||||
$assert_session = $this->assertSession();
|
||||
$assert_session->linkExists('Save Layout');
|
||||
// Go to the Save Layout page. Currently there are random test failures if
|
||||
// 'clickLink()' is used.
|
||||
// @todo Convert tests that extend this class to NightWatch tests in
|
||||
// https://www.drupal.org/node/2984161
|
||||
$link = $this->getSession()->getPage()->findLink('Save Layout');
|
||||
$this->drupalGet($link->getAttribute('href'));
|
||||
$page = $this->getSession()->getPage();
|
||||
|
||||
// Reload the page to prevent random failures.
|
||||
$this->drupalGet($this->getUrl());
|
||||
$page->pressButton('Save layout');
|
||||
$this->assertNotEmpty($assert_session->waitForElement('css', '.messages--status'));
|
||||
|
||||
if (stristr($this->getUrl(), 'admin/structure') === FALSE) {
|
||||
|
|
|
@ -116,8 +116,7 @@ class LayoutBuilderOptInTest extends WebDriverTestBase {
|
|||
$page->selectFieldOption('settings[formatter][type]', 'text_trimmed');
|
||||
$assert_session->assertWaitOnAjaxRequest();
|
||||
$page->pressButton('Update');
|
||||
$assert_session->linkExists('Save Layout');
|
||||
$this->clickLink('Save Layout');
|
||||
$page->pressButton('Save layout');
|
||||
|
||||
$this->drupalGet($layout_builder_ui);
|
||||
$assert_session->fieldValueEquals('settings[formatter][type]', 'text_trimmed');
|
||||
|
|
|
@ -140,8 +140,7 @@ class LayoutBuilderTest extends WebDriverTestBase {
|
|||
$assert_session->pageTextContains('Powered by Drupal');
|
||||
|
||||
// Save the layout, and the new block is visible.
|
||||
$assert_session->linkExists('Save Layout');
|
||||
$this->clickLink('Save Layout');
|
||||
$page->pressButton('Save layout');
|
||||
$assert_session->addressEquals($node_url);
|
||||
$assert_session->pageTextContains('Powered by Drupal');
|
||||
$assert_session->pageTextContains('This is the label');
|
||||
|
@ -177,8 +176,7 @@ class LayoutBuilderTest extends WebDriverTestBase {
|
|||
$assert_session->elementTextContains('css', '.layout__region--second', 'Powered by Drupal');
|
||||
|
||||
// Ensure the dragged block is still in the correct position after save.
|
||||
$assert_session->linkExists('Save Layout');
|
||||
$this->clickLink('Save Layout');
|
||||
$page->pressButton('Save layout');
|
||||
$assert_session->elementExists('css', '.layout__region--second .block-system-powered-by-block');
|
||||
$assert_session->elementTextContains('css', '.layout__region--second', 'Powered by Drupal');
|
||||
|
||||
|
@ -212,8 +210,7 @@ class LayoutBuilderTest extends WebDriverTestBase {
|
|||
$assert_session->addressEquals($layout_url);
|
||||
$this->assertPageNotReloaded();
|
||||
|
||||
$assert_session->linkExists('Save Layout');
|
||||
$this->clickLink('Save Layout');
|
||||
$page->pressButton('Save layout');
|
||||
$assert_session->elementExists('css', '.layout');
|
||||
|
||||
// Test deriver-based blocks.
|
||||
|
@ -242,8 +239,7 @@ class LayoutBuilderTest extends WebDriverTestBase {
|
|||
$assert_session->linkNotExists('Add Block');
|
||||
$this->assertPageNotReloaded();
|
||||
|
||||
$assert_session->linkExists('Save Layout');
|
||||
$this->clickLink('Save Layout');
|
||||
$page->pressButton('Save layout');
|
||||
|
||||
// Removing all sections results in the default layout display being used.
|
||||
$assert_session->addressEquals($node_url);
|
||||
|
|
|
@ -89,7 +89,7 @@ class LayoutBuilderUiTest extends WebDriverTestBase {
|
|||
|
||||
// Make and then save changes.
|
||||
$this->assertModifiedLayout(static::FIELD_UI_PREFIX . '/display-layout/default');
|
||||
$page->clickLink('Save Layout');
|
||||
$page->pressButton('Save layout');
|
||||
$assert_session->pageTextNotContains('You have unsaved changes.');
|
||||
}
|
||||
|
||||
|
|
|
@ -330,6 +330,7 @@ class DefaultsSectionStorageTest extends UnitTestCase {
|
|||
/**
|
||||
* @covers ::buildRoutes
|
||||
* @covers ::getEntityTypes
|
||||
* @covers \Drupal\layout_builder\Routing\LayoutBuilderRoutesTrait::buildLayoutRoutes
|
||||
*/
|
||||
public function testBuildRoutes() {
|
||||
$entity_types = [];
|
||||
|
@ -338,25 +339,34 @@ class DefaultsSectionStorageTest extends UnitTestCase {
|
|||
$not_fieldable->entityClassImplements(FieldableEntityInterface::class)->willReturn(FALSE);
|
||||
$entity_types['not_fieldable'] = $not_fieldable->reveal();
|
||||
|
||||
$no_layout_builder_form = $this->prophesize(EntityTypeInterface::class);
|
||||
$no_layout_builder_form->entityClassImplements(FieldableEntityInterface::class)->willReturn(TRUE);
|
||||
$no_layout_builder_form->hasHandlerClass('form', 'layout_builder')->willReturn(FALSE);
|
||||
$entity_types['no_layout_builder_form'] = $no_layout_builder_form->reveal();
|
||||
|
||||
$no_view_builder = $this->prophesize(EntityTypeInterface::class);
|
||||
$no_view_builder->entityClassImplements(FieldableEntityInterface::class)->willReturn(TRUE);
|
||||
$no_view_builder->hasHandlerClass('form', 'layout_builder')->willReturn(TRUE);
|
||||
$no_view_builder->hasViewBuilderClass()->willReturn(FALSE);
|
||||
$entity_types['no_view_builder'] = $no_view_builder->reveal();
|
||||
|
||||
$no_field_ui_route = $this->prophesize(EntityTypeInterface::class);
|
||||
$no_field_ui_route->entityClassImplements(FieldableEntityInterface::class)->willReturn(TRUE);
|
||||
$no_field_ui_route->hasHandlerClass('form', 'layout_builder')->willReturn(TRUE);
|
||||
$no_field_ui_route->hasViewBuilderClass()->willReturn(TRUE);
|
||||
$no_field_ui_route->get('field_ui_base_route')->willReturn(NULL);
|
||||
$entity_types['no_field_ui_route'] = $no_field_ui_route->reveal();
|
||||
|
||||
$unknown_field_ui_route = $this->prophesize(EntityTypeInterface::class);
|
||||
$unknown_field_ui_route->entityClassImplements(FieldableEntityInterface::class)->willReturn(TRUE);
|
||||
$unknown_field_ui_route->hasHandlerClass('form', 'layout_builder')->willReturn(TRUE);
|
||||
$unknown_field_ui_route->hasViewBuilderClass()->willReturn(TRUE);
|
||||
$unknown_field_ui_route->get('field_ui_base_route')->willReturn('unknown');
|
||||
$entity_types['unknown_field_ui_route'] = $unknown_field_ui_route->reveal();
|
||||
|
||||
$with_bundle_key = $this->prophesize(EntityTypeInterface::class);
|
||||
$with_bundle_key->entityClassImplements(FieldableEntityInterface::class)->willReturn(TRUE);
|
||||
$with_bundle_key->hasHandlerClass('form', 'layout_builder')->willReturn(TRUE);
|
||||
$with_bundle_key->hasViewBuilderClass()->willReturn(TRUE);
|
||||
$with_bundle_key->get('field_ui_base_route')->willReturn('known');
|
||||
$with_bundle_key->hasKey('bundle')->willReturn(TRUE);
|
||||
|
@ -365,6 +375,7 @@ class DefaultsSectionStorageTest extends UnitTestCase {
|
|||
|
||||
$with_bundle_parameter = $this->prophesize(EntityTypeInterface::class);
|
||||
$with_bundle_parameter->entityClassImplements(FieldableEntityInterface::class)->willReturn(TRUE);
|
||||
$with_bundle_parameter->hasHandlerClass('form', 'layout_builder')->willReturn(TRUE);
|
||||
$with_bundle_parameter->hasViewBuilderClass()->willReturn(TRUE);
|
||||
$with_bundle_parameter->get('field_ui_base_route')->willReturn('with_bundle');
|
||||
$entity_types['with_bundle_parameter'] = $with_bundle_parameter->reveal();
|
||||
|
@ -378,9 +389,10 @@ class DefaultsSectionStorageTest extends UnitTestCase {
|
|||
[
|
||||
'entity_type_id' => 'with_bundle_key',
|
||||
'bundle_key' => 'my_bundle_type',
|
||||
'bundle' => '',
|
||||
'section_storage_type' => 'defaults',
|
||||
'section_storage' => '',
|
||||
'_controller' => '\Drupal\layout_builder\Controller\LayoutBuilderController::layout',
|
||||
'_entity_form' => 'entity_view_display.layout_builder',
|
||||
'_title_callback' => '\Drupal\layout_builder\Controller\LayoutBuilderController::title',
|
||||
],
|
||||
[
|
||||
|
@ -394,28 +406,7 @@ class DefaultsSectionStorageTest extends UnitTestCase {
|
|||
],
|
||||
'_layout_builder' => TRUE,
|
||||
'_admin_route' => FALSE,
|
||||
]
|
||||
),
|
||||
'layout_builder.defaults.with_bundle_key.save' => new Route(
|
||||
'/admin/entity/whatever/display-layout/{view_mode_name}/save',
|
||||
[
|
||||
'entity_type_id' => 'with_bundle_key',
|
||||
'bundle_key' => 'my_bundle_type',
|
||||
'section_storage_type' => 'defaults',
|
||||
'section_storage' => '',
|
||||
'_controller' => '\Drupal\layout_builder\Controller\LayoutBuilderController::saveLayout',
|
||||
],
|
||||
[
|
||||
'_field_ui_view_mode_access' => 'administer with_bundle_key display',
|
||||
'_has_layout_section' => 'true',
|
||||
'_layout_builder_access' => 'view',
|
||||
],
|
||||
[
|
||||
'parameters' => [
|
||||
'section_storage' => ['layout_builder_tempstore' => TRUE],
|
||||
],
|
||||
'_layout_builder' => TRUE,
|
||||
'_admin_route' => FALSE,
|
||||
'_field_ui' => TRUE,
|
||||
]
|
||||
),
|
||||
'layout_builder.defaults.with_bundle_key.discard_changes' => new Route(
|
||||
|
@ -466,7 +457,7 @@ class DefaultsSectionStorageTest extends UnitTestCase {
|
|||
'entity_type_id' => 'with_bundle_parameter',
|
||||
'section_storage_type' => 'defaults',
|
||||
'section_storage' => '',
|
||||
'_controller' => '\Drupal\layout_builder\Controller\LayoutBuilderController::layout',
|
||||
'_entity_form' => 'entity_view_display.layout_builder',
|
||||
'_title_callback' => '\Drupal\layout_builder\Controller\LayoutBuilderController::title',
|
||||
],
|
||||
[
|
||||
|
@ -482,27 +473,6 @@ class DefaultsSectionStorageTest extends UnitTestCase {
|
|||
'_admin_route' => FALSE,
|
||||
]
|
||||
),
|
||||
'layout_builder.defaults.with_bundle_parameter.save' => new Route(
|
||||
'/admin/entity/{bundle}/display-layout/{view_mode_name}/save',
|
||||
[
|
||||
'entity_type_id' => 'with_bundle_parameter',
|
||||
'section_storage_type' => 'defaults',
|
||||
'section_storage' => '',
|
||||
'_controller' => '\Drupal\layout_builder\Controller\LayoutBuilderController::saveLayout',
|
||||
],
|
||||
[
|
||||
'_field_ui_view_mode_access' => 'administer with_bundle_parameter display',
|
||||
'_has_layout_section' => 'true',
|
||||
'_layout_builder_access' => 'view',
|
||||
],
|
||||
[
|
||||
'parameters' => [
|
||||
'section_storage' => ['layout_builder_tempstore' => TRUE],
|
||||
],
|
||||
'_layout_builder' => TRUE,
|
||||
'_admin_route' => FALSE,
|
||||
]
|
||||
),
|
||||
'layout_builder.defaults.with_bundle_parameter.discard_changes' => new Route(
|
||||
'/admin/entity/{bundle}/display-layout/{view_mode_name}/discard-changes',
|
||||
[
|
||||
|
|
|
@ -4,6 +4,7 @@ namespace Drupal\Tests\layout_builder\Unit;
|
|||
|
||||
use Drupal\Core\Entity\EntityFieldManagerInterface;
|
||||
use Drupal\Core\Entity\EntityStorageInterface;
|
||||
use Drupal\Core\Entity\EntityType;
|
||||
use Drupal\Core\Entity\EntityTypeInterface;
|
||||
use Drupal\Core\Entity\EntityTypeManagerInterface;
|
||||
use Drupal\Core\Entity\FieldableEntityInterface;
|
||||
|
@ -132,9 +133,13 @@ class OverridesSectionStorageTest extends UnitTestCase {
|
|||
$entity_storage->load('entity_with_layout')->willReturn($entity_with_layout->reveal());
|
||||
|
||||
$this->entityTypeManager->getStorage($expected_entity_type_id)->willReturn($entity_storage->reveal());
|
||||
|
||||
$entity_type = new EntityType(['id' => $expected_entity_type_id]);
|
||||
$this->entityTypeManager->getDefinition($expected_entity_type_id)->willReturn($entity_type);
|
||||
}
|
||||
else {
|
||||
$this->entityTypeManager->getStorage(Argument::any())->shouldNotBeCalled();
|
||||
$this->entityTypeManager->getDefinition(Argument::any())->shouldNotBeCalled();
|
||||
}
|
||||
|
||||
if (!$success) {
|
||||
|
@ -196,6 +201,10 @@ class OverridesSectionStorageTest extends UnitTestCase {
|
|||
$entity_with_layout->hasField(OverridesSectionStorage::FIELD_NAME)->willReturn(TRUE);
|
||||
$entity_storage->load('entity_with_layout')->willReturn($entity_with_layout->reveal());
|
||||
$this->entityTypeManager->getStorage($expected_entity_type_id)->willReturn($entity_storage->reveal());
|
||||
$entity_type = new EntityType([
|
||||
'id' => $expected_entity_type_id,
|
||||
]);
|
||||
$this->entityTypeManager->getDefinition($expected_entity_type_id)->willReturn($entity_type);
|
||||
}
|
||||
else {
|
||||
$this->entityTypeManager->getStorage(Argument::any())->shouldNotBeCalled();
|
||||
|
@ -256,6 +265,7 @@ class OverridesSectionStorageTest extends UnitTestCase {
|
|||
* @covers ::buildRoutes
|
||||
* @covers ::hasIntegerId
|
||||
* @covers ::getEntityTypes
|
||||
* @covers \Drupal\layout_builder\Routing\LayoutBuilderRoutesTrait::buildLayoutRoutes
|
||||
*/
|
||||
public function testBuildRoutes() {
|
||||
$entity_types = [];
|
||||
|
@ -264,15 +274,23 @@ class OverridesSectionStorageTest extends UnitTestCase {
|
|||
$not_fieldable->entityClassImplements(FieldableEntityInterface::class)->willReturn(FALSE);
|
||||
$entity_types['not_fieldable'] = $not_fieldable->reveal();
|
||||
|
||||
$no_layout_builder_form = $this->prophesize(EntityTypeInterface::class);
|
||||
$no_layout_builder_form->entityClassImplements(FieldableEntityInterface::class)->willReturn(TRUE);
|
||||
$no_layout_builder_form->hasHandlerClass('form', 'layout_builder')->willReturn(FALSE);
|
||||
$entity_types['no_layout_builder_form'] = $no_layout_builder_form->reveal();
|
||||
$this->entityFieldManager->getFieldStorageDefinitions('no_layout_builder_form')->shouldNotBeCalled();
|
||||
|
||||
$no_view_builder = $this->prophesize(EntityTypeInterface::class);
|
||||
$no_view_builder->entityClassImplements(FieldableEntityInterface::class)->willReturn(TRUE);
|
||||
$no_view_builder->hasViewBuilderClass()->willReturn(FALSE);
|
||||
$no_view_builder->hasHandlerClass('form', 'layout_builder')->willReturn(TRUE);
|
||||
$entity_types['no_view_builder'] = $no_view_builder->reveal();
|
||||
|
||||
$no_canonical_link = $this->prophesize(EntityTypeInterface::class);
|
||||
$no_canonical_link->entityClassImplements(FieldableEntityInterface::class)->willReturn(TRUE);
|
||||
$no_canonical_link->hasViewBuilderClass()->willReturn(TRUE);
|
||||
$no_canonical_link->hasLinkTemplate('canonical')->willReturn(FALSE);
|
||||
$no_canonical_link->hasHandlerClass('form', 'layout_builder')->willReturn(TRUE);
|
||||
$entity_types['no_canonical_link'] = $no_canonical_link->reveal();
|
||||
$this->entityFieldManager->getFieldStorageDefinitions('no_canonical_link')->shouldNotBeCalled();
|
||||
|
||||
|
@ -281,6 +299,7 @@ class OverridesSectionStorageTest extends UnitTestCase {
|
|||
$with_string_id->hasViewBuilderClass()->willReturn(TRUE);
|
||||
$with_string_id->hasLinkTemplate('canonical')->willReturn(TRUE);
|
||||
$with_string_id->getLinkTemplate('canonical')->willReturn('/entity/{entity}');
|
||||
$with_string_id->hasHandlerClass('form', 'layout_builder')->willReturn(TRUE);
|
||||
$with_string_id->id()->willReturn('with_string_id');
|
||||
$with_string_id->getKey('id')->willReturn('id');
|
||||
$entity_types['with_string_id'] = $with_string_id->reveal();
|
||||
|
@ -293,6 +312,7 @@ class OverridesSectionStorageTest extends UnitTestCase {
|
|||
$with_integer_id->hasViewBuilderClass()->willReturn(TRUE);
|
||||
$with_integer_id->hasLinkTemplate('canonical')->willReturn(TRUE);
|
||||
$with_integer_id->getLinkTemplate('canonical')->willReturn('/entity/{entity}');
|
||||
$with_integer_id->hasHandlerClass('form', 'layout_builder')->willReturn(TRUE);
|
||||
$with_integer_id->id()->willReturn('with_integer_id');
|
||||
$with_integer_id->getKey('id')->willReturn('id');
|
||||
$entity_types['with_integer_id'] = $with_integer_id->reveal();
|
||||
|
@ -309,28 +329,8 @@ class OverridesSectionStorageTest extends UnitTestCase {
|
|||
'entity_type_id' => 'with_string_id',
|
||||
'section_storage_type' => 'overrides',
|
||||
'section_storage' => '',
|
||||
'_controller' => '\Drupal\layout_builder\Controller\LayoutBuilderController::layout',
|
||||
'_title_callback' => '\Drupal\layout_builder\Controller\LayoutBuilderController::title',
|
||||
],
|
||||
[
|
||||
'_has_layout_section' => 'true',
|
||||
'_layout_builder_access' => 'view',
|
||||
],
|
||||
[
|
||||
'parameters' => [
|
||||
'section_storage' => ['layout_builder_tempstore' => TRUE],
|
||||
'with_string_id' => ['type' => 'entity:with_string_id'],
|
||||
],
|
||||
'_layout_builder' => TRUE,
|
||||
]
|
||||
),
|
||||
'layout_builder.overrides.with_string_id.save' => new Route(
|
||||
'/entity/{entity}/layout/save',
|
||||
[
|
||||
'entity_type_id' => 'with_string_id',
|
||||
'section_storage_type' => 'overrides',
|
||||
'section_storage' => '',
|
||||
'_controller' => '\Drupal\layout_builder\Controller\LayoutBuilderController::saveLayout',
|
||||
'_entity_form' => 'with_string_id.layout_builder',
|
||||
],
|
||||
[
|
||||
'_has_layout_section' => 'true',
|
||||
|
@ -390,29 +390,8 @@ class OverridesSectionStorageTest extends UnitTestCase {
|
|||
'entity_type_id' => 'with_integer_id',
|
||||
'section_storage_type' => 'overrides',
|
||||
'section_storage' => '',
|
||||
'_controller' => '\Drupal\layout_builder\Controller\LayoutBuilderController::layout',
|
||||
'_title_callback' => '\Drupal\layout_builder\Controller\LayoutBuilderController::title',
|
||||
],
|
||||
[
|
||||
'_has_layout_section' => 'true',
|
||||
'_layout_builder_access' => 'view',
|
||||
'with_integer_id' => '\d+',
|
||||
],
|
||||
[
|
||||
'parameters' => [
|
||||
'section_storage' => ['layout_builder_tempstore' => TRUE],
|
||||
'with_integer_id' => ['type' => 'entity:with_integer_id'],
|
||||
],
|
||||
'_layout_builder' => TRUE,
|
||||
]
|
||||
),
|
||||
'layout_builder.overrides.with_integer_id.save' => new Route(
|
||||
'/entity/{entity}/layout/save',
|
||||
[
|
||||
'entity_type_id' => 'with_integer_id',
|
||||
'section_storage_type' => 'overrides',
|
||||
'section_storage' => '',
|
||||
'_controller' => '\Drupal\layout_builder\Controller\LayoutBuilderController::saveLayout',
|
||||
'_entity_form' => 'with_integer_id.layout_builder',
|
||||
],
|
||||
[
|
||||
'_has_layout_section' => 'true',
|
||||
|
|
Loading…
Reference in New Issue