.../EntityReferenceEntityFormatter.php | 132 ++++++++++++++++++++-
core/misc/ajax.es6.js | 6 +
core/misc/ajax.js | 4 +
.../src/Controller/EntityDisplayModeController.php | 21 ++++
.../field_ui/src/Form/EntityDisplayFormBase.php | 48 ++++++--
.../field_ui/src/Routing/RouteSubscriber.php | 5 +-
.../tests/src/Functional/FieldUIRouteTest.php | 4 +-
...core.entity_view_display.media.file.default.yml | 36 +-----
...ore.entity_view_display.media.image.default.yml | 34 +-----
.../system/src/Tests/Menu/BreadcrumbTest.php | 6 +-
10 files changed, 220 insertions(+), 76 deletions(-)
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 af99436..393858c 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 daa6254..66f5ea2 100644
--- a/core/misc/ajax.es6.js
+++ b/core/misc/ajax.es6.js
@@ -367,6 +367,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 1e15175..e827c88 100644
--- a/core/misc/ajax.js
+++ b/core/misc/ajax.js
@@ -163,6 +163,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 b4a608a..763a025 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 0a91862..8dfbd78 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,19 +248,47 @@ 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.
*
* @param \Drupal\Core\Field\FieldDefinitionInterface $field_definition
@@ -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 b08e7f8..495f14a 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 fe7a270..1b27d27 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/media/config/install/core.entity_view_display.media.file.default.yml b/core/modules/media/config/install/core.entity_view_display.media.file.default.yml
index 697f117..dd704e1 100644
--- a/core/modules/media/config/install/core.entity_view_display.media.file.default.yml
+++ b/core/modules/media/config/install/core.entity_view_display.media.file.default.yml
@@ -3,48 +3,22 @@ status: true
dependencies:
config:
- field.field.media.file.field_media_file
- - image.style.thumbnail
- media.type.file
module:
- file
- - image
- - user
id: media.file.default
targetEntityType: media
bundle: file
mode: default
content:
- created:
- label: hidden
- type: timestamp
- weight: 0
- region: content
- settings:
- date_format: medium
- custom_date_format: ''
- timezone: ''
- third_party_settings: { }
field_media_file:
- label: above
+ label: hidden
settings: { }
third_party_settings: { }
type: file_default
- weight: 6
- region: content
- thumbnail:
- type: image
- weight: 5
- label: hidden
- settings:
- image_style: thumbnail
- image_link: ''
- region: content
- third_party_settings: { }
- uid:
- label: hidden
- type: author
weight: 0
region: content
- settings: { }
- third_party_settings: { }
-hidden: { }
+hidden:
+ created: true
+ thumbnail: true
+ uid: true
diff --git a/core/modules/media/config/install/core.entity_view_display.media.image.default.yml b/core/modules/media/config/install/core.entity_view_display.media.image.default.yml
index e541d7f..f46a79c 100644
--- a/core/modules/media/config/install/core.entity_view_display.media.image.default.yml
+++ b/core/modules/media/config/install/core.entity_view_display.media.image.default.yml
@@ -7,45 +7,21 @@ dependencies:
- media.type.image
module:
- image
- - user
id: media.image.default
targetEntityType: media
bundle: image
mode: default
content:
- created:
- label: hidden
- type: timestamp
- weight: 0
- region: content
- settings:
- date_format: medium
- custom_date_format: ''
- timezone: ''
- third_party_settings: { }
field_media_image:
- label: above
- settings:
- image_style: ''
- image_link: ''
- third_party_settings: { }
- type: image
- weight: 6
- region: content
- thumbnail:
- type: image
- weight: 5
label: hidden
settings:
image_style: thumbnail
image_link: ''
- region: content
third_party_settings: { }
- uid:
- label: hidden
- type: author
+ type: image
weight: 0
region: content
- settings: { }
- third_party_settings: { }
-hidden: { }
+hidden:
+ created: true
+ thumbnail: true
+ uid: true
diff --git a/core/modules/system/src/Tests/Menu/BreadcrumbTest.php b/core/modules/system/src/Tests/Menu/BreadcrumbTest.php
index 39303ff..cc9f101 100644
--- a/core/modules/system/src/Tests/Menu/BreadcrumbTest.php
+++ b/core/modules/system/src/Tests/Menu/BreadcrumbTest.php
@@ -110,10 +110,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'),