composer update
This commit is contained in:
parent
f6abc3dce2
commit
71dfaca858
1753 changed files with 45274 additions and 14619 deletions
|
@ -85,3 +85,32 @@
|
|||
display: block;
|
||||
padding-top: 0.55em;
|
||||
}
|
||||
|
||||
#drupal-off-canvas .inline-block-create-button {
|
||||
display: block;
|
||||
padding: 24px;
|
||||
padding-left: 44px;
|
||||
font-size: 16px;
|
||||
color: #eee;
|
||||
background: url(../../../misc/icons/bebebe/plus.svg) transparent 16px no-repeat;
|
||||
}
|
||||
|
||||
#drupal-off-canvas .inline-block-create-button,
|
||||
#drupal-off-canvas .inline-block-list__item {
|
||||
margin: 0 -20px;
|
||||
background-color: #444;
|
||||
}
|
||||
|
||||
#drupal-off-canvas .inline-block-create-button:hover,
|
||||
#drupal-off-canvas .inline-block-list__item:hover {
|
||||
background-color: #333;
|
||||
}
|
||||
|
||||
#drupal-off-canvas .inline-block-list {
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
#drupal-off-canvas .inline-block-list__item {
|
||||
display: block;
|
||||
padding: 15px 0 15px 25px;
|
||||
}
|
||||
|
|
|
@ -9,3 +9,5 @@ dependencies:
|
|||
- drupal:contextual
|
||||
# @todo Discuss removing in https://www.drupal.org/project/drupal/issues/2935999.
|
||||
- drupal:field_ui
|
||||
# @todo Discuss removing in https://www.drupal.org/project/drupal/issues/3003610.
|
||||
- drupal:block
|
||||
|
|
|
@ -19,6 +19,7 @@ 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().
|
||||
|
@ -62,8 +63,8 @@ function layout_builder_entity_type_alter(array &$entity_types) {
|
|||
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']['layout_builder__layout']);
|
||||
$key = array_search('layout_builder__layout', $form['#fields']);
|
||||
unset($form['fields'][OverridesSectionStorage::FIELD_NAME]);
|
||||
$key = array_search(OverridesSectionStorage::FIELD_NAME, $form['#fields']);
|
||||
if ($key !== FALSE) {
|
||||
unset($form['#fields'][$key]);
|
||||
}
|
||||
|
@ -177,7 +178,7 @@ function layout_builder_cron() {
|
|||
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') {
|
||||
if ($consumer !== 'layout_builder' || !isset($extra['list']) || $extra['list'] !== 'inline_blocks') {
|
||||
foreach ($definitions as $id => $definition) {
|
||||
if ($definition['id'] === 'inline_block') {
|
||||
unset($definitions[$id]);
|
||||
|
@ -202,3 +203,21 @@ function layout_builder_block_content_access(EntityInterface $entity, $operation
|
|||
}
|
||||
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 definition with required contexts.
|
||||
if ($definition['provider'] === 'layout_builder' && !empty($definition['context'])) {
|
||||
/** @var \Drupal\Core\Plugin\Context\ContextDefinitionInterface $context */
|
||||
foreach ($definition['context'] as $context) {
|
||||
if ($context->isRequired()) {
|
||||
unset($definitions[$id]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -80,6 +80,19 @@ layout_builder.add_block:
|
|||
section_storage:
|
||||
layout_builder_tempstore: TRUE
|
||||
|
||||
layout_builder.choose_inline_block:
|
||||
path: '/layout_builder/choose/inline-block/{section_storage_type}/{section_storage}/{delta}/{region}'
|
||||
defaults:
|
||||
_controller: '\Drupal\layout_builder\Controller\ChooseBlockController::inlineBlockList'
|
||||
_title: 'Add a new Inline Block'
|
||||
requirements:
|
||||
_permission: 'configure any layout'
|
||||
options:
|
||||
_admin_route: TRUE
|
||||
parameters:
|
||||
section_storage:
|
||||
layout_builder_tempstore: TRUE
|
||||
|
||||
layout_builder.update_block:
|
||||
path: '/layout_builder/update/block/{section_storage_type}/{section_storage}/{delta}/{region}/{uuid}'
|
||||
defaults:
|
||||
|
|
|
@ -5,6 +5,7 @@ namespace Drupal\layout_builder\Controller;
|
|||
use Drupal\Core\Ajax\AjaxHelperTrait;
|
||||
use Drupal\Core\Block\BlockManagerInterface;
|
||||
use Drupal\Core\DependencyInjection\ContainerInjectionInterface;
|
||||
use Drupal\Core\Entity\EntityTypeManagerInterface;
|
||||
use Drupal\Core\StringTranslation\StringTranslationTrait;
|
||||
use Drupal\Core\Url;
|
||||
use Drupal\layout_builder\Context\LayoutBuilderContextTrait;
|
||||
|
@ -29,14 +30,24 @@ class ChooseBlockController implements ContainerInjectionInterface {
|
|||
*/
|
||||
protected $blockManager;
|
||||
|
||||
/**
|
||||
* The entity type manager.
|
||||
*
|
||||
* @var \Drupal\Core\Entity\EntityTypeManagerInterface
|
||||
*/
|
||||
protected $entityTypeManager;
|
||||
|
||||
/**
|
||||
* ChooseBlockController constructor.
|
||||
*
|
||||
* @param \Drupal\Core\Block\BlockManagerInterface $block_manager
|
||||
* The block manager.
|
||||
* @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
|
||||
* The entity type manager.
|
||||
*/
|
||||
public function __construct(BlockManagerInterface $block_manager) {
|
||||
public function __construct(BlockManagerInterface $block_manager, EntityTypeManagerInterface $entity_type_manager) {
|
||||
$this->blockManager = $block_manager;
|
||||
$this->entityTypeManager = $entity_type_manager;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -44,7 +55,8 @@ class ChooseBlockController implements ContainerInjectionInterface {
|
|||
*/
|
||||
public static function create(ContainerInterface $container) {
|
||||
return new static(
|
||||
$container->get('plugin.manager.block')
|
||||
$container->get('plugin.manager.block'),
|
||||
$container->get('entity_type.manager')
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -63,8 +75,43 @@ class ChooseBlockController implements ContainerInjectionInterface {
|
|||
*/
|
||||
public function build(SectionStorageInterface $section_storage, $delta, $region) {
|
||||
$build['#title'] = $this->t('Choose a block');
|
||||
$build['#type'] = 'container';
|
||||
$build['#attributes']['class'][] = 'block-categories';
|
||||
if ($this->entityTypeManager->hasDefinition('block_content_type') && $types = $this->entityTypeManager->getStorage('block_content_type')->loadMultiple()) {
|
||||
if (count($types) === 1) {
|
||||
$type = reset($types);
|
||||
$plugin_id = 'inline_block:' . $type->id();
|
||||
if ($this->blockManager->hasDefinition($plugin_id)) {
|
||||
$url = Url::fromRoute('layout_builder.add_block', [
|
||||
'section_storage_type' => $section_storage->getStorageType(),
|
||||
'section_storage' => $section_storage->getStorageId(),
|
||||
'delta' => $delta,
|
||||
'region' => $region,
|
||||
'plugin_id' => $plugin_id,
|
||||
]);
|
||||
}
|
||||
}
|
||||
else {
|
||||
$url = Url::fromRoute('layout_builder.choose_inline_block', [
|
||||
'section_storage_type' => $section_storage->getStorageType(),
|
||||
'section_storage' => $section_storage->getStorageId(),
|
||||
'delta' => $delta,
|
||||
'region' => $region,
|
||||
]);
|
||||
}
|
||||
if (isset($url)) {
|
||||
$build['add_block'] = [
|
||||
'#type' => 'link',
|
||||
'#url' => $url,
|
||||
'#title' => $this->t('Create @entity_type', [
|
||||
'@entity_type' => $this->entityTypeManager->getDefinition('block_content')->getSingularLabel(),
|
||||
]),
|
||||
'#attributes' => $this->getAjaxAttributes(),
|
||||
];
|
||||
$build['add_block']['#attributes']['class'][] = 'inline-block-create-button';
|
||||
}
|
||||
}
|
||||
|
||||
$block_categories['#type'] = 'container';
|
||||
$block_categories['#attributes']['class'][] = 'block-categories';
|
||||
|
||||
// @todo Explicitly cast delta to an integer, remove this in
|
||||
// https://www.drupal.org/project/drupal/issues/2984509.
|
||||
|
@ -75,35 +122,116 @@ class ChooseBlockController implements ContainerInjectionInterface {
|
|||
'delta' => $delta,
|
||||
'region' => $region,
|
||||
]);
|
||||
foreach ($this->blockManager->getGroupedDefinitions($definitions) as $category => $blocks) {
|
||||
$build[$category]['#type'] = 'details';
|
||||
$build[$category]['#open'] = TRUE;
|
||||
$build[$category]['#title'] = $category;
|
||||
$build[$category]['links'] = [
|
||||
'#theme' => 'links',
|
||||
];
|
||||
foreach ($blocks as $block_id => $block) {
|
||||
$link = [
|
||||
'title' => $block['admin_label'],
|
||||
'url' => Url::fromRoute('layout_builder.add_block',
|
||||
[
|
||||
'section_storage_type' => $section_storage->getStorageType(),
|
||||
'section_storage' => $section_storage->getStorageId(),
|
||||
'delta' => $delta,
|
||||
'region' => $region,
|
||||
'plugin_id' => $block_id,
|
||||
]
|
||||
),
|
||||
];
|
||||
if ($this->isAjax()) {
|
||||
$link['attributes']['class'][] = 'use-ajax';
|
||||
$link['attributes']['data-dialog-type'][] = 'dialog';
|
||||
$link['attributes']['data-dialog-renderer'][] = 'off_canvas';
|
||||
}
|
||||
$build[$category]['links']['#links'][] = $link;
|
||||
$grouped_definitions = $this->blockManager->getGroupedDefinitions($definitions);
|
||||
foreach ($grouped_definitions as $category => $blocks) {
|
||||
$block_categories[$category]['#type'] = 'details';
|
||||
$block_categories[$category]['#open'] = TRUE;
|
||||
$block_categories[$category]['#title'] = $category;
|
||||
$block_categories[$category]['links'] = $this->getBlockLinks($section_storage, $delta, $region, $blocks);
|
||||
}
|
||||
$build['block_categories'] = $block_categories;
|
||||
return $build;
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides the UI for choosing a new inline block.
|
||||
*
|
||||
* @param \Drupal\layout_builder\SectionStorageInterface $section_storage
|
||||
* The section storage.
|
||||
* @param int $delta
|
||||
* The delta of the section to splice.
|
||||
* @param string $region
|
||||
* The region the block is going in.
|
||||
*
|
||||
* @return array
|
||||
* A render array.
|
||||
*/
|
||||
public function inlineBlockList(SectionStorageInterface $section_storage, $delta, $region) {
|
||||
$definitions = $this->blockManager->getFilteredDefinitions('layout_builder', $this->getAvailableContexts($section_storage), [
|
||||
'section_storage' => $section_storage,
|
||||
'region' => $region,
|
||||
'list' => 'inline_blocks',
|
||||
]);
|
||||
$blocks = $this->blockManager->getGroupedDefinitions($definitions);
|
||||
$build = [];
|
||||
if (isset($blocks['Inline blocks'])) {
|
||||
$build['links'] = $this->getBlockLinks($section_storage, $delta, $region, $blocks['Inline blocks']);
|
||||
$build['links']['#attributes']['class'][] = 'inline-block-list';
|
||||
foreach ($build['links']['#links'] as &$link) {
|
||||
$link['attributes']['class'][] = 'inline-block-list__item';
|
||||
}
|
||||
$build['back_button'] = [
|
||||
'#type' => 'link',
|
||||
'#url' => Url::fromRoute('layout_builder.choose_block',
|
||||
[
|
||||
'section_storage_type' => $section_storage->getStorageType(),
|
||||
'section_storage' => $section_storage->getStorageId(),
|
||||
'delta' => $delta,
|
||||
'region' => $region,
|
||||
]
|
||||
),
|
||||
'#title' => $this->t('Back'),
|
||||
'#attributes' => $this->getAjaxAttributes(),
|
||||
];
|
||||
}
|
||||
return $build;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a render array of block links.
|
||||
*
|
||||
* @param \Drupal\layout_builder\SectionStorageInterface $section_storage
|
||||
* The section storage.
|
||||
* @param int $delta
|
||||
* The delta of the section to splice.
|
||||
* @param string $region
|
||||
* The region the block is going in.
|
||||
* @param array $blocks
|
||||
* The information for each block.
|
||||
*
|
||||
* @return array
|
||||
* The block links render array.
|
||||
*/
|
||||
protected function getBlockLinks(SectionStorageInterface $section_storage, $delta, $region, array $blocks) {
|
||||
$links = [];
|
||||
foreach ($blocks as $block_id => $block) {
|
||||
$link = [
|
||||
'title' => $block['admin_label'],
|
||||
'url' => Url::fromRoute('layout_builder.add_block',
|
||||
[
|
||||
'section_storage_type' => $section_storage->getStorageType(),
|
||||
'section_storage' => $section_storage->getStorageId(),
|
||||
'delta' => $delta,
|
||||
'region' => $region,
|
||||
'plugin_id' => $block_id,
|
||||
]
|
||||
),
|
||||
'attributes' => $this->getAjaxAttributes(),
|
||||
];
|
||||
|
||||
$links[] = $link;
|
||||
}
|
||||
return [
|
||||
'#theme' => 'links',
|
||||
'#links' => $links,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get dialog attributes if an ajax request.
|
||||
*
|
||||
* @return array
|
||||
* The attributes array.
|
||||
*/
|
||||
protected function getAjaxAttributes() {
|
||||
if ($this->isAjax()) {
|
||||
return [
|
||||
'class' => ['use-ajax'],
|
||||
'data-dialog-type' => 'dialog',
|
||||
'data-dialog-renderer' => 'off_canvas',
|
||||
];
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
namespace Drupal\layout_builder\Controller;
|
||||
|
||||
use Drupal\Core\Ajax\AjaxHelperTrait;
|
||||
use Drupal\Core\DependencyInjection\ContainerInjectionInterface;
|
||||
use Drupal\Core\Messenger\MessengerInterface;
|
||||
use Drupal\Core\Plugin\PluginFormInterface;
|
||||
|
@ -24,6 +25,7 @@ class LayoutBuilderController implements ContainerInjectionInterface {
|
|||
|
||||
use LayoutBuilderContextTrait;
|
||||
use StringTranslationTrait;
|
||||
use AjaxHelperTrait;
|
||||
|
||||
/**
|
||||
* The layout tempstore repository.
|
||||
|
@ -90,6 +92,11 @@ class LayoutBuilderController implements ContainerInjectionInterface {
|
|||
$this->prepareLayout($section_storage, $is_rebuilding);
|
||||
|
||||
$output = [];
|
||||
if ($this->isAjax()) {
|
||||
$output['status_messages'] = [
|
||||
'#type' => 'status_messages',
|
||||
];
|
||||
}
|
||||
$count = 0;
|
||||
for ($i = 0; $i < $section_storage->count(); $i++) {
|
||||
$output[] = $this->buildAddSectionLink($section_storage, $count);
|
||||
|
@ -114,6 +121,11 @@ class LayoutBuilderController implements ContainerInjectionInterface {
|
|||
* Indicates if the layout is rebuilding.
|
||||
*/
|
||||
protected function prepareLayout(SectionStorageInterface $section_storage, $is_rebuilding) {
|
||||
// If the layout has pending changes, add a warning.
|
||||
if ($this->layoutTempstoreRepository->has($section_storage)) {
|
||||
$this->messenger->addWarning($this->t('You have unsaved changes.'));
|
||||
}
|
||||
|
||||
// Only add sections if the layout is new and empty.
|
||||
if (!$is_rebuilding && $section_storage->count() === 0) {
|
||||
$sections = [];
|
||||
|
@ -269,7 +281,7 @@ class LayoutBuilderController implements ContainerInjectionInterface {
|
|||
],
|
||||
'remove' => [
|
||||
'#type' => 'link',
|
||||
'#title' => $this->t('Remove section'),
|
||||
'#title' => $this->t('Remove section <span class="visually-hidden">@section</span>', ['@section' => $delta + 1]),
|
||||
'#url' => Url::fromRoute('layout_builder.remove_section', [
|
||||
'section_storage_type' => $storage_type,
|
||||
'section_storage' => $storage_id,
|
||||
|
|
|
@ -9,6 +9,7 @@ use Drupal\Core\Plugin\Context\EntityContext;
|
|||
use Drupal\Core\StringTranslation\TranslatableMarkup;
|
||||
use Drupal\field\Entity\FieldConfig;
|
||||
use Drupal\field\Entity\FieldStorageConfig;
|
||||
use Drupal\layout_builder\Plugin\SectionStorage\OverridesSectionStorage;
|
||||
use Drupal\layout_builder\Section;
|
||||
use Drupal\layout_builder\SectionComponent;
|
||||
use Drupal\layout_builder\SectionStorage\SectionStorageTrait;
|
||||
|
@ -110,10 +111,10 @@ class LayoutBuilderEntityViewDisplay extends BaseEntityViewDisplay implements La
|
|||
$bundle = $this->getTargetBundle();
|
||||
|
||||
if ($new_value) {
|
||||
$this->addSectionField($entity_type_id, $bundle, 'layout_builder__layout');
|
||||
$this->addSectionField($entity_type_id, $bundle, OverridesSectionStorage::FIELD_NAME);
|
||||
}
|
||||
else {
|
||||
$this->removeSectionField($entity_type_id, $bundle, 'layout_builder__layout');
|
||||
$this->removeSectionField($entity_type_id, $bundle, OverridesSectionStorage::FIELD_NAME);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -274,8 +275,8 @@ class LayoutBuilderEntityViewDisplay extends BaseEntityViewDisplay implements La
|
|||
* The sections.
|
||||
*/
|
||||
protected function getRuntimeSections(FieldableEntityInterface $entity) {
|
||||
if ($this->isOverridable() && !$entity->get('layout_builder__layout')->isEmpty()) {
|
||||
return $entity->get('layout_builder__layout')->getSections();
|
||||
if ($this->isOverridable() && !$entity->get(OverridesSectionStorage::FIELD_NAME)->isEmpty()) {
|
||||
return $entity->get(OverridesSectionStorage::FIELD_NAME)->getSections();
|
||||
}
|
||||
|
||||
return $this->getSections();
|
||||
|
|
|
@ -5,6 +5,8 @@ namespace Drupal\layout_builder\EventSubscriber;
|
|||
use Drupal\block_content\Access\RefinableDependentAccessInterface;
|
||||
use Drupal\Core\Access\AccessResult;
|
||||
use Drupal\Core\Block\BlockPluginInterface;
|
||||
use Drupal\Core\Render\Element;
|
||||
use Drupal\Core\Render\PreviewFallbackInterface;
|
||||
use Drupal\Core\Session\AccountInterface;
|
||||
use Drupal\layout_builder\Access\LayoutPreviewAccessAllowed;
|
||||
use Drupal\layout_builder\Event\SectionComponentBuildRenderArrayEvent;
|
||||
|
@ -88,6 +90,14 @@ class BlockComponentRenderArray implements EventSubscriberInterface {
|
|||
if ($access->isAllowed()) {
|
||||
$event->addCacheableDependency($block);
|
||||
|
||||
$content = $block->build();
|
||||
$is_content_empty = Element::isEmpty($content);
|
||||
$is_placeholder_ready = $event->inPreview() && $block instanceof PreviewFallbackInterface;
|
||||
// If the content is empty and no placeholder is available, return.
|
||||
if ($is_content_empty && !$is_placeholder_ready) {
|
||||
return;
|
||||
}
|
||||
|
||||
$build = [
|
||||
// @todo Move this to BlockBase in https://www.drupal.org/node/2931040.
|
||||
'#theme' => 'block',
|
||||
|
@ -96,8 +106,11 @@ class BlockComponentRenderArray implements EventSubscriberInterface {
|
|||
'#base_plugin_id' => $block->getBaseId(),
|
||||
'#derivative_plugin_id' => $block->getDerivativeId(),
|
||||
'#weight' => $event->getComponent()->getWeight(),
|
||||
'content' => $block->build(),
|
||||
'content' => $content,
|
||||
];
|
||||
if ($is_content_empty && $is_placeholder_ready) {
|
||||
$build['content']['#markup'] = $block->getPreviewFallbackString();
|
||||
}
|
||||
$event->setBuild($build);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@ use Drupal\Core\Field\FieldDefinitionInterface;
|
|||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\field_ui\Form\EntityViewDisplayEditForm;
|
||||
use Drupal\layout_builder\Entity\LayoutEntityDisplayInterface;
|
||||
use Drupal\layout_builder\Plugin\SectionStorage\OverridesSectionStorage;
|
||||
use Drupal\layout_builder\SectionStorageInterface;
|
||||
|
||||
/**
|
||||
|
@ -48,8 +49,8 @@ class LayoutBuilderEntityViewDisplayForm extends EntityViewDisplayEditForm {
|
|||
$form = parent::form($form, $form_state);
|
||||
|
||||
// Remove the Layout Builder field from the list.
|
||||
$form['#fields'] = array_diff($form['#fields'], ['layout_builder__layout']);
|
||||
unset($form['fields']['layout_builder__layout']);
|
||||
$form['#fields'] = array_diff($form['#fields'], [OverridesSectionStorage::FIELD_NAME]);
|
||||
unset($form['fields'][OverridesSectionStorage::FIELD_NAME]);
|
||||
|
||||
$is_enabled = $this->entity->isLayoutBuilderEnabled();
|
||||
if ($is_enabled) {
|
||||
|
@ -133,7 +134,7 @@ class LayoutBuilderEntityViewDisplayForm extends EntityViewDisplayEditForm {
|
|||
|
||||
$entity_type = $this->entityTypeManager->getDefinition($display->getTargetEntityTypeId());
|
||||
$query = $this->entityTypeManager->getStorage($display->getTargetEntityTypeId())->getQuery()
|
||||
->exists('layout_builder__layout');
|
||||
->exists(OverridesSectionStorage::FIELD_NAME);
|
||||
if ($bundle_key = $entity_type->getKey('bundle')) {
|
||||
$query->condition($bundle_key, $display->getTargetBundle());
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@ use Drupal\Component\Plugin\DerivativeInspectionInterface;
|
|||
use Drupal\Core\Entity\EntityInterface;
|
||||
use Drupal\Core\Entity\FieldableEntityInterface;
|
||||
use Drupal\layout_builder\Entity\LayoutEntityDisplayInterface;
|
||||
use Drupal\layout_builder\Plugin\SectionStorage\OverridesSectionStorage;
|
||||
|
||||
/**
|
||||
* Methods to help with entities using the layout builder.
|
||||
|
@ -65,7 +66,7 @@ trait LayoutEntityHelperTrait {
|
|||
return $entity->getSections();
|
||||
}
|
||||
elseif ($this->isEntityUsingFieldOverride($entity)) {
|
||||
return $entity->get('layout_builder__layout')->getSections();
|
||||
return $entity->get(OverridesSectionStorage::FIELD_NAME)->getSections();
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
@ -102,7 +103,7 @@ trait LayoutEntityHelperTrait {
|
|||
* TRUE if the entity is using a field for a layout override.
|
||||
*/
|
||||
protected function isEntityUsingFieldOverride(EntityInterface $entity) {
|
||||
return $entity instanceof FieldableEntityInterface && $entity->hasField('layout_builder__layout');
|
||||
return $entity instanceof FieldableEntityInterface && $entity->hasField(OverridesSectionStorage::FIELD_NAME);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -45,6 +45,15 @@ class LayoutTempstoreRepository implements LayoutTempstoreRepositoryInterface {
|
|||
return $section_storage;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function has(SectionStorageInterface $section_storage) {
|
||||
$id = $section_storage->getStorageId();
|
||||
$tempstore = $this->getTempstore($section_storage)->get($id);
|
||||
return !empty($tempstore['section_storage']);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
|
|
|
@ -35,6 +35,17 @@ interface LayoutTempstoreRepositoryInterface {
|
|||
*/
|
||||
public function set(SectionStorageInterface $section_storage);
|
||||
|
||||
/**
|
||||
* Checks for the existence of a tempstore version of a section storage.
|
||||
*
|
||||
* @param \Drupal\layout_builder\SectionStorageInterface $section_storage
|
||||
* The section storage to check for in tempstore.
|
||||
*
|
||||
* @return bool
|
||||
* TRUE if there is a tempstore version of this section storage.
|
||||
*/
|
||||
public function has(SectionStorageInterface $section_storage);
|
||||
|
||||
/**
|
||||
* Removes the tempstore version of a section storage.
|
||||
*
|
||||
|
|
|
@ -130,13 +130,22 @@ class ExtraFieldBlock extends BlockBase implements ContextAwarePluginInterface,
|
|||
// render array. If the hook is invoked the placeholder will be
|
||||
// replaced.
|
||||
// @see ::replaceFieldPlaceholder()
|
||||
'#markup' => new TranslatableMarkup('Placeholder for the "@field" field', ['@field' => $extra_fields['display'][$this->fieldName]['label']]),
|
||||
'#markup' => $this->getPreviewFallbackString(),
|
||||
];
|
||||
}
|
||||
CacheableMetadata::createFromObject($this)->applyTo($build);
|
||||
return $build;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getPreviewFallbackString() {
|
||||
$entity = $this->getEntity();
|
||||
$extra_fields = $this->entityFieldManager->getExtraFields($entity->getEntityTypeId(), $entity->bundle());
|
||||
return new TranslatableMarkup('Placeholder for the "@field" field', ['@field' => $extra_fields['display'][$this->fieldName]['label']]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Replaces all placeholders for a given field.
|
||||
*
|
||||
|
|
|
@ -17,7 +17,6 @@ use Drupal\Core\Field\FormatterPluginManager;
|
|||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
|
||||
use Drupal\Core\Plugin\ContextAwarePluginInterface;
|
||||
use Drupal\Core\Render\Element;
|
||||
use Drupal\Core\Session\AccountInterface;
|
||||
use Drupal\Core\StringTranslation\TranslatableMarkup;
|
||||
use Psr\Log\LoggerInterface;
|
||||
|
@ -160,13 +159,17 @@ class FieldBlock extends BlockBase implements ContextAwarePluginInterface, Conta
|
|||
$build = [];
|
||||
$this->logger->warning('The field "%field" failed to render with the error of "%error".', ['%field' => $this->fieldName, '%error' => $e->getMessage()]);
|
||||
}
|
||||
if (!empty($entity->in_preview) && !Element::getVisibleChildren($build)) {
|
||||
$build['content']['#markup'] = new TranslatableMarkup('Placeholder for the "@field" field', ['@field' => $this->getFieldDefinition()->getLabel()]);
|
||||
}
|
||||
CacheableMetadata::createFromObject($this)->applyTo($build);
|
||||
return $build;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getPreviewFallbackString() {
|
||||
return new TranslatableMarkup('Placeholder for the "@field" field', ['@field' => $this->getFieldDefinition()->getLabel()]);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
|
|
|
@ -106,7 +106,7 @@ class FieldBlockDeriver extends DeriverBase implements ContainerDeriverInterface
|
|||
$derivative['default_formatter'] = $field_type_definition['default_formatter'];
|
||||
}
|
||||
|
||||
$derivative['category'] = $this->t('@entity', ['@entity' => $entity_type_labels[$entity_type_id]]);
|
||||
$derivative['category'] = $this->t('@entity fields', ['@entity' => $entity_type_labels[$entity_type_id]]);
|
||||
|
||||
$derivative['admin_label'] = $field_definition->getLabel();
|
||||
|
||||
|
|
|
@ -3,11 +3,11 @@
|
|||
namespace Drupal\layout_builder\Plugin\Derivative;
|
||||
|
||||
use Drupal\Component\Plugin\Derivative\DeriverBase;
|
||||
use Drupal\Core\Entity\EntityTypeInterface;
|
||||
use Drupal\Core\Entity\EntityTypeManagerInterface;
|
||||
use Drupal\Core\Entity\FieldableEntityInterface;
|
||||
use Drupal\Core\Plugin\Discovery\ContainerDeriverInterface;
|
||||
use Drupal\Core\StringTranslation\StringTranslationTrait;
|
||||
use Drupal\layout_builder\Plugin\SectionStorage\SectionStorageLocalTaskProviderInterface;
|
||||
use Drupal\layout_builder\SectionStorage\SectionStorageManagerInterface;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
/**
|
||||
|
@ -28,14 +28,24 @@ class LayoutBuilderLocalTaskDeriver extends DeriverBase implements ContainerDeri
|
|||
*/
|
||||
protected $entityTypeManager;
|
||||
|
||||
/**
|
||||
* The section storage manager.
|
||||
*
|
||||
* @var \Drupal\layout_builder\SectionStorage\SectionStorageManagerInterface
|
||||
*/
|
||||
protected $sectionStorageManager;
|
||||
|
||||
/**
|
||||
* Constructs a new LayoutBuilderLocalTaskDeriver.
|
||||
*
|
||||
* @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
|
||||
* The entity type manager.
|
||||
* @param \Drupal\layout_builder\SectionStorage\SectionStorageManagerInterface $section_storage_manager
|
||||
* The section storage manager.
|
||||
*/
|
||||
public function __construct(EntityTypeManagerInterface $entity_type_manager) {
|
||||
public function __construct(EntityTypeManagerInterface $entity_type_manager, SectionStorageManagerInterface $section_storage_manager) {
|
||||
$this->entityTypeManager = $entity_type_manager;
|
||||
$this->sectionStorageManager = $section_storage_manager;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -43,7 +53,8 @@ class LayoutBuilderLocalTaskDeriver extends DeriverBase implements ContainerDeri
|
|||
*/
|
||||
public static function create(ContainerInterface $container, $base_plugin_id) {
|
||||
return new static(
|
||||
$container->get('entity_type.manager')
|
||||
$container->get('entity_type.manager'),
|
||||
$container->get('plugin.manager.layout_builder.section_storage')
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -51,84 +62,13 @@ class LayoutBuilderLocalTaskDeriver extends DeriverBase implements ContainerDeri
|
|||
* {@inheritdoc}
|
||||
*/
|
||||
public function getDerivativeDefinitions($base_plugin_definition) {
|
||||
foreach ($this->getEntityTypesForOverrides() as $entity_type_id => $entity_type) {
|
||||
// Overrides.
|
||||
$this->derivatives["layout_builder.overrides.$entity_type_id.view"] = $base_plugin_definition + [
|
||||
'route_name' => "layout_builder.overrides.$entity_type_id.view",
|
||||
'weight' => 15,
|
||||
'title' => $this->t('Layout'),
|
||||
'base_route' => "entity.$entity_type_id.canonical",
|
||||
'cache_contexts' => ['layout_builder_is_active:' . $entity_type_id],
|
||||
];
|
||||
$this->derivatives["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],
|
||||
];
|
||||
$this->derivatives["layout_builder.overrides.$entity_type_id.cancel"] = $base_plugin_definition + [
|
||||
'route_name' => "layout_builder.overrides.$entity_type_id.cancel",
|
||||
'title' => $this->t('Cancel Layout'),
|
||||
'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.
|
||||
$this->derivatives["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],
|
||||
];
|
||||
foreach ($this->sectionStorageManager->getDefinitions() as $plugin_id => $definition) {
|
||||
$section_storage = $this->sectionStorageManager->loadEmpty($plugin_id);
|
||||
if ($section_storage instanceof SectionStorageLocalTaskProviderInterface) {
|
||||
$this->derivatives += $section_storage->buildLocalTasks($base_plugin_definition);
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($this->getEntityTypesForDefaults() as $entity_type_id => $entity_type) {
|
||||
// Defaults.
|
||||
$this->derivatives["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",
|
||||
];
|
||||
$this->derivatives["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",
|
||||
];
|
||||
$this->derivatives["layout_builder.defaults.$entity_type_id.cancel"] = $base_plugin_definition + [
|
||||
'route_name' => "layout_builder.defaults.$entity_type_id.cancel",
|
||||
'title' => $this->t('Cancel Layout'),
|
||||
'weight' => 5,
|
||||
'parent_id' => "layout_builder_ui:layout_builder.defaults.$entity_type_id.view",
|
||||
];
|
||||
}
|
||||
|
||||
return $this->derivatives;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of entity types relevant for defaults.
|
||||
*
|
||||
* @return \Drupal\Core\Entity\EntityTypeInterface[]
|
||||
* An array of entity types.
|
||||
*/
|
||||
protected function getEntityTypesForDefaults() {
|
||||
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');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of entity types relevant for overrides.
|
||||
*
|
||||
* @return \Drupal\Core\Entity\EntityTypeInterface[]
|
||||
* An array of entity types.
|
||||
*/
|
||||
protected function getEntityTypesForOverrides() {
|
||||
return array_filter($this->entityTypeManager->getDefinitions(), function (EntityTypeInterface $entity_type) {
|
||||
return $entity_type->entityClassImplements(FieldableEntityInterface::class) && $entity_type->hasViewBuilderClass() && $entity_type->hasLinkTemplate('canonical');
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -32,7 +32,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 {
|
||||
class DefaultsSectionStorage extends SectionStorageBase implements ContainerFactoryPluginInterface, DefaultsSectionStorageInterface, SectionStorageLocalTaskProviderInterface {
|
||||
|
||||
/**
|
||||
* The entity type manager.
|
||||
|
@ -196,6 +196,32 @@ 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.cancel"] = $base_plugin_definition + [
|
||||
'route_name' => "layout_builder.defaults.$entity_type_id.cancel",
|
||||
'title' => $this->t('Cancel Layout'),
|
||||
'weight' => 5,
|
||||
'parent_id' => "layout_builder_ui:layout_builder.defaults.$entity_type_id.view",
|
||||
];
|
||||
}
|
||||
return $local_tasks;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of relevant entity types.
|
||||
*
|
||||
|
|
|
@ -30,7 +30,14 @@ use Symfony\Component\Routing\RouteCollection;
|
|||
* experimental modules and development releases of contributed modules.
|
||||
* See https://www.drupal.org/core/experimental for more information.
|
||||
*/
|
||||
class OverridesSectionStorage extends SectionStorageBase implements ContainerFactoryPluginInterface, OverridesSectionStorageInterface {
|
||||
class OverridesSectionStorage extends SectionStorageBase implements ContainerFactoryPluginInterface, OverridesSectionStorageInterface, SectionStorageLocalTaskProviderInterface {
|
||||
|
||||
/**
|
||||
* The field name used by this storage.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const FIELD_NAME = 'layout_builder__layout';
|
||||
|
||||
/**
|
||||
* The entity type manager.
|
||||
|
@ -127,8 +134,8 @@ class OverridesSectionStorage extends SectionStorageBase implements ContainerFac
|
|||
if (strpos($id, '.') !== FALSE) {
|
||||
list($entity_type_id, $entity_id) = explode('.', $id, 2);
|
||||
$entity = $this->entityTypeManager->getStorage($entity_type_id)->load($entity_id);
|
||||
if ($entity instanceof FieldableEntityInterface && $entity->hasField('layout_builder__layout')) {
|
||||
return $entity->get('layout_builder__layout');
|
||||
if ($entity instanceof FieldableEntityInterface && $entity->hasField(static::FIELD_NAME)) {
|
||||
return $entity->get(static::FIELD_NAME);
|
||||
}
|
||||
}
|
||||
throw new \InvalidArgumentException(sprintf('The "%s" ID for the "%s" section storage type is invalid', $id, $this->getStorageType()));
|
||||
|
@ -157,6 +164,45 @@ class OverridesSectionStorage extends SectionStorageBase implements ContainerFac
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildLocalTasks($base_plugin_definition) {
|
||||
$local_tasks = [];
|
||||
foreach ($this->getEntityTypes() as $entity_type_id => $entity_type) {
|
||||
$local_tasks["layout_builder.overrides.$entity_type_id.view"] = $base_plugin_definition + [
|
||||
'route_name' => "layout_builder.overrides.$entity_type_id.view",
|
||||
'weight' => 15,
|
||||
'title' => $this->t('Layout'),
|
||||
'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.cancel"] = $base_plugin_definition + [
|
||||
'route_name' => "layout_builder.overrides.$entity_type_id.cancel",
|
||||
'title' => $this->t('Cancel Layout'),
|
||||
'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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if this entity type's ID is stored as an integer.
|
||||
*
|
||||
|
|
|
@ -0,0 +1,29 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\layout_builder\Plugin\SectionStorage;
|
||||
|
||||
/**
|
||||
* Allows section storage plugins to provide local tasks.
|
||||
*
|
||||
* @see \Drupal\layout_builder\Plugin\Derivative\LayoutBuilderLocalTaskDeriver
|
||||
* @see \Drupal\layout_builder\SectionStorageInterface
|
||||
*
|
||||
* @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.
|
||||
*/
|
||||
interface SectionStorageLocalTaskProviderInterface {
|
||||
|
||||
/**
|
||||
* Provides the local tasks dynamically for Layout Builder plugins.
|
||||
*
|
||||
* @param mixed $base_plugin_definition
|
||||
* The definition of the base plugin.
|
||||
*
|
||||
* @return array
|
||||
* An array of full derivative definitions keyed on derivative ID.
|
||||
*/
|
||||
public function buildLocalTasks($base_plugin_definition);
|
||||
|
||||
}
|
|
@ -356,4 +356,13 @@ class Section {
|
|||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Magic method: Implements a deep clone.
|
||||
*/
|
||||
public function __clone() {
|
||||
foreach ($this->components as $uuid => $component) {
|
||||
$this->components[$uuid] = clone $component;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -111,4 +111,17 @@ trait SectionStorageTrait {
|
|||
return isset($this->getSections()[$delta]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Magic method: Implements a deep clone.
|
||||
*/
|
||||
public function __clone() {
|
||||
$sections = $this->getSections();
|
||||
|
||||
foreach ($sections as $delta => $item) {
|
||||
$sections[$delta] = clone $item;
|
||||
}
|
||||
|
||||
$this->setSections($sections);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
# See \Drupal\layout_builder_fieldblock_test\Plugin\Block\FieldBlock.
|
||||
block.settings.field_block_test:*:*:*:
|
||||
type: block.settings.field_block:*:*:*
|
|
@ -0,0 +1,6 @@
|
|||
name: 'Layout Builder test'
|
||||
type: module
|
||||
description: 'Support module for testing layout building.'
|
||||
package: Testing
|
||||
version: VERSION
|
||||
core: 8.x
|
|
@ -0,0 +1,27 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\layout_builder_fieldblock_test\Plugin\Block;
|
||||
|
||||
use Drupal\layout_builder\Plugin\Block\FieldBlock as LayoutBuilderFieldBlock;
|
||||
|
||||
/**
|
||||
* Provides test field block to test with Block UI.
|
||||
*
|
||||
* \Drupal\Tests\layout_builder\FunctionalJavascript\FieldBlockTest provides
|
||||
* test coverage of complex AJAX interactions within certain field blocks.
|
||||
* layout_builder_plugin_filter_block__block_ui_alter() removes certain blocks
|
||||
* with 'layout_builder' as the provider. To make these blocks available during
|
||||
* testing, this plugin uses the same deriver but each derivative will have a
|
||||
* different provider.
|
||||
*
|
||||
* @Block(
|
||||
* id = "field_block_test",
|
||||
* deriver = "\Drupal\layout_builder\Plugin\Derivative\FieldBlockDeriver",
|
||||
* )
|
||||
*
|
||||
* @see \Drupal\Tests\layout_builder\FunctionalJavascript\FieldBlockTest
|
||||
* @see layout_builder_plugin_filter_block__block_ui_alter()
|
||||
*/
|
||||
class FieldBlock extends LayoutBuilderFieldBlock {
|
||||
|
||||
}
|
|
@ -22,6 +22,7 @@ class LayoutBuilderTest extends BrowserTestBase {
|
|||
'layout_builder_views_test',
|
||||
'layout_test',
|
||||
'block',
|
||||
'block_test',
|
||||
'node',
|
||||
'layout_builder_test',
|
||||
];
|
||||
|
@ -90,7 +91,7 @@ class LayoutBuilderTest extends BrowserTestBase {
|
|||
// The body field is only present once.
|
||||
$assert_session->elementsCount('css', '.field--name-body', 1);
|
||||
// The extra field is only present once.
|
||||
$this->assertTextAppearsOnce('Placeholder for the "Extra label" field');
|
||||
$assert_session->pageTextContainsOnce('Placeholder for the "Extra label" field');
|
||||
// Save the defaults.
|
||||
$assert_session->linkExists('Save Layout');
|
||||
$this->clickLink('Save Layout');
|
||||
|
@ -105,7 +106,7 @@ class LayoutBuilderTest extends BrowserTestBase {
|
|||
// The body field is only present once.
|
||||
$assert_session->elementsCount('css', '.field--name-body', 1);
|
||||
// The extra field is only present once.
|
||||
$this->assertTextAppearsOnce('Placeholder for the "Extra label" field');
|
||||
$assert_session->pageTextContainsOnce('Placeholder for the "Extra label" field');
|
||||
|
||||
// Add a new block.
|
||||
$assert_session->linkExists('Add Block');
|
||||
|
@ -316,6 +317,11 @@ class LayoutBuilderTest extends BrowserTestBase {
|
|||
$page->fillField('id', 'myothermenu');
|
||||
$page->pressButton('Save');
|
||||
|
||||
$page->clickLink('Add link');
|
||||
$page->fillField('title[0][value]', 'My link');
|
||||
$page->fillField('link[0][uri]', '/');
|
||||
$page->pressButton('Save');
|
||||
|
||||
$this->drupalPostForm('admin/structure/types/manage/bundle_with_section_field/display', ['layout[enabled]' => TRUE], 'Save');
|
||||
$assert_session->linkExists('Manage layout');
|
||||
$this->clickLink('Manage layout');
|
||||
|
@ -514,13 +520,68 @@ class LayoutBuilderTest extends BrowserTestBase {
|
|||
}
|
||||
|
||||
/**
|
||||
* Asserts that a text string only appears once on the page.
|
||||
* Tests the usage of placeholders for empty blocks.
|
||||
*
|
||||
* @param string $needle
|
||||
* The string to look for.
|
||||
* @see \Drupal\Core\Block\BlockPluginInterface::getPlaceholderString()
|
||||
* @see \Drupal\layout_builder\EventSubscriber\BlockComponentRenderArray::onBuildRender()
|
||||
*/
|
||||
protected function assertTextAppearsOnce($needle) {
|
||||
$this->assertEquals(1, substr_count($this->getSession()->getPage()->getContent(), $needle), "'$needle' only appears once on the page.");
|
||||
public function testBlockPlaceholder() {
|
||||
$assert_session = $this->assertSession();
|
||||
$page = $this->getSession()->getPage();
|
||||
|
||||
$this->drupalLogin($this->drupalCreateUser([
|
||||
'configure any layout',
|
||||
'administer node display',
|
||||
]));
|
||||
|
||||
$field_ui_prefix = 'admin/structure/types/manage/bundle_with_section_field';
|
||||
$this->drupalPostForm("$field_ui_prefix/display/default", ['layout[enabled]' => TRUE], 'Save');
|
||||
|
||||
// Customize the default view mode.
|
||||
$this->drupalGet("$field_ui_prefix/display-layout/default");
|
||||
|
||||
// Add a block whose content is controlled by state and is empty by default.
|
||||
$this->clickLink('Add Block');
|
||||
$this->clickLink('Test block caching');
|
||||
$page->fillField('settings[label]', 'The block label');
|
||||
$page->pressButton('Add Block');
|
||||
|
||||
$block_content = 'I am content';
|
||||
$placeholder_content = 'Placeholder for the "The block label" block';
|
||||
|
||||
// The block placeholder is displayed and there is no content.
|
||||
$assert_session->pageTextContains($placeholder_content);
|
||||
$assert_session->pageTextNotContains($block_content);
|
||||
|
||||
// Set block content and reload the page.
|
||||
\Drupal::state()->set('block_test.content', $block_content);
|
||||
$this->getSession()->reload();
|
||||
|
||||
// The block placeholder is no longer displayed and the content is visible.
|
||||
$assert_session->pageTextNotContains($placeholder_content);
|
||||
$assert_session->pageTextContains($block_content);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the Block UI when Layout Builder is installed.
|
||||
*/
|
||||
public function testBlockUiListing() {
|
||||
$assert_session = $this->assertSession();
|
||||
$page = $this->getSession()->getPage();
|
||||
|
||||
$this->drupalLogin($this->drupalCreateUser([
|
||||
'administer blocks',
|
||||
]));
|
||||
|
||||
$this->drupalGet('admin/structure/block');
|
||||
$page->clickLink('Place block');
|
||||
|
||||
// Ensure that blocks expected to appear are available.
|
||||
$assert_session->pageTextContains('Test HTML block');
|
||||
$assert_session->pageTextContains('Block test');
|
||||
// Ensure that blocks not expected to appear are not available.
|
||||
$assert_session->pageTextNotContains('Body');
|
||||
$assert_session->pageTextNotContains('Content fields');
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@ namespace Drupal\Tests\layout_builder\Functional;
|
|||
|
||||
use Drupal\language\Entity\ConfigurableLanguage;
|
||||
use Drupal\layout_builder\Entity\LayoutBuilderEntityViewDisplay;
|
||||
use Drupal\layout_builder\Plugin\SectionStorage\OverridesSectionStorage;
|
||||
use Drupal\layout_builder\Section;
|
||||
use Drupal\layout_builder\SectionComponent;
|
||||
use Drupal\Tests\BrowserTestBase;
|
||||
|
@ -20,13 +21,6 @@ class LayoutSectionTest extends BrowserTestBase {
|
|||
*/
|
||||
public static $modules = ['field_ui', 'layout_builder', 'node', 'block_test'];
|
||||
|
||||
/**
|
||||
* The name of the layout section field.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $fieldName = 'layout_builder__layout';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
|
@ -226,7 +220,7 @@ class LayoutSectionTest extends BrowserTestBase {
|
|||
]);
|
||||
$entity->addTranslation('es', [
|
||||
'title' => 'Translated node title',
|
||||
$this->fieldName => [
|
||||
OverridesSectionStorage::FIELD_NAME => [
|
||||
[
|
||||
'section' => new Section('layout_twocol', [], [
|
||||
'foo' => new SectionComponent('foo', 'first', [
|
||||
|
@ -373,7 +367,7 @@ class LayoutSectionTest extends BrowserTestBase {
|
|||
'value' => 'The node body',
|
||||
],
|
||||
],
|
||||
$this->fieldName => $section_values,
|
||||
OverridesSectionStorage::FIELD_NAME => $section_values,
|
||||
]);
|
||||
}
|
||||
|
||||
|
|
|
@ -16,7 +16,14 @@ class FieldBlockTest extends WebDriverTestBase {
|
|||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = ['block', 'datetime', 'layout_builder', 'user'];
|
||||
protected static $modules = [
|
||||
'block',
|
||||
'datetime',
|
||||
'layout_builder',
|
||||
'user',
|
||||
// See \Drupal\layout_builder_fieldblock_test\Plugin\Block\FieldBlock.
|
||||
'layout_builder_fieldblock_test',
|
||||
];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
|
@ -67,7 +74,7 @@ class FieldBlockTest extends WebDriverTestBase {
|
|||
$assert_session->pageTextNotContains('Initial email');
|
||||
|
||||
$assert_session->pageTextContains('Date field');
|
||||
$block_url = 'admin/structure/block/add/field_block%3Auser%3Auser%3Afield_date/classy';
|
||||
$block_url = 'admin/structure/block/add/field_block_test%3Auser%3Auser%3Afield_date/classy';
|
||||
$assert_session->linkByHrefExists($block_url);
|
||||
|
||||
$this->drupalGet($block_url);
|
||||
|
|
|
@ -192,8 +192,8 @@ class InlineBlockPrivateFilesTest extends InlineBlockTestBase {
|
|||
$page = $this->getSession()->getPage();
|
||||
$page->clickLink('Add Block');
|
||||
$assert_session->assertWaitOnAjaxRequest();
|
||||
$this->assertNotEmpty($assert_session->waitForElementVisible('css', '.block-categories details:contains(Create new block)'));
|
||||
$this->clickLink('Basic block');
|
||||
$this->assertNotEmpty($assert_session->waitForLink('Create custom block'));
|
||||
$this->clickLink('Create custom block');
|
||||
$assert_session->assertWaitOnAjaxRequest();
|
||||
$assert_session->fieldValueEquals('Title', '');
|
||||
$page->findField('Title')->setValue($title);
|
||||
|
|
|
@ -428,4 +428,74 @@ class InlineBlockTest extends InlineBlockTestBase {
|
|||
$assert_session->pageTextNotContains('You are not authorized to access this page');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the workflow for adding an inline block depending on number of types.
|
||||
*
|
||||
* @throws \Behat\Mink\Exception\ElementNotFoundException
|
||||
* @throws \Behat\Mink\Exception\ExpectationException
|
||||
*/
|
||||
public function testAddWorkFlow() {
|
||||
$assert_session = $this->assertSession();
|
||||
$page = $this->getSession()->getPage();
|
||||
$type_storage = $this->container->get('entity_type.manager')->getStorage('block_content_type');
|
||||
foreach ($type_storage->loadByProperties() as $type) {
|
||||
$type->delete();
|
||||
}
|
||||
|
||||
$this->drupalLogin($this->drupalCreateUser([
|
||||
'access contextual links',
|
||||
'configure any layout',
|
||||
'administer node display',
|
||||
'administer node fields',
|
||||
]));
|
||||
|
||||
// Enable layout builder and overrides.
|
||||
$this->drupalPostForm(
|
||||
static::FIELD_UI_PREFIX . '/display/default',
|
||||
['layout[enabled]' => TRUE, 'layout[allow_custom]' => TRUE],
|
||||
'Save'
|
||||
);
|
||||
|
||||
$layout_default_path = 'admin/structure/types/manage/bundle_with_section_field/display-layout/default';
|
||||
$this->drupalGet($layout_default_path);
|
||||
// Add a basic block with the body field set.
|
||||
$page->clickLink('Add Block');
|
||||
$assert_session->assertWaitOnAjaxRequest();
|
||||
// Confirm that with no block content types the link does not appear.
|
||||
$assert_session->linkNotExists('Create custom block');
|
||||
|
||||
$this->createBlockContentType('basic', 'Basic block');
|
||||
|
||||
$this->drupalGet($layout_default_path);
|
||||
// Add a basic block with the body field set.
|
||||
$page->clickLink('Add Block');
|
||||
$assert_session->assertWaitOnAjaxRequest();
|
||||
// Confirm with only 1 type the "Create custom block" link goes directly t
|
||||
// block add form.
|
||||
$assert_session->linkNotExists('Basic block');
|
||||
$this->clickLink('Create custom block');
|
||||
$assert_session->assertWaitOnAjaxRequest();
|
||||
$assert_session->fieldExists('Title');
|
||||
|
||||
$this->createBlockContentType('advanced', 'Advanced block');
|
||||
|
||||
$this->drupalGet($layout_default_path);
|
||||
// Add a basic block with the body field set.
|
||||
$page->clickLink('Add Block');
|
||||
// Confirm that, when more than 1 type exists, "Create custom block" shows a
|
||||
// list of block types.
|
||||
$assert_session->assertWaitOnAjaxRequest();
|
||||
$assert_session->linkNotExists('Basic block');
|
||||
$assert_session->linkNotExists('Advanced block');
|
||||
$this->clickLink('Create custom block');
|
||||
$assert_session->assertWaitOnAjaxRequest();
|
||||
$assert_session->fieldNotExists('Title');
|
||||
$assert_session->linkExists('Basic block');
|
||||
$assert_session->linkExists('Advanced block');
|
||||
|
||||
$this->clickLink('Advanced block');
|
||||
$assert_session->assertWaitOnAjaxRequest();
|
||||
$assert_session->fieldExists('Title');
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -71,13 +71,7 @@ abstract class InlineBlockTestBase extends WebDriverTestBase {
|
|||
],
|
||||
],
|
||||
]);
|
||||
$bundle = BlockContentType::create([
|
||||
'id' => 'basic',
|
||||
'label' => 'Basic block',
|
||||
'revision' => 1,
|
||||
]);
|
||||
$bundle->save();
|
||||
block_content_add_body_field($bundle->id());
|
||||
$this->createBlockContentType('basic', 'Basic block');
|
||||
|
||||
$this->blockStorage = $this->container->get('entity_type.manager')->getStorage('block_content');
|
||||
}
|
||||
|
@ -146,8 +140,8 @@ abstract class InlineBlockTestBase extends WebDriverTestBase {
|
|||
$page = $this->getSession()->getPage();
|
||||
$page->clickLink('Add Block');
|
||||
$assert_session->assertWaitOnAjaxRequest();
|
||||
$this->assertNotEmpty($assert_session->waitForElementVisible('css', '.block-categories details:contains(Create new block)'));
|
||||
$this->clickLink('Basic block');
|
||||
$this->assertNotEmpty($assert_session->waitForLink('Create custom block'));
|
||||
$this->clickLink('Create custom block');
|
||||
$assert_session->assertWaitOnAjaxRequest();
|
||||
$textarea = $assert_session->waitForElement('css', '[name="settings[block_form][body][0][value]"]');
|
||||
$this->assertNotEmpty($textarea);
|
||||
|
@ -219,4 +213,22 @@ abstract class InlineBlockTestBase extends WebDriverTestBase {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a block content type.
|
||||
*
|
||||
* @param string $id
|
||||
* The block type id.
|
||||
* @param string $label
|
||||
* The block type label.
|
||||
*/
|
||||
protected function createBlockContentType($id, $label) {
|
||||
$bundle = BlockContentType::create([
|
||||
'id' => $id,
|
||||
'label' => $label,
|
||||
'revision' => 1,
|
||||
]);
|
||||
$bundle->save();
|
||||
block_content_add_body_field($bundle->id());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,94 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\layout_builder\FunctionalJavascript;
|
||||
|
||||
use Drupal\FunctionalJavascriptTests\WebDriverTestBase;
|
||||
|
||||
/**
|
||||
* Tests the Layout Builder UI.
|
||||
*
|
||||
* @group layout_builder
|
||||
*/
|
||||
class LayoutBuilderUiTest extends WebDriverTestBase {
|
||||
|
||||
/**
|
||||
* Path prefix for the field UI for the test bundle.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const FIELD_UI_PREFIX = 'admin/structure/types/manage/bundle_with_section_field';
|
||||
|
||||
public static $modules = [
|
||||
'layout_builder',
|
||||
'block',
|
||||
'node',
|
||||
];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
// @todo The Layout Builder UI relies on local tasks; fix in
|
||||
// https://www.drupal.org/project/drupal/issues/2917777.
|
||||
$this->drupalPlaceBlock('local_tasks_block');
|
||||
|
||||
$this->createContentType(['type' => 'bundle_with_section_field']);
|
||||
|
||||
$this->drupalLogin($this->drupalCreateUser([
|
||||
'configure any layout',
|
||||
'administer node display',
|
||||
'administer node fields',
|
||||
]));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the message indicating unsaved changes.
|
||||
*/
|
||||
public function testUnsavedChangesMessage() {
|
||||
$assert_session = $this->assertSession();
|
||||
$page = $this->getSession()->getPage();
|
||||
|
||||
// Enable layout builder.
|
||||
$this->drupalPostForm(
|
||||
static::FIELD_UI_PREFIX . '/display/default',
|
||||
['layout[enabled]' => TRUE],
|
||||
'Save'
|
||||
);
|
||||
|
||||
// Make and then cancel changes.
|
||||
$this->assertModifiedLayout(static::FIELD_UI_PREFIX . '/display-layout/default');
|
||||
$page->clickLink('Cancel Layout');
|
||||
$assert_session->pageTextNotContains('You have unsaved changes.');
|
||||
|
||||
// Make and then save changes.
|
||||
$this->assertModifiedLayout(static::FIELD_UI_PREFIX . '/display-layout/default');
|
||||
$page->clickLink('Save Layout');
|
||||
$assert_session->pageTextNotContains('You have unsaved changes.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that modifying a layout works as expected.
|
||||
*
|
||||
* @param string $path
|
||||
* The path to a Layout Builder UI page.
|
||||
*/
|
||||
protected function assertModifiedLayout($path) {
|
||||
$assert_session = $this->assertSession();
|
||||
$page = $this->getSession()->getPage();
|
||||
|
||||
$this->drupalGet($path);
|
||||
$page->clickLink('Add Section');
|
||||
$assert_session->assertWaitOnAjaxRequest();
|
||||
$assert_session->pageTextNotContains('You have unsaved changes.');
|
||||
$page->clickLink('One column');
|
||||
$assert_session->assertWaitOnAjaxRequest();
|
||||
$assert_session->pageTextContainsOnce('You have unsaved changes.');
|
||||
|
||||
// Reload the page.
|
||||
$this->drupalGet($path);
|
||||
$assert_session->pageTextContainsOnce('You have unsaved changes.');
|
||||
}
|
||||
|
||||
}
|
|
@ -230,11 +230,10 @@ class FieldBlockTest extends EntityKernelTestBase {
|
|||
* @covers ::build
|
||||
* @dataProvider providerTestBuild
|
||||
*/
|
||||
public function testBuild(PromiseInterface $promise, $in_preview, $expected_markup, $log_message = '', $log_arguments = []) {
|
||||
public function testBuild(PromiseInterface $promise, $expected_markup, $log_message = '', $log_arguments = []) {
|
||||
$entity = $this->prophesize(FieldableEntityInterface::class);
|
||||
$field = $this->prophesize(FieldItemListInterface::class);
|
||||
$entity->get('the_field_name')->willReturn($field->reveal());
|
||||
$entity->in_preview = $in_preview;
|
||||
$field->view(Argument::type('array'))->will($promise);
|
||||
|
||||
$field_definition = $this->prophesize(FieldDefinitionInterface::class);
|
||||
|
@ -269,40 +268,20 @@ class FieldBlockTest extends EntityKernelTestBase {
|
|||
*/
|
||||
public function providerTestBuild() {
|
||||
$data = [];
|
||||
$data['array, no preview'] = [
|
||||
$data['array'] = [
|
||||
new ReturnPromise([['content' => ['#markup' => 'The field value']]]),
|
||||
FALSE,
|
||||
'The field value',
|
||||
];
|
||||
$data['array, preview'] = [
|
||||
new ReturnPromise([['content' => ['#markup' => 'The field value']]]),
|
||||
TRUE,
|
||||
'The field value',
|
||||
];
|
||||
$data['empty array, no preview'] = [
|
||||
$data['empty array'] = [
|
||||
new ReturnPromise([[]]),
|
||||
FALSE,
|
||||
'',
|
||||
];
|
||||
$data['empty array, preview'] = [
|
||||
new ReturnPromise([[]]),
|
||||
TRUE,
|
||||
'Placeholder for the "The Field Label" field',
|
||||
];
|
||||
$data['exception, no preview'] = [
|
||||
$data['exception'] = [
|
||||
new ThrowPromise(new \Exception('The exception message')),
|
||||
FALSE,
|
||||
'',
|
||||
'The field "%field" failed to render with the error of "%error".',
|
||||
['%field' => 'the_field_name', '%error' => 'The exception message'],
|
||||
];
|
||||
$data['exception, preview'] = [
|
||||
new ThrowPromise(new \Exception('The exception message')),
|
||||
TRUE,
|
||||
'Placeholder for the "The Field Label" field',
|
||||
'The field "%field" failed to render with the error of "%error".',
|
||||
['%field' => 'the_field_name', '%error' => 'The exception message'],
|
||||
];
|
||||
return $data;
|
||||
}
|
||||
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
namespace Drupal\Tests\layout_builder\Kernel;
|
||||
|
||||
use Drupal\layout_builder\Plugin\SectionStorage\OverridesSectionStorage;
|
||||
use Drupal\layout_builder\Section;
|
||||
|
||||
/**
|
||||
|
@ -56,7 +57,7 @@ class LayoutBuilderFieldLayoutCompatibilityTest extends LayoutBuilderCompatibili
|
|||
// Add a layout override.
|
||||
$this->enableOverrides();
|
||||
/** @var \Drupal\layout_builder\SectionStorageInterface $field_list */
|
||||
$field_list = $this->entity->get('layout_builder__layout');
|
||||
$field_list = $this->entity->get(OverridesSectionStorage::FIELD_NAME);
|
||||
$field_list->appendSection(new Section('layout_onecol'));
|
||||
$this->entity->save();
|
||||
|
||||
|
|
|
@ -4,6 +4,7 @@ namespace Drupal\Tests\layout_builder\Kernel;
|
|||
|
||||
use Drupal\field\Entity\FieldConfig;
|
||||
use Drupal\field\Entity\FieldStorageConfig;
|
||||
use Drupal\layout_builder\Plugin\SectionStorage\OverridesSectionStorage;
|
||||
use Drupal\layout_builder\Section;
|
||||
|
||||
/**
|
||||
|
@ -35,7 +36,7 @@ class LayoutBuilderInstallTest extends LayoutBuilderCompatibilityTestBase {
|
|||
// Add a layout override.
|
||||
$this->enableOverrides();
|
||||
$this->entity = $this->reloadEntity($this->entity);
|
||||
$this->entity->get('layout_builder__layout')->appendSection(new Section('layout_onecol'));
|
||||
$this->entity->get(OverridesSectionStorage::FIELD_NAME)->appendSection(new Section('layout_onecol'));
|
||||
$this->entity->save();
|
||||
|
||||
// The rendered entity has now changed. The non-configurable field is shown
|
||||
|
@ -50,7 +51,7 @@ class LayoutBuilderInstallTest extends LayoutBuilderCompatibilityTestBase {
|
|||
$this->assertNotEmpty($this->cssSelect('.layout--onecol'));
|
||||
|
||||
// Removing the layout restores the original rendering of the entity.
|
||||
$this->entity->get('layout_builder__layout')->removeSection(0);
|
||||
$this->entity->get(OverridesSectionStorage::FIELD_NAME)->removeSection(0);
|
||||
$this->entity->save();
|
||||
$this->assertFieldAttributes($this->entity, $expected_fields);
|
||||
|
||||
|
|
|
@ -4,6 +4,7 @@ namespace Drupal\Tests\layout_builder\Kernel;
|
|||
|
||||
use Drupal\entity_test\Entity\EntityTestBaseFieldDisplay;
|
||||
use Drupal\layout_builder\Entity\LayoutBuilderEntityViewDisplay;
|
||||
use Drupal\layout_builder\Plugin\SectionStorage\OverridesSectionStorage;
|
||||
|
||||
/**
|
||||
* Tests the field type for Layout Sections.
|
||||
|
@ -42,10 +43,10 @@ class LayoutSectionItemListTest extends SectionStorageTestBase {
|
|||
}, $section_data);
|
||||
$entity = EntityTestBaseFieldDisplay::create([
|
||||
'name' => 'The test entity',
|
||||
'layout_builder__layout' => $section_data,
|
||||
OverridesSectionStorage::FIELD_NAME => $section_data,
|
||||
]);
|
||||
$entity->save();
|
||||
return $entity->get('layout_builder__layout');
|
||||
return $entity->get(OverridesSectionStorage::FIELD_NAME);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -137,6 +137,17 @@ abstract class SectionStorageTestBase extends EntityKernelTestBase {
|
|||
$this->assertSections($expected);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests __clone().
|
||||
*/
|
||||
public function testClone() {
|
||||
$this->assertSame([], $this->sectionStorage->getSection(0)->getLayoutSettings());
|
||||
|
||||
$new_section_storage = clone $this->sectionStorage;
|
||||
$new_section_storage->getSection(0)->setLayoutSettings(['asdf' => 'qwer']);
|
||||
$this->assertSame([], $this->sectionStorage->getSection(0)->getLayoutSettings());
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that the field list has the expected sections.
|
||||
*
|
||||
|
|
|
@ -11,6 +11,7 @@ use Drupal\Core\Cache\Cache;
|
|||
use Drupal\Core\DependencyInjection\ContainerBuilder;
|
||||
use Drupal\Core\Entity\EntityInterface;
|
||||
use Drupal\Core\Plugin\Context\ContextHandlerInterface;
|
||||
use Drupal\Core\Render\PreviewFallbackInterface;
|
||||
use Drupal\Core\Session\AccountInterface;
|
||||
use Drupal\layout_builder\Access\LayoutPreviewAccessAllowed;
|
||||
use Drupal\layout_builder\Event\SectionComponentBuildRenderArrayEvent;
|
||||
|
@ -252,6 +253,98 @@ class BlockComponentRenderArrayTest extends UnitTestCase {
|
|||
$this->assertEquals($expected_cache, $result);
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::onBuildRender
|
||||
*/
|
||||
public function testOnBuildRenderInPreviewEmptyBuild() {
|
||||
$block = $this->prophesize(BlockPluginInterface::class)->willImplement(PreviewFallbackInterface::class);
|
||||
|
||||
$block->access($this->account->reveal(), TRUE)->shouldNotBeCalled();
|
||||
$block->getCacheContexts()->willReturn([]);
|
||||
$block->getCacheTags()->willReturn(['test']);
|
||||
$block->getCacheMaxAge()->willReturn(Cache::PERMANENT);
|
||||
$block->getConfiguration()->willReturn([]);
|
||||
$block->getPluginId()->willReturn('block_plugin_id');
|
||||
$block->getBaseId()->willReturn('block_plugin_id');
|
||||
$block->getDerivativeId()->willReturn(NULL);
|
||||
$placeholder_string = 'The placeholder string';
|
||||
$block->getPreviewFallbackString()->willReturn($placeholder_string);
|
||||
|
||||
$block_content = [];
|
||||
$block->build()->willReturn($block_content);
|
||||
$this->blockManager->createInstance('some_block_id', ['id' => 'some_block_id'])->willReturn($block->reveal());
|
||||
|
||||
$component = new SectionComponent('some-uuid', 'some-region', ['id' => 'some_block_id']);
|
||||
$event = new SectionComponentBuildRenderArrayEvent($component, [], TRUE);
|
||||
|
||||
$subscriber = new BlockComponentRenderArray($this->account->reveal());
|
||||
|
||||
$expected_build = [
|
||||
'#theme' => 'block',
|
||||
'#weight' => 0,
|
||||
'#configuration' => [],
|
||||
'#plugin_id' => 'block_plugin_id',
|
||||
'#base_plugin_id' => 'block_plugin_id',
|
||||
'#derivative_plugin_id' => NULL,
|
||||
'content' => $block_content,
|
||||
];
|
||||
$expected_build['content']['#markup'] = $placeholder_string;
|
||||
|
||||
$expected_cache = $expected_build + [
|
||||
'#cache' => [
|
||||
'contexts' => [],
|
||||
'tags' => ['test'],
|
||||
'max-age' => 0,
|
||||
],
|
||||
];
|
||||
|
||||
$subscriber->onBuildRender($event);
|
||||
$result = $event->getBuild();
|
||||
$this->assertEquals($expected_build, $result);
|
||||
$event->getCacheableMetadata()->applyTo($result);
|
||||
$this->assertEquals($expected_cache, $result);
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::onBuildRender
|
||||
*/
|
||||
public function testOnBuildRenderEmptyBuild() {
|
||||
$block = $this->prophesize(BlockPluginInterface::class);
|
||||
$access_result = AccessResult::allowed();
|
||||
$block->access($this->account->reveal(), TRUE)->willReturn($access_result)->shouldBeCalled();
|
||||
$block->getCacheContexts()->willReturn([]);
|
||||
$block->getCacheTags()->willReturn(['test']);
|
||||
$block->getCacheMaxAge()->willReturn(Cache::PERMANENT);
|
||||
$block->getConfiguration()->willReturn([]);
|
||||
$block->getPluginId()->willReturn('block_plugin_id');
|
||||
$block->getBaseId()->willReturn('block_plugin_id');
|
||||
$block->getDerivativeId()->willReturn(NULL);
|
||||
|
||||
$block->build()->willReturn([]);
|
||||
$this->blockManager->createInstance('some_block_id', ['id' => 'some_block_id'])->willReturn($block->reveal());
|
||||
|
||||
$component = new SectionComponent('some-uuid', 'some-region', ['id' => 'some_block_id']);
|
||||
$event = new SectionComponentBuildRenderArrayEvent($component, [], FALSE);
|
||||
|
||||
$subscriber = new BlockComponentRenderArray($this->account->reveal());
|
||||
|
||||
$expected_build = [];
|
||||
|
||||
$expected_cache = $expected_build + [
|
||||
'#cache' => [
|
||||
'contexts' => [],
|
||||
'tags' => ['test'],
|
||||
'max-age' => -1,
|
||||
],
|
||||
];
|
||||
|
||||
$subscriber->onBuildRender($event);
|
||||
$result = $event->getBuild();
|
||||
$this->assertEquals($expected_build, $result);
|
||||
$event->getCacheableMetadata()->applyTo($result);
|
||||
$this->assertEquals($expected_cache, $result);
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::onBuildRender
|
||||
*/
|
||||
|
|
|
@ -16,6 +16,7 @@ class LayoutTempstoreRepositoryTest extends UnitTestCase {
|
|||
|
||||
/**
|
||||
* @covers ::get
|
||||
* @covers ::has
|
||||
*/
|
||||
public function testGetEmptyTempstore() {
|
||||
$section_storage = $this->prophesize(SectionStorageInterface::class);
|
||||
|
@ -30,12 +31,15 @@ class LayoutTempstoreRepositoryTest extends UnitTestCase {
|
|||
|
||||
$repository = new LayoutTempstoreRepository($tempstore_factory->reveal());
|
||||
|
||||
$this->assertFalse($repository->has($section_storage->reveal()));
|
||||
|
||||
$result = $repository->get($section_storage->reveal());
|
||||
$this->assertSame($section_storage->reveal(), $result);
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::get
|
||||
* @covers ::has
|
||||
*/
|
||||
public function testGetLoadedTempstore() {
|
||||
$section_storage = $this->prophesize(SectionStorageInterface::class);
|
||||
|
@ -50,6 +54,8 @@ class LayoutTempstoreRepositoryTest extends UnitTestCase {
|
|||
|
||||
$repository = new LayoutTempstoreRepository($tempstore_factory->reveal());
|
||||
|
||||
$this->assertTrue($repository->has($section_storage->reveal()));
|
||||
|
||||
$result = $repository->get($section_storage->reveal());
|
||||
$this->assertSame($tempstore_section_storage->reveal(), $result);
|
||||
$this->assertNotSame($section_storage->reveal(), $result);
|
||||
|
|
|
@ -112,13 +112,13 @@ class OverridesSectionStorageTest extends UnitTestCase {
|
|||
$entity_storage = $this->prophesize(EntityStorageInterface::class);
|
||||
|
||||
$entity_without_layout = $this->prophesize(FieldableEntityInterface::class);
|
||||
$entity_without_layout->hasField('layout_builder__layout')->willReturn(FALSE);
|
||||
$entity_without_layout->get('layout_builder__layout')->shouldNotBeCalled();
|
||||
$entity_without_layout->hasField(OverridesSectionStorage::FIELD_NAME)->willReturn(FALSE);
|
||||
$entity_without_layout->get(OverridesSectionStorage::FIELD_NAME)->shouldNotBeCalled();
|
||||
$entity_storage->load('entity_without_layout')->willReturn($entity_without_layout->reveal());
|
||||
|
||||
$entity_with_layout = $this->prophesize(FieldableEntityInterface::class);
|
||||
$entity_with_layout->hasField('layout_builder__layout')->willReturn(TRUE);
|
||||
$entity_with_layout->get('layout_builder__layout')->willReturn('the_return_value');
|
||||
$entity_with_layout->hasField(OverridesSectionStorage::FIELD_NAME)->willReturn(TRUE);
|
||||
$entity_with_layout->get(OverridesSectionStorage::FIELD_NAME)->willReturn('the_return_value');
|
||||
$entity_storage->load('entity_with_layout')->willReturn($entity_with_layout->reveal());
|
||||
|
||||
$this->entityTypeManager->getStorage($expected_entity_type_id)->willReturn($entity_storage->reveal());
|
||||
|
|
|
@ -231,6 +231,7 @@ class SectionRenderTest extends UnitTestCase {
|
|||
* @covers ::toRenderArray
|
||||
*/
|
||||
public function testContextAwareBlock() {
|
||||
$block_content = ['#markup' => 'The block content.'];
|
||||
$render_array = [
|
||||
'#theme' => 'block',
|
||||
'#weight' => 0,
|
||||
|
@ -238,7 +239,7 @@ class SectionRenderTest extends UnitTestCase {
|
|||
'#plugin_id' => 'block_plugin_id',
|
||||
'#base_plugin_id' => 'block_plugin_id',
|
||||
'#derivative_plugin_id' => NULL,
|
||||
'content' => [],
|
||||
'content' => $block_content,
|
||||
'#cache' => [
|
||||
'contexts' => [],
|
||||
'tags' => [],
|
||||
|
@ -251,7 +252,7 @@ class SectionRenderTest extends UnitTestCase {
|
|||
|
||||
$access_result = AccessResult::allowed();
|
||||
$block->access($this->account->reveal(), TRUE)->willReturn($access_result);
|
||||
$block->build()->willReturn([]);
|
||||
$block->build()->willReturn($block_content);
|
||||
$block->getCacheContexts()->willReturn([]);
|
||||
$block->getCacheTags()->willReturn([]);
|
||||
$block->getCacheMaxAge()->willReturn(Cache::PERMANENT);
|
||||
|
|
Reference in a new issue