diff --git a/core/lib/Drupal/Core/Field/Plugin/Field/FieldFormatter/EntityReferenceEntityFormatter.php b/core/lib/Drupal/Core/Field/Plugin/Field/FieldFormatter/EntityReferenceEntityFormatter.php
index af994361fa..393858ce52 100644
--- a/core/lib/Drupal/Core/Field/Plugin/Field/FieldFormatter/EntityReferenceEntityFormatter.php
+++ b/core/lib/Drupal/Core/Field/Plugin/Field/FieldFormatter/EntityReferenceEntityFormatter.php
@@ -2,6 +2,7 @@
namespace Drupal\Core\Field\Plugin\Field\FieldFormatter;
+use Drupal\Core\Entity\Entity\EntityViewMode;
use Drupal\Core\Entity\EntityDisplayRepositoryInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Field\FieldDefinitionInterface;
@@ -9,6 +10,8 @@
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Logger\LoggerChannelFactoryInterface;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
+use Drupal\Core\Render\Markup;
+use Drupal\Core\Url;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
@@ -124,16 +127,141 @@ public static function defaultSettings() {
] + parent::defaultSettings();
}
+ protected function getSelectedViewModeName(FormStateInterface $form_state) {
+ return $this->getSetting('view_mode', $form_state);
+ }
+
+ /**
+ * Wrapper around ::getSetting() to reflect current values from Form State.
+ *
+ * @param string $key
+ * The setting name.
+ * @param \Drupal\Core\Form\FormStateInterface $form_state
+ * (optional) The form state object. If absent, this method is equivalent to
+ * parent::getSetting().
+ *
+ * @return mixed|null
+ * The value of the setting, or NULL if absent.
+ */
+ public function getSetting($key, FormStateInterface $form_state = NULL) {
+ if ($form_state) {
+ $field_name = $this->fieldDefinition->getName();
+ $form_state_key = [
+ 'fields',
+ $field_name,
+ 'settings_edit_form',
+ 'settings',
+ $key,
+ ];
+ if ($form_state->hasValue($form_state_key)) {
+ return $form_state->getValue($form_state_key);
+ }
+ }
+
+ return parent::getSetting($key);
+ }
+
+ /**
+ * Ajax callback for view mode selection change.
+ */
+ public static function onViewModeChange(array &$form, FormStateInterface $form_state) {
+ return $form['fields'][$form_state->get('plugin_settings_edit')]['plugin']['settings_edit_form']['settings'];
+ }
+
+ /**
+ * Rebuilds the form on select submit.
+ */
+ public static function rebuildOnSubmit(array &$form, FormStateInterface $form_state) {
+ $form_state->setRebuild(TRUE);
+ }
+
/**
* {@inheritdoc}
*/
public function settingsForm(array $form, FormStateInterface $form_state) {
+ $entity_type_id = $this->getFieldSetting('target_type');
+ $bundle_entity_type_id = $this->entityTypeManager->getDefinition($entity_type_id)->getBundleEntityType();
+ $bundles = array_keys($this->getFieldSetting('handler_settings')['target_bundles']);
+
+ $currently_selected_view_mode_name = $this->getSelectedViewModeName($form_state);
+ $currently_selected_view_mode = EntityViewMode::load("$entity_type_id.$currently_selected_view_mode_name");
+ // @todo Simplify this in https://www.drupal.org/node/2844203.
+ $currently_selected_view_mode_label = $currently_selected_view_mode ? $currently_selected_view_mode->label() : $this->t('Default');
+
+ $elements['#prefix'] = '
';
+ $elements['#suffix'] = '
';
+ $description = [
+ '#theme' => 'item_list',
+ '#context' => ['list_style' => 'comma-list'],
+ '#items' => [],
+ ];
+ if (count($bundles) === 1) {
+ $description['#items'][] = [
+ '#type' => 'link',
+ '#title' => $this->t('Configure %view-mode-label view mode', ['%view-mode-label' => $currently_selected_view_mode_label]),
+ '#url' => $currently_selected_view_mode_name === 'default'
+ ? Url::fromRoute(sprintf('entity.entity_view_display.%s.default', $entity_type_id), [$bundle_entity_type_id => $bundles[0]])
+ : Url::fromRoute(sprintf('entity.entity_view_display.%s.view_mode', $entity_type_id), [$bundle_entity_type_id => $bundles[0], 'view_mode_name' => $currently_selected_view_mode_name]),
+ '#attributes' => [
+ 'class' => ['use-ajax'],
+ 'data-dialog-type' => 'modal',
+ 'data-dialog-options' => json_encode([
+ 'width' => 800,
+ ]),
+ ],
+ ];
+ }
+ else {
+ $bundle_info = entity_get_bundles($entity_type_id);
+ for ($i = 0; $i < count($bundles); $i++) {
+ $description['#items'][] = [
+ '#type' => 'link',
+ '#prefix' => $i == 0
+ ? $this->t('Configure %view-mode-label view mode for', ['%view-mode-label' => $currently_selected_view_mode_label]) . ' '
+ : '',
+ '#title' => $this->t('%bundle-label %entity-type-label', ['%bundle-label' => $bundle_info[$bundles[$i]]['label'], '%entity-type-label' => 'Media items']),
+ '#url' => $currently_selected_view_mode_name === 'default'
+ ? Url::fromRoute(sprintf('entity.entity_view_display.%s.default', $entity_type_id), [$bundle_entity_type_id => $bundles[$i]])
+ : Url::fromRoute(sprintf('entity.entity_view_display.%s.view_mode', $entity_type_id), [$bundle_entity_type_id => $bundles[$i], 'view_mode_name' => $currently_selected_view_mode_name]),
+ '#attributes' => [
+ 'class' => ['use-ajax'],
+ 'data-dialog-type' => 'modal',
+ 'data-dialog-options' => json_encode([
+ 'width' => 800,
+ ]),
+ ],
+ ];
+ }
+ }
+ $description['#items'][] = [
+ '#type' => 'link',
+ '#title' => $this->t('add new view mode'),
+ '#prefix' => Markup::create(t('or') . ' '),
+ '#url' => Url::fromRoute('entity.entity_view_mode.add_form', ['entity_type_id' => $entity_type_id]),
+ '#attributes' => [
+ 'class' => ['use-ajax'],
+ 'data-dialog-type' => 'modal',
+ 'data-dialog-options' => json_encode([
+ 'width' => 800,
+ ]),
+ ],
+ ];
$elements['view_mode'] = [
'#type' => 'select',
- '#options' => $this->entityDisplayRepository->getViewModeOptions($this->getFieldSetting('target_type')),
+ '#options' => $this->entityDisplayRepository->getViewModeOptions($entity_type_id),
'#title' => t('View mode'),
- '#default_value' => $this->getSetting('view_mode'),
+ '#default_value' => $currently_selected_view_mode_name,
+ '#ajax' => [
+ 'callback' => [static::class, 'onViewModeChange'],
+ 'wrapper' => 'entity-reference-entity-formatter-settings-ajax',
+ 'method' => 'replace',
+ ],
+ '#submit' => [[static::class, 'rebuildOnSubmit']],
+ '#executes_submit_callback' => TRUE,
'#required' => TRUE,
+ '#description' => $description + [
+ '#access' => \Drupal::currentUser()->hasPermission('administer ' . $entity_type_id . ' display')
+ ],
];
return $elements;
diff --git a/core/misc/ajax.es6.js b/core/misc/ajax.es6.js
index 6248e46efe..b4fa3f25ca 100644
--- a/core/misc/ajax.es6.js
+++ b/core/misc/ajax.es6.js
@@ -368,6 +368,12 @@
* @type {string}
*/
this.wrapper = `#${this.wrapper}`;
+ // To avoid problems when the same form exists multiple times on the same page, allow its
+ // wrapper to be scoped to the form it lives in. This can f.e. happen when the same base form
+ // is both in the parent page and in a modal.
+ if (this.scope && this.scope === 'form') {
+ this.wrapper = `#${element.form.id} ${this.wrapper}`;
+ }
}
/**
diff --git a/core/misc/ajax.js b/core/misc/ajax.js
index 5ea52425be..d9b89a56e2 100644
--- a/core/misc/ajax.js
+++ b/core/misc/ajax.js
@@ -164,6 +164,10 @@ function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr
if (this.wrapper) {
this.wrapper = '#' + this.wrapper;
+
+ if (this.scope && this.scope === 'form') {
+ this.wrapper = '#' + element.form.id + ' ' + this.wrapper;
+ }
}
this.element = element;
diff --git a/core/modules/field_ui/src/Controller/EntityDisplayModeController.php b/core/modules/field_ui/src/Controller/EntityDisplayModeController.php
index b4a608acd5..763a025c62 100644
--- a/core/modules/field_ui/src/Controller/EntityDisplayModeController.php
+++ b/core/modules/field_ui/src/Controller/EntityDisplayModeController.php
@@ -3,6 +3,7 @@
namespace Drupal\field_ui\Controller;
use Drupal\Core\Controller\ControllerBase;
+use Drupal\Core\Entity\Entity\EntityViewMode;
use Drupal\Core\Url;
/**
@@ -10,6 +11,26 @@
*/
class EntityDisplayModeController extends ControllerBase {
+ public function editViewDisplayTitle($entity_type_id, $bundle, $view_mode_name) {
+ $entity_type = $this->entityTypeManager()->getDefinition($entity_type_id);
+ if ($entity_type->hasKey('bundle')) {
+ return $this->t('Edit %view-mode-label display of %bundle-label %entity-type-label', [
+ '%view-mode-label' => $view_mode_name === 'default'
+ ? t('Default')
+ : EntityViewMode::load("$entity_type_id.$view_mode_name")->label(),
+ '%bundle-label' => $this->entityManager()->getBundleInfo($entity_type_id)[$bundle]['label'],
+ '%entity-type-label' => $this->entityTypeManager()->getDefinition($entity_type_id)->getPluralLabel(),
+ ]);
+ }
+ else {
+ return $this->t('Edit %view-mode-label display of %entity-type-label', [
+ '%view-mode-label' => $view_mode_name === 'default'
+ ? t('Default')
+ : EntityViewMode::load("$entity_type_id.$view_mode_name")->label(),
+ '%entity-type-label' => $this->entityTypeManager()->getDefinition($entity_type_id)->getPluralLabel(),
+ ]);
+ }
+ }
/**
* Provides a list of eligible entity types for adding view modes.
*
diff --git a/core/modules/field_ui/src/Form/EntityDisplayFormBase.php b/core/modules/field_ui/src/Form/EntityDisplayFormBase.php
index 0a91862b35..8dfbd78412 100644
--- a/core/modules/field_ui/src/Form/EntityDisplayFormBase.php
+++ b/core/modules/field_ui/src/Form/EntityDisplayFormBase.php
@@ -4,6 +4,9 @@
use Drupal\Component\Plugin\Factory\DefaultFactory;
use Drupal\Component\Plugin\PluginManagerBase;
+use Drupal\Core\Ajax\AjaxResponse;
+use Drupal\Core\Ajax\CloseModalDialogCommand;
+use Drupal\Core\Ajax\HtmlCommand;
use Drupal\Core\Entity\EntityForm;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Entity\EntityWithPluginCollectionInterface;
@@ -235,6 +238,7 @@ public function form(array $form, FormStateInterface $form_state) {
'#submit' => ['::multistepSubmit'],
'#ajax' => [
'callback' => '::multistepAjax',
+ 'scope' => 'form',
'wrapper' => 'field-display-overview-wrapper',
'effect' => 'fade',
// The button stays hidden, so we hide the Ajax spinner too. Ad-hoc
@@ -244,18 +248,46 @@ public function form(array $form, FormStateInterface $form_state) {
'#attributes' => ['class' => ['visually-hidden']]
];
- $form['actions'] = ['#type' => 'actions'];
- $form['actions']['submit'] = [
- '#type' => 'submit',
- '#button_type' => 'primary',
- '#value' => $this->t('Save'),
- ];
-
$form['#attached']['library'][] = 'field_ui/drupal.field_ui';
return $form;
}
+ /**
+ * {@inheritdoc}
+ */
+ protected function actions(array $form, FormStateInterface $form_state) {
+ $actions = parent::actions($form, $form_state);
+ $actions['submit']['#ajax'] = [
+ 'callback' => '::ajaxCallback',
+ 'event' => 'click',
+ ];
+ return $actions;
+ }
+
+ /**
+ * Ajax callback to close the modal and update the selected text.
+ *
+ * @return \Drupal\Core\Ajax\AjaxResponse
+ * An ajax response object.
+ */
+ public function ajaxCallback(array $form, FormStateInterface $form_state) {
+ $response = new AjaxResponse();
+ if ($form_state->getErrors()) {
+ unset($form['#prefix'], $form['#suffix']);
+ $form['status_messages'] = [
+ '#type' => 'status_messages',
+ '#weight' => -10,
+ ];
+ $response->addCommand(new HtmlCommand('#drupal-modal', $form));
+ }
+ else {
+ // @todo show message for successful saving of settings once https://www.drupal.org/node/77245 lands. This message is set by \Drupal\field_ui\Form\EntityDisplayFormBase::submitForm().
+ $response->addCommand(new CloseModalDialogCommand());
+ }
+ return $response;
+ }
+
/**
* Builds the table row structure for a single field.
*
@@ -347,6 +379,7 @@ protected function buildFieldRow(FieldDefinitionInterface $field_definition, arr
'#submit' => ['::multistepSubmit'],
'#ajax' => [
'callback' => '::multistepAjax',
+ 'scope' => 'form',
'wrapper' => 'field-display-overview-wrapper',
'effect' => 'fade',
],
@@ -375,7 +408,6 @@ protected function buildFieldRow(FieldDefinitionInterface $field_definition, arr
'settings' => $settings_form,
'third_party_settings' => $third_party_settings_form,
'actions' => [
- '#type' => 'actions',
'save_settings' => $base_button + [
'#type' => 'submit',
'#button_type' => 'primary',
diff --git a/core/modules/field_ui/src/Routing/RouteSubscriber.php b/core/modules/field_ui/src/Routing/RouteSubscriber.php
index b08e7f8fe5..495f14a426 100644
--- a/core/modules/field_ui/src/Routing/RouteSubscriber.php
+++ b/core/modules/field_ui/src/Routing/RouteSubscriber.php
@@ -5,6 +5,7 @@
use Drupal\Core\Entity\EntityManagerInterface;
use Drupal\Core\Routing\RouteSubscriberBase;
use Drupal\Core\Routing\RoutingEvents;
+use Drupal\field_ui\Controller\EntityDisplayModeController;
use Symfony\Component\Routing\Route;
use Symfony\Component\Routing\RouteCollection;
@@ -136,7 +137,7 @@ protected function alterRoutes(RouteCollection $collection) {
"$path/display",
[
'_entity_form' => 'entity_view_display.edit',
- '_title' => 'Manage display',
+ '_title_callback' => EntityDisplayModeController::class . '::editViewDisplayTitle',
'view_mode_name' => 'default',
] + $defaults,
['_field_ui_view_mode_access' => 'administer ' . $entity_type_id . ' display'],
@@ -148,7 +149,7 @@ protected function alterRoutes(RouteCollection $collection) {
"$path/display/{view_mode_name}",
[
'_entity_form' => 'entity_view_display.edit',
- '_title' => 'Manage display',
+ '_title_callback' => EntityDisplayModeController::class . '::editViewDisplayTitle',
] + $defaults,
['_field_ui_view_mode_access' => 'administer ' . $entity_type_id . ' display'],
$options
diff --git a/core/modules/field_ui/tests/src/Functional/FieldUIRouteTest.php b/core/modules/field_ui/tests/src/Functional/FieldUIRouteTest.php
index fe7a27011c..1b27d2742a 100644
--- a/core/modules/field_ui/tests/src/Functional/FieldUIRouteTest.php
+++ b/core/modules/field_ui/tests/src/Functional/FieldUIRouteTest.php
@@ -46,13 +46,13 @@ public function testFieldUIRoutes() {
$this->assertResponse(403);
$this->drupalGet('admin/config/people/accounts/display');
- $this->assertTitle('Manage display | Drupal');
+ $this->assertTitle('Edit Default display of user entities | Drupal');
$this->assertLocalTasks();
$edit = ['display_modes_custom[compact]' => TRUE];
$this->drupalPostForm(NULL, $edit, t('Save'));
$this->drupalGet('admin/config/people/accounts/display/compact');
- $this->assertTitle('Manage display | Drupal');
+ $this->assertTitle('Edit Compact display of user entities | Drupal');
$this->assertLocalTasks();
// Test manage form display tabs and titles.
diff --git a/core/modules/system/tests/src/Functional/Menu/BreadcrumbTest.php b/core/modules/system/tests/src/Functional/Menu/BreadcrumbTest.php
index e9142dc853..09e448e330 100644
--- a/core/modules/system/tests/src/Functional/Menu/BreadcrumbTest.php
+++ b/core/modules/system/tests/src/Functional/Menu/BreadcrumbTest.php
@@ -113,10 +113,12 @@ public function testBreadCrumbs() {
];
$this->assertBreadcrumb("admin/structure/types/manage/$type/fields", $trail);
$this->assertBreadcrumb("admin/structure/types/manage/$type/display", $trail);
+ $title_html = t('Edit %view-mode-label display of %bundle-label %entity-type-label', ['%view-mode-label' => 'Teaser', '%bundle-label' => NodeType::load($type)->label(), '%entity-type-label' => 'content items']);
$trail_teaser = $trail + [
- "admin/structure/types/manage/$type/display" => t('Manage display'),
+ // @todo Improve ::assertBreadcrumbParts()
+ "admin/structure/types/manage/$type/display" => 'Edit display of ',
];
- $this->assertBreadcrumb("admin/structure/types/manage/$type/display/teaser", $trail_teaser);
+ $this->assertBreadcrumb("admin/structure/types/manage/$type/display/teaser", $trail_teaser, strip_tags($title_html));
$this->assertBreadcrumb("admin/structure/types/manage/$type/delete", $trail);
$trail += [
"admin/structure/types/manage/$type/fields" => t('Manage fields'),