diff --git a/core/config/schema/core.entity.schema.yml b/core/config/schema/core.entity.schema.yml index df2a2fd..139fd40 100644 --- a/core/config/schema/core.entity.schema.yml +++ b/core/config/schema/core.entity.schema.yml @@ -78,6 +78,27 @@ core.entity_view_display.*.*.*: label: 'Third party settings' sequence: type: field.formatter.third_party.[%key] + blocks: + type: sequence + label: 'Block fields' + sequence: + type: mapping + mapping: + machine_name: + type: string + label: 'ID' + plugin_id: + type: string + label: 'Plugin ID' + weight: + type: integer + label: 'Weight' + region: + type: string + label: 'Region' + settings: + type: block.settings.[id] + hidden: type: sequence label: 'Field display setting' diff --git a/core/lib/Drupal/Core/Entity/Display/EntityViewDisplayInterface.php b/core/lib/Drupal/Core/Entity/Display/EntityViewDisplayInterface.php index 3598b51..a930f30 100644 --- a/core/lib/Drupal/Core/Entity/Display/EntityViewDisplayInterface.php +++ b/core/lib/Drupal/Core/Entity/Display/EntityViewDisplayInterface.php @@ -46,4 +46,24 @@ public function build(FieldableEntityInterface $entity); */ public function buildMultiple(array $entities); + /** + * @todo. + */ + public function getBlocks(); + + /** + * @todo. + */ + public function getBlock($name); + + /** + * @todo. + */ + public function removeBlock($name); + + /** + * @todo. + */ + public function setBlock($name, $block); + } diff --git a/core/lib/Drupal/Core/Entity/Entity/EntityViewDisplay.php b/core/lib/Drupal/Core/Entity/Entity/EntityViewDisplay.php index 74b15b6..4a26e8e 100644 --- a/core/lib/Drupal/Core/Entity/Entity/EntityViewDisplay.php +++ b/core/lib/Drupal/Core/Entity/Entity/EntityViewDisplay.php @@ -8,6 +8,7 @@ use Drupal\Core\Entity\EntityStorageInterface; use Drupal\Core\Entity\FieldableEntityInterface; use Drupal\Core\Entity\EntityDisplayBase; +use Drupal\Core\Plugin\ContextAwarePluginInterface; use Drupal\Core\TypedData\TranslatableInterface; /** @@ -27,6 +28,7 @@ * "bundle", * "mode", * "content", + * "blocks", * "hidden", * } * ) @@ -39,6 +41,44 @@ class EntityViewDisplay extends EntityDisplayBase implements EntityViewDisplayIn protected $displayContext = 'view'; /** + * @todo. + * + * @var array + */ + protected $blocks = []; + + /** + * {@inheritdoc} + */ + public function getBlocks() { + return $this->blocks; + } + + /** + * {@inheritdoc} + */ + public function getBlock($name) { + $blocks = $this->getBlocks(); + return isset($blocks[$name]) ? $blocks[$name] : NULL; + } + + /** + * {@inheritdoc} + */ + public function removeBlock($name) { + unset($this->blocks[$name]); + return $this; + } + + /** + * {@inheritdoc} + */ + public function setBlock($name, $block) { + $this->blocks[$name] = $block; + return $this; + } + + /** * Returns the display objects used to render a set of entities. * * Depending on the configuration of the view mode for each bundle, this can @@ -263,6 +303,24 @@ public function buildMultiple(array $entities) { } } + foreach ($this->getBlocks() as $name => $block_info) { + if ($block_info['region'] !== 'hidden') { + /** @var \Drupal\Core\Block\BlockPluginInterface $block */ + $block = \Drupal::service('plugin.manager.block')->createInstance($block_info['plugin_id'], $block_info['settings']); + + if ($block instanceof ContextAwarePluginInterface) { + $contexts = \Drupal::service('context.repository')->getRuntimeContexts($block->getContextMapping()); + \Drupal::service('context.handler')->applyContextMapping($block, $contexts); + } + + $block_access = $block->access(\Drupal::currentUser(), TRUE); + foreach ($entities as $id => $entity) { + $build_list[$id][$name] = $block_access->isAllowed() ? $block->build() : []; + $this->renderer->addCacheableDependency($build_list[$id][$name], $block_access); + } + } + } + foreach ($entities as $id => $entity) { // Assign the configured weights. foreach ($this->getComponents() as $name => $options) { diff --git a/core/modules/aggregator/config/install/core.entity_view_display.aggregator_feed.aggregator_feed.default.yml b/core/modules/aggregator/config/install/core.entity_view_display.aggregator_feed.aggregator_feed.default.yml index e0232cf..1b8a7e1 100644 --- a/core/modules/aggregator/config/install/core.entity_view_display.aggregator_feed.aggregator_feed.default.yml +++ b/core/modules/aggregator/config/install/core.entity_view_display.aggregator_feed.aggregator_feed.default.yml @@ -34,5 +34,6 @@ content: settings: { } third_party_settings: { } label: inline +blocks: { } hidden: more_link: true diff --git a/core/modules/aggregator/config/install/core.entity_view_display.aggregator_feed.aggregator_feed.summary.yml b/core/modules/aggregator/config/install/core.entity_view_display.aggregator_feed.aggregator_feed.summary.yml index 5e5e468..7128751 100644 --- a/core/modules/aggregator/config/install/core.entity_view_display.aggregator_feed.aggregator_feed.summary.yml +++ b/core/modules/aggregator/config/install/core.entity_view_display.aggregator_feed.aggregator_feed.summary.yml @@ -16,6 +16,7 @@ content: more_link: weight: 1 region: content +blocks: { } hidden: checked: true description: true diff --git a/core/modules/aggregator/config/install/core.entity_view_display.aggregator_item.aggregator_item.summary.yml b/core/modules/aggregator/config/install/core.entity_view_display.aggregator_item.aggregator_item.summary.yml index 8e29395..7b40b0c 100644 --- a/core/modules/aggregator/config/install/core.entity_view_display.aggregator_item.aggregator_item.summary.yml +++ b/core/modules/aggregator/config/install/core.entity_view_display.aggregator_item.aggregator_item.summary.yml @@ -13,6 +13,7 @@ content: timestamp: weight: 0 region: content +blocks: { } hidden: author: true description: true diff --git a/core/modules/book/config/optional/core.entity_view_display.node.book.default.yml b/core/modules/book/config/optional/core.entity_view_display.node.book.default.yml index d6ef64d..5aafb59 100644 --- a/core/modules/book/config/optional/core.entity_view_display.node.book.default.yml +++ b/core/modules/book/config/optional/core.entity_view_display.node.book.default.yml @@ -22,4 +22,5 @@ content: links: weight: 101 region: content +blocks: { } hidden: { } diff --git a/core/modules/book/config/optional/core.entity_view_display.node.book.teaser.yml b/core/modules/book/config/optional/core.entity_view_display.node.book.teaser.yml index 77a62c3..14ae374 100644 --- a/core/modules/book/config/optional/core.entity_view_display.node.book.teaser.yml +++ b/core/modules/book/config/optional/core.entity_view_display.node.book.teaser.yml @@ -24,4 +24,5 @@ content: links: weight: 101 region: content +blocks: { } hidden: { } diff --git a/core/modules/field_layout/tests/src/Kernel/FieldLayoutEntityDisplayTest.php b/core/modules/field_layout/tests/src/Kernel/FieldLayoutEntityDisplayTest.php index 3ca5e59..65ef50a 100644 --- a/core/modules/field_layout/tests/src/Kernel/FieldLayoutEntityDisplayTest.php +++ b/core/modules/field_layout/tests/src/Kernel/FieldLayoutEntityDisplayTest.php @@ -56,6 +56,7 @@ public function testPreSave() { 'type' => 'hidden', ], ], + 'blocks' => [], 'hidden' => [], ]; $this->assertEntityValues($expected, $entity_display->toArray()); diff --git a/core/modules/field_ui/field_ui.module b/core/modules/field_ui/field_ui.module index 79b3f15..324942e 100644 --- a/core/modules/field_ui/field_ui.module +++ b/core/modules/field_ui/field_ui.module @@ -11,6 +11,7 @@ use Drupal\Core\Entity\EntityViewModeInterface; use Drupal\Core\Entity\EntityFormModeInterface; use Drupal\Core\Url; +use Drupal\field_ui\Form\EntityViewDisplayAddBlockForm; use Drupal\field_ui\FieldUI; use Drupal\field_ui\Plugin\Derivative\FieldUiLocalTask; @@ -81,6 +82,7 @@ function field_ui_entity_type_build(array &$entity_types) { $entity_types['entity_form_display']->setFormClass('edit', 'Drupal\field_ui\Form\EntityFormDisplayEditForm'); $entity_types['entity_view_display']->setFormClass('edit', 'Drupal\field_ui\Form\EntityViewDisplayEditForm'); + $entity_types['entity_view_display']->setFormClass('add_block', EntityViewDisplayAddBlockForm::class); $form_mode = $entity_types['entity_form_mode']; $form_mode->setListBuilderClass('Drupal\field_ui\EntityFormModeListBuilder'); diff --git a/core/modules/field_ui/src/Form/EntityViewDisplayAddBlockForm.php b/core/modules/field_ui/src/Form/EntityViewDisplayAddBlockForm.php new file mode 100644 index 0000000..ba6e2d0 --- /dev/null +++ b/core/modules/field_ui/src/Form/EntityViewDisplayAddBlockForm.php @@ -0,0 +1,154 @@ +blockManager = $block_manager; + $this->contextRepository = $context_repository; + } + + /** + * {@inheritdoc} + */ + public static function create(ContainerInterface $container) { + return new static( + $container->get('plugin.manager.block'), + $container->get('context.repository'), + $container->get('entity_field.manager') + ); + } + + /** + * {@inheritdoc} + */ + public function getEntityFromRouteMatch(RouteMatchInterface $route_match, $entity_type_id) { + $route_parameters = $route_match->getParameters()->all(); + + return entity_get_display($route_parameters['entity_type_id'], $route_parameters['bundle'], $route_parameters['view_mode_name']); + } + + /** + * {@inheritdoc} + */ + public function form(array $form, FormStateInterface $form_state) { + $definitions = $this->blockManager->getDefinitionsForContexts($this->contextRepository->getAvailableContexts()); + $definitions = $this->blockManager->getSortedDefinitions($definitions); + + $options = []; + foreach ($definitions as $plugin_id => $plugin_definition) { + $options[$plugin_id] = [ + 'title' => $plugin_definition['admin_label'], + 'category' => $plugin_definition['category'], + ]; + } + + $form['blocks'] = [ + '#type' => 'tableselect', + '#header' => [ + 'title' => $this->t('Block'), + 'category' => $this->t('Category'), + ], + '#options' => $options, + '#js_select' => FALSE, + '#empty' => $this->t('No blocks available.'), + '#element_validate' => [[$this, 'validateBlockValues']], + ]; + + return $form; + } + + /** + * Transforms the list of new block IDs into full block info arrays. + */ + public function validateBlockValues($element, FormStateInterface $form_state) { + $new_block_ids = array_keys(array_filter($form_state->getValue($element['#parents']))); + $existing_blocks = $this->entity->getBlocks(); + foreach ($new_block_ids as $block_id) { + /** @var \Drupal\Core\Block\BlockPluginInterface $block */ + $block = $this->blockManager->createInstance($block_id); + + // Determine a unique machine name. + $suggestion = $block->getMachineNameSuggestion(); + $count = 1; + $machine_name = $suggestion; + while (isset($existing_blocks[$machine_name])) { + $machine_name = $suggestion . '_' . ++$count; + } + + $existing_blocks[$machine_name] = [ + 'machine_name' => $machine_name, + 'plugin_id' => $block_id, + 'settings' => $block->getConfiguration(), + 'region' => 'content', + 'weight' => 0, + ]; + } + $form_state->setValueForElement($element, $existing_blocks); + } + + /** + * {@inheritdoc} + */ + public function submitForm(array &$form, FormStateInterface $form_state) { + parent::submitForm($form, $form_state); + + $entity_type = $this->entityTypeManager->getDefinition($this->entity->getTargetEntityTypeId()); + $parameters = ['view_mode_name' => $this->entity->getMode()] + FieldUI::getRouteBundleParameter($entity_type, $this->entity->getTargetBundle()); + $form_state->setRedirect('entity.entity_view_display.' . $this->entity->getTargetEntityTypeId() . '.view_mode', $parameters); + + drupal_set_message($this->t('Your settings have been saved.')); + } + + /** + * {@inheritdoc} + */ + protected function actions(array $form, FormStateInterface $form_state) { + $actions = parent::actions($form, $form_state); + $actions['submit']['#value'] = $this->t('Add block'); + return $actions; + } + +} diff --git a/core/modules/field_ui/src/Form/EntityViewDisplayEditForm.php b/core/modules/field_ui/src/Form/EntityViewDisplayEditForm.php index c27f3d4..1ff11e0 100644 --- a/core/modules/field_ui/src/Form/EntityViewDisplayEditForm.php +++ b/core/modules/field_ui/src/Form/EntityViewDisplayEditForm.php @@ -2,9 +2,16 @@ namespace Drupal\field_ui\Form; +use Drupal\Component\Plugin\ContextAwarePluginInterface; +use Drupal\Component\Plugin\PluginManagerBase; +use Drupal\Component\Serialization\Json; +use Drupal\Core\Block\BlockManagerInterface; +use Drupal\Core\Entity\EntityInterface; use Drupal\Core\Field\FieldDefinitionInterface; +use Drupal\Core\Field\FieldTypePluginManagerInterface; use Drupal\Core\Field\PluginSettingsInterface; use Drupal\Core\Form\FormStateInterface; +use Drupal\Core\Form\SubformState; use Drupal\Core\Url; use Drupal\field_ui\FieldUI; use Symfony\Component\DependencyInjection\ContainerInterface; @@ -15,6 +22,13 @@ class EntityViewDisplayEditForm extends EntityDisplayFormBase { /** + * The entity being used by this form. + * + * @var \Drupal\Core\Entity\Display\EntityViewDisplayInterface + */ + protected $entity; + + /** * {@inheritdoc} */ protected $displayContext = 'view'; @@ -83,6 +97,236 @@ protected function buildExtraFieldRow($field_id, $extra_field) { /** * {@inheritdoc} */ + public function form(array $form, FormStateInterface $form_state) { + $form = parent::form($form, $form_state); + + $form_state->setTemporaryValue('gathered_contexts', \Drupal::service('context.repository')->getAvailableContexts()); + foreach ($this->entity->getBlocks() as $block_id => $block_info) { + $form['fields'][$block_id] = $this->buildBlockFieldRow($block_id, $block_info, $form, $form_state); + } + + $entity_type = $this->entityTypeManager->getDefinition($this->entity->getTargetEntityTypeId()); + $parameters = ['view_mode_name' => $this->entity->getMode()] + FieldUI::getRouteBundleParameter($entity_type, $this->entity->getTargetBundle()); + $url = Url::fromRoute('entity.entity_view_display.' . $this->entity->getTargetEntityTypeId() . '.add_block', $parameters); + $form['add_block'] = [ + '#weight' => -100, + '#type' => 'link', + '#title' => $this->t('Add field block'), + '#url' => $url, + '#attributes' => [ + 'class' => ['use-ajax', 'button', 'button--small'], + 'data-dialog-type' => 'modal', + 'data-dialog-options' => Json::encode([ + 'width' => 700, + ]), + ], + ]; + return $form; + } + + /** + * @todo. + */ + protected function getRouteBundleParameters($mode) { + $entity_type = $this->entityTypeManager->getDefinition($this->entity->getTargetEntityTypeId()); + return ['view_mode_name' => $mode] + FieldUI::getRouteBundleParameter($entity_type, $this->entity->getTargetBundle()); + } + + /** + * @todo. + */ + protected function buildBlockFieldRow($block_name, $block_info, $form, FormStateInterface $form_state) { + /** @var \Drupal\Core\Block\BlockPluginInterface $block */ + $block_manager = \Drupal::service('plugin.manager.block'); + $block = $block_manager->createInstance($block_info['plugin_id'], $block_info['settings']); + + $regions = array_keys($this->getRegions()); + $label = $block->label(); + $block_field_row = [ + '#attributes' => ['class' => ['draggable', 'tabledrag-leaf']], + '#row_type' => 'field', + '#region_callback' => [$this, 'getRowRegion'], + '#js_settings' => ['rowHandler' => 'field'], + 'human_name' => [ + '#markup' => $label, + ], + 'weight' => [ + '#type' => 'textfield', + '#title' => $this->t('Weight for @title', ['@title' => $label]), + '#title_display' => 'invisible', + '#default_value' => $block_info ? $block_info['weight'] : 0, + '#size' => 3, + '#attributes' => ['class' => ['field-weight']], + ], + 'parent_wrapper' => [ + 'parent' => [ + '#type' => 'select', + '#title' => $this->t('Parents for @title', ['@title' => $label]), + '#title_display' => 'invisible', + '#options' => array_combine($regions, $regions), + '#empty_value' => '', + '#attributes' => ['class' => ['js-field-parent', 'field-parent']], + '#parents' => ['fields', $block_name, 'parent'], + ], + 'hidden_name' => [ + '#type' => 'hidden', + '#default_value' => $block_name, + '#attributes' => ['class' => ['field-name']], + ], + ], + 'region' => [ + '#type' => 'select', + '#title' => $this->t('Region for @title', ['@title' => $label]), + '#title_display' => 'invisible', + '#options' => $this->getRegionOptions(), + '#default_value' => $block_info ? $block_info['region'] : 'visible', + '#attributes' => ['class' => ['field-region']], + ], + 'empty_cell' => [ + '#markup' => ' ', + ], + 'plugin' => [ + 'type' => [ + '#type' => 'hidden', + '#value' => $block_info ? 'visible' : 'hidden', + '#parents' => ['fields', $block_name, 'type'], + '#attributes' => ['class' => ['field-plugin-type']], + ], + ], + 'settings_summary' => [], + 'settings_edit' => [], + ]; + + // Base button element for the various plugin settings actions. + $base_button = [ + '#submit' => ['::multistepSubmit'], + '#ajax' => [ + 'callback' => '::multistepAjax', + 'wrapper' => 'field-display-overview-wrapper', + 'effect' => 'fade', + ], + '#field_name' => $block_name, + ]; + + if ($form_state->get('plugin_settings_edit') == $block_name) { + // Generate the settings form and allow other modules to alter it. + $block_field_row['plugin']['#cell_attributes'] = ['colspan' => 3]; + $block_field_row['plugin']['settings_edit_form'] = [ + '#type' => 'container', + '#attributes' => ['class' => ['field-plugin-settings-edit-form']], + '#parents' => ['fields', $block_name, 'settings_edit_form'], + 'settings' => [], + 'actions' => [ + '#type' => 'actions', + 'save_settings' => $base_button + [ + '#type' => 'submit', + '#button_type' => 'primary', + '#name' => $block_name . '_plugin_settings_update', + '#value' => $this->t('Update'), + '#op' => 'update', + ], + 'cancel_settings' => $base_button + [ + '#type' => 'submit', + '#name' => $block_name . '_plugin_settings_cancel', + '#value' => $this->t('Cancel'), + '#op' => 'cancel', + // Do not check errors for the 'Cancel' button, but make sure we + // get the value of the 'plugin type' select. + '#limit_validation_errors' => [['fields', $block_name, 'type']], + ], + 'remove_settings' => $base_button + [ + '#type' => 'submit', + '#button_type' => 'danger', + '#name' => $block_name . '_plugin_settings_remove', + '#value' => $this->t('Remove'), + '#op' => 'remove', + // Do not check errors for the 'Remove' button. + '#limit_validation_errors' => [], + ], + ], + ]; + $block_field_row['#attributes']['class'][] = 'field-plugin-settings-editing'; + $subform_state = SubformState::createForSubform($block_field_row['plugin']['settings_edit_form']['settings'], $form, $form_state); + $block_field_row['plugin']['settings_edit_form']['settings'] = $block->buildConfigurationForm($block_field_row['plugin']['settings_edit_form']['settings'], $subform_state); + } + else { + // Check selected plugin settings to display edit link or not. + $block_field_row['settings_edit'] = $base_button + [ + '#type' => 'image_button', + '#name' => $block_name . '_settings_edit', + '#src' => 'core/misc/icons/787878/cog.svg', + '#attributes' => ['class' => ['field-plugin-settings-edit'], 'alt' => $this->t('Edit')], + '#op' => 'edit', + // Do not check errors for the 'Edit' button, but make sure we get + // the value of the 'plugin type' select. + '#limit_validation_errors' => [['fields', $block_name, 'type']], + '#prefix' => '
', + '#suffix' => '
', + ]; + } + + return $block_field_row; + } + + /** + * {@inheritdoc} + */ + protected function copyFormValuesToEntity(EntityInterface $entity, array $form, FormStateInterface $form_state) { + /** @var \Drupal\Core\Entity\Display\EntityViewDisplayInterface $entity */ + parent::copyFormValuesToEntity($entity, $form, $form_state); + + $block_manager = \Drupal::service('plugin.manager.block'); + foreach ($entity->getBlocks() as $name => $block_info) { + $form_values = $form_state->getValue(['fields', $name]); + if ($form_state->get('plugin_settings_remove') === $name) { + $form_state->set('plugin_settings_remove', NULL); + $entity->removeBlock($name); + } + else { + if ($form_state->get('plugin_settings_update') === $name) { + $form_state->set('plugin_settings_update', NULL); + + $block = $block_manager->createInstance($block_info['plugin_id'], $block_info['settings']); + + $sub_form_state = SubformState::createForSubform($form['fields'][$name]['plugin']['settings_edit_form']['settings'], $form, $form_state); + $block->submitConfigurationForm($form, $sub_form_state); + + if ($block instanceof ContextAwarePluginInterface && $block->getContextDefinitions()) { + $context_mapping = $sub_form_state->getValue('context_mapping', []); + $block->setContextMapping($context_mapping); + } + + $block_info['settings'] = $block->getConfiguration(); + } + $block_info['weight'] = $form_values['weight']; + $block_info['region'] = $form_values['region']; + $entity->setBlock($name, $block_info); + } + } + } + + /** + * {@inheritdoc} + */ + public function multistepSubmit($form, FormStateInterface $form_state) { + $trigger = $form_state->getTriggeringElement(); + if ($trigger['#op'] == 'remove') { + // Set the field back to 'non edit' mode, and update $this->entity with + // the new settings fro the next rebuild. + $field_name = $trigger['#field_name']; + $form_state->set('plugin_settings_edit', NULL); + $form_state->set('plugin_settings_remove', $field_name); + $this->entity = $this->buildEntity($form, $form_state); + $form_state->setRebuild(); + } + else { + parent::multistepSubmit($form, $form_state); + } + } + + /** + * {@inheritdoc} + */ protected function getEntityDisplay($entity_type_id, $bundle, $mode) { return entity_get_display($entity_type_id, $bundle, $mode); } diff --git a/core/modules/field_ui/src/Routing/RouteSubscriber.php b/core/modules/field_ui/src/Routing/RouteSubscriber.php index b08e7f8..0f9a66b 100644 --- a/core/modules/field_ui/src/Routing/RouteSubscriber.php +++ b/core/modules/field_ui/src/Routing/RouteSubscriber.php @@ -154,6 +154,17 @@ protected function alterRoutes(RouteCollection $collection) { $options ); $collection->add("entity.entity_view_display.{$entity_type_id}.view_mode", $route); + + $route = new Route( + "$path/display/{view_mode_name}/add-block", + [ + '_entity_form' => 'entity_view_display.add_block', + '_title' => 'Add block', + ] + $defaults, + ['_field_ui_view_mode_access' => 'administer ' . $entity_type_id . ' display'], + $options + ); + $collection->add("entity.entity_view_display.{$entity_type_id}.add_block", $route); } } } diff --git a/core/modules/field_ui/tests/src/FunctionalJavascript/EntityDisplayTest.php b/core/modules/field_ui/tests/src/FunctionalJavascript/EntityDisplayTest.php index 6da3c41..2c15103 100644 --- a/core/modules/field_ui/tests/src/FunctionalJavascript/EntityDisplayTest.php +++ b/core/modules/field_ui/tests/src/FunctionalJavascript/EntityDisplayTest.php @@ -15,7 +15,12 @@ class EntityDisplayTest extends JavascriptTestBase { /** * {@inheritdoc} */ - public static $modules = ['field_ui', 'entity_test']; + public static $modules = ['field_ui', 'entity_test', 'block_test']; + + /** + * @var \Drupal\Core\Session\AccountInterface + */ + protected $user; /** * {@inheritdoc} @@ -30,7 +35,7 @@ protected function setUp() { ]], ]); $entity->save(); - $this->drupalLogin($this->drupalCreateUser([ + $this->user = $this->drupalCreateUser([ 'access administration pages', 'view test entity', 'administer entity_test content', @@ -38,7 +43,8 @@ protected function setUp() { 'administer entity_test display', 'administer entity_test form display', 'view the administration theme', - ])); + ]); + $this->drupalLogin($this->user); } /** @@ -87,6 +93,88 @@ public function testEntityView() { } /** + * Tests that blocks can be added to entity view displays. + */ + public function testEntityViewWithBlocks() { + $assert_session = $this->assertSession(); + $page = $this->getSession()->getPage(); + + $this->drupalGet('entity_test/structure/entity_test/display'); + $this->clickLink('Add field block'); + $assert_session->assertWaitOnAjaxRequest(); + // Ensure that blocks with unsatisfiable contexts are not shown. + $assert_session->pageTextNotContains('Test context-aware unsatisfied block'); + // Ensure that context-aware blocks are shown. + $assert_session->pageTextContains('Test context-aware block'); + + // Set the context-aware to visible, but do not assign a context mapping. + $page->checkField('blocks[test_context_aware]'); + $page->find('css', '.ui-dialog-buttonpane .button--primary')->press(); + $assert_session->pageTextContains('Your settings have been saved.'); + + $this->drupalGet('entity_test/1'); + $assert_session->pageTextContains('No context mapping selected.'); + + // Set the context mapping. + $this->drupalGet('entity_test/structure/entity_test/display'); + $this->click('[name=testcontextawareblock_settings_edit]'); + $assert_session->assertWaitOnAjaxRequest(); + $page->selectFieldOption('fields[testcontextawareblock][settings_edit_form][settings][context_mapping][user]', '@user.current_user_context:current_user'); + $this->submitForm([], 'Update'); + $assert_session->assertWaitOnAjaxRequest(); + $this->submitForm([], 'Save'); + $assert_session->pageTextContains('Your settings have been saved.'); + + $this->drupalGet('entity_test/1'); + $assert_session->pageTextNotContains('No context mapping selected'); + $assert_session->pageTextContains($this->user->getAccountName()); + + // Add another instance of the same block. + $this->drupalGet('entity_test/structure/entity_test/display'); + $this->clickLink('Add field block'); + $assert_session->assertWaitOnAjaxRequest(); + $page->checkField('blocks[test_context_aware]'); + $page->find('css', '.ui-dialog-buttonpane .button--primary')->press(); + $assert_session->pageTextContains('Your settings have been saved.'); + + $this->drupalGet('entity_test/1'); + $assert_session->pageTextContains('No context mapping selected'); + $assert_session->pageTextContains($this->user->getAccountName()); + + // Disable the field by dragging. + $this->drupalGet('entity_test/structure/entity_test/display'); + $block_field_row = $page->find('css', '#testcontextawareblock-2'); + $disabled_region_row = $page->find('css', '.region-hidden-title'); + $block_field_row->find('css', '.handle')->dragTo($disabled_region_row); + $assert_session->assertWaitOnAjaxRequest(); + $this->submitForm([], 'Save'); + $this->assertSame('hidden', $assert_session->selectExists('fields[testcontextawareblock_2][region]')->getValue()); + $assert_session->pageTextContains('Your settings have been saved.'); + + $this->drupalGet('entity_test/1'); + $assert_session->pageTextNotContains('No context mapping selected'); + $assert_session->pageTextContains($this->user->getAccountName()); + + // Remove the blocks. + $this->drupalGet('entity_test/structure/entity_test/display'); + $this->click('[name=testcontextawareblock_settings_edit]'); + $assert_session->assertWaitOnAjaxRequest(); + $page->pressButton('Remove'); + $assert_session->assertWaitOnAjaxRequest(); + $this->click('[name=testcontextawareblock_2_settings_edit]'); + $assert_session->assertWaitOnAjaxRequest(); + $page->pressButton('Remove'); + $assert_session->assertWaitOnAjaxRequest(); + $this->submitForm([], 'Save'); + $assert_session->pageTextNotContains('Test context-aware block'); + $assert_session->pageTextContains('Your settings have been saved.'); + + $this->drupalGet('entity_test/1'); + $assert_session->pageTextNotContains('No context mapping selected'); + $assert_session->pageTextNotContains($this->user->getAccountName()); + } + + /** * Tests extra fields. */ public function testExtraFields() { diff --git a/core/modules/forum/config/optional/core.entity_view_display.comment.comment_forum.default.yml b/core/modules/forum/config/optional/core.entity_view_display.comment.comment_forum.default.yml index befeba8..b1d2d79 100644 --- a/core/modules/forum/config/optional/core.entity_view_display.comment.comment_forum.default.yml +++ b/core/modules/forum/config/optional/core.entity_view_display.comment.comment_forum.default.yml @@ -21,4 +21,5 @@ content: links: weight: 100 region: content +blocks: { } hidden: { } diff --git a/core/modules/forum/config/optional/core.entity_view_display.node.forum.default.yml b/core/modules/forum/config/optional/core.entity_view_display.node.forum.default.yml index f3e8c5c..fc36c27 100644 --- a/core/modules/forum/config/optional/core.entity_view_display.node.forum.default.yml +++ b/core/modules/forum/config/optional/core.entity_view_display.node.forum.default.yml @@ -43,4 +43,5 @@ content: settings: link: true third_party_settings: { } +blocks: { } hidden: { } diff --git a/core/modules/forum/config/optional/core.entity_view_display.node.forum.teaser.yml b/core/modules/forum/config/optional/core.entity_view_display.node.forum.teaser.yml index 7b174f4..0ba658f 100644 --- a/core/modules/forum/config/optional/core.entity_view_display.node.forum.teaser.yml +++ b/core/modules/forum/config/optional/core.entity_view_display.node.forum.teaser.yml @@ -34,5 +34,6 @@ content: settings: link: true third_party_settings: { } +blocks: { } hidden: comment_forum: true diff --git a/core/modules/forum/config/optional/core.entity_view_display.taxonomy_term.forums.default.yml b/core/modules/forum/config/optional/core.entity_view_display.taxonomy_term.forums.default.yml index b326039..14ef792 100644 --- a/core/modules/forum/config/optional/core.entity_view_display.taxonomy_term.forums.default.yml +++ b/core/modules/forum/config/optional/core.entity_view_display.taxonomy_term.forums.default.yml @@ -18,5 +18,6 @@ content: settings: { } third_party_settings: { } label: above +blocks: { } hidden: forum_container: true diff --git a/core/modules/options/tests/options_config_install_test/config/install/core.entity_view_display.node.options_install_test.default.yml b/core/modules/options/tests/options_config_install_test/config/install/core.entity_view_display.node.options_install_test.default.yml index aaea1cb..bcec50d 100644 --- a/core/modules/options/tests/options_config_install_test/config/install/core.entity_view_display.node.options_install_test.default.yml +++ b/core/modules/options/tests/options_config_install_test/config/install/core.entity_view_display.node.options_install_test.default.yml @@ -22,6 +22,7 @@ content: region: content settings: { } third_party_settings: { } +blocks: { } hidden: langcode: true third_party_settings: { } diff --git a/core/modules/options/tests/options_config_install_test/config/install/core.entity_view_display.node.options_install_test.teaser.yml b/core/modules/options/tests/options_config_install_test/config/install/core.entity_view_display.node.options_install_test.teaser.yml index 6e79af9..6e27348 100644 --- a/core/modules/options/tests/options_config_install_test/config/install/core.entity_view_display.node.options_install_test.teaser.yml +++ b/core/modules/options/tests/options_config_install_test/config/install/core.entity_view_display.node.options_install_test.teaser.yml @@ -24,6 +24,7 @@ content: settings: trim_length: 600 third_party_settings: { } +blocks: { } hidden: langcode: true third_party_settings: { } diff --git a/core/profiles/standard/config/install/core.entity_view_display.block_content.basic.default.yml b/core/profiles/standard/config/install/core.entity_view_display.block_content.basic.default.yml index e494882..603df4e 100644 --- a/core/profiles/standard/config/install/core.entity_view_display.block_content.basic.default.yml +++ b/core/profiles/standard/config/install/core.entity_view_display.block_content.basic.default.yml @@ -18,4 +18,5 @@ content: region: content settings: { } third_party_settings: { } +blocks: { } hidden: { } diff --git a/core/profiles/standard/config/install/core.entity_view_display.comment.comment.default.yml b/core/profiles/standard/config/install/core.entity_view_display.comment.comment.default.yml index 6ae213d..32a6969 100644 --- a/core/profiles/standard/config/install/core.entity_view_display.comment.comment.default.yml +++ b/core/profiles/standard/config/install/core.entity_view_display.comment.comment.default.yml @@ -21,4 +21,5 @@ content: links: weight: 100 region: content +blocks: { } hidden: { } diff --git a/core/profiles/standard/config/install/core.entity_view_display.node.article.default.yml b/core/profiles/standard/config/install/core.entity_view_display.node.article.default.yml index 5c43252..f20f87b 100644 --- a/core/profiles/standard/config/install/core.entity_view_display.node.article.default.yml +++ b/core/profiles/standard/config/install/core.entity_view_display.node.article.default.yml @@ -55,6 +55,7 @@ content: links: weight: 100 region: content +blocks: { } hidden: field_image: true field_tags: true diff --git a/core/profiles/standard/config/install/core.entity_view_display.node.article.rss.yml b/core/profiles/standard/config/install/core.entity_view_display.node.article.rss.yml index 84660b6..e972f68 100644 --- a/core/profiles/standard/config/install/core.entity_view_display.node.article.rss.yml +++ b/core/profiles/standard/config/install/core.entity_view_display.node.article.rss.yml @@ -18,6 +18,7 @@ content: links: weight: 100 region: content +blocks: { } hidden: body: true comment: true diff --git a/core/profiles/standard/config/install/core.entity_view_display.node.article.teaser.yml b/core/profiles/standard/config/install/core.entity_view_display.node.article.teaser.yml index 7b96908..3d60011 100644 --- a/core/profiles/standard/config/install/core.entity_view_display.node.article.teaser.yml +++ b/core/profiles/standard/config/install/core.entity_view_display.node.article.teaser.yml @@ -46,6 +46,7 @@ content: links: weight: 100 region: content +blocks: { } hidden: comment: true field_image: true diff --git a/core/profiles/standard/config/install/core.entity_view_display.node.page.default.yml b/core/profiles/standard/config/install/core.entity_view_display.node.page.default.yml index 8afd942..cb23010 100644 --- a/core/profiles/standard/config/install/core.entity_view_display.node.page.default.yml +++ b/core/profiles/standard/config/install/core.entity_view_display.node.page.default.yml @@ -22,4 +22,5 @@ content: links: weight: 101 region: content +blocks: { } hidden: { } diff --git a/core/profiles/standard/config/install/core.entity_view_display.node.page.teaser.yml b/core/profiles/standard/config/install/core.entity_view_display.node.page.teaser.yml index bc7a68c..e646d7a 100644 --- a/core/profiles/standard/config/install/core.entity_view_display.node.page.teaser.yml +++ b/core/profiles/standard/config/install/core.entity_view_display.node.page.teaser.yml @@ -24,4 +24,5 @@ content: links: weight: 101 region: content +blocks: { } hidden: { } diff --git a/core/profiles/standard/config/install/core.entity_view_display.user.user.compact.yml b/core/profiles/standard/config/install/core.entity_view_display.user.user.compact.yml index 2ff13ad..f5097d3 100644 --- a/core/profiles/standard/config/install/core.entity_view_display.user.user.compact.yml +++ b/core/profiles/standard/config/install/core.entity_view_display.user.user.compact.yml @@ -22,5 +22,6 @@ content: image_link: content third_party_settings: { } label: hidden +blocks: { } hidden: member_for: true diff --git a/core/profiles/standard/config/install/core.entity_view_display.user.user.default.yml b/core/profiles/standard/config/install/core.entity_view_display.user.user.default.yml index ef1fdd7..5036faf 100644 --- a/core/profiles/standard/config/install/core.entity_view_display.user.user.default.yml +++ b/core/profiles/standard/config/install/core.entity_view_display.user.user.default.yml @@ -24,4 +24,5 @@ content: image_link: content third_party_settings: { } label: hidden +blocks: { } hidden: { } diff --git a/core/tests/Drupal/KernelTests/Core/Entity/EntityDisplayFormBaseTest.php b/core/tests/Drupal/KernelTests/Core/Entity/EntityDisplayFormBaseTest.php index 11aeede..179062b 100644 --- a/core/tests/Drupal/KernelTests/Core/Entity/EntityDisplayFormBaseTest.php +++ b/core/tests/Drupal/KernelTests/Core/Entity/EntityDisplayFormBaseTest.php @@ -2,7 +2,7 @@ namespace Drupal\KernelTests\Core\Entity; -use Drupal\Core\Entity\Display\EntityDisplayInterface; +use Drupal\Core\Entity\Display\EntityViewDisplayInterface; use Drupal\Core\Form\FormState; use Drupal\field_ui\Form\EntityViewDisplayEditForm; use Drupal\KernelTests\KernelTestBase; @@ -24,8 +24,10 @@ class EntityDisplayFormBaseTest extends KernelTestBase { */ public function testCopyFormValuesToEntity() { $field_values = []; - $entity = $this->prophesize(EntityDisplayInterface::class); + $entity = $this->prophesize(EntityViewDisplayInterface::class); $entity->getPluginCollections()->willReturn([]); + // @todo. + $entity->getBlocks()->willReturn([]); $entity->getTargetEntityTypeId()->willReturn('entity_test_with_bundle'); $entity->getTargetBundle()->willReturn('target_bundle');