diff --git a/core/config/schema/core.entity.schema.yml b/core/config/schema/core.entity.schema.yml index bf0e12d939cb177c09db7b966b05fa20297cffbe..711ca0f7c7e07ff3dcc970cea45393162387ede9 100644 --- a/core/config/schema/core.entity.schema.yml +++ b/core/config/schema/core.entity.schema.yml @@ -64,6 +64,9 @@ core.entity_view_display.*.*.*: weight: type: integer label: 'Weight' + region: + type: string + label: 'Region' label: type: string label: 'Label setting machine name' @@ -115,6 +118,9 @@ core.entity_form_display.*.*.*: weight: type: integer label: 'Weight' + region: + type: string + label: 'Region' settings: type: field.widget.settings.[%parent.type] label: 'Settings' diff --git a/core/lib/Drupal/Core/Entity/EntityDisplayBase.php b/core/lib/Drupal/Core/Entity/EntityDisplayBase.php index 4de891a825c5a867f00c6b068353444e860c16ac..0835aa06eea08dfac30c8013e987d22a3f6dbaef 100644 --- a/core/lib/Drupal/Core/Entity/EntityDisplayBase.php +++ b/core/lib/Drupal/Core/Entity/EntityDisplayBase.php @@ -154,6 +154,7 @@ public function __construct(array $values, $entity_type) { protected function init() { // Only populate defaults for "official" view modes and form modes. if ($this->mode !== static::CUSTOM_MODE) { + $default_region = $this->getDefaultRegion(); // Fill in defaults for extra fields. $context = $this->displayContext == 'view' ? 'display' : $this->displayContext; $extra_fields = \Drupal::entityManager()->getExtraFields($this->targetEntityType, $this->bundle); @@ -163,6 +164,8 @@ protected function init() { // Extra fields are visible by default unless they explicitly say so. if (!isset($definition['visible']) || $definition['visible'] == TRUE) { $this->content[$name] = array( + 'type' => 'visible', + 'region' => $default_region, 'weight' => $definition['weight'] ); } @@ -178,10 +181,13 @@ protected function init() { if (!$definition->isDisplayConfigurable($this->displayContext) || (!isset($this->content[$name]) && !isset($this->hidden[$name]))) { $options = $definition->getDisplayOptions($this->displayContext); - if (!empty($options['type']) && $options['type'] == 'hidden') { + // Check if either 'type' or 'region' is set to hidden. + // @todo Remove handling of 'type' in https://www.drupal.org/node/2799641. + if ((!empty($options['type']) && $options['type'] === 'hidden') || (!empty($options['region']) && $options['region'] === 'hidden')) { $this->hidden[$name] = TRUE; } elseif ($options) { + $options += ['region' => $default_region]; $this->content[$name] = $this->pluginManager->prepareConfiguration($definition->getType(), $options); } // Note: (base) fields that do not specify display options are not @@ -334,6 +340,12 @@ public function setComponent($name, array $options = array()) { // Ensure we always have an empty settings and array. $options += ['settings' => [], 'third_party_settings' => []]; + // Ensure that a region is set. + // @todo Make 'region' required in https://www.drupal.org/node/2799641. + if (!isset($options['region'])) { + $options['region'] = (isset($options['type']) && $options['type'] === 'hidden') ? 'hidden' : $this->getDefaultRegion(); + } + $this->content[$name] = $options; unset($this->hidden[$name]); unset($this->plugins[$name]); @@ -504,6 +516,16 @@ protected function getPluginRemovedDependencies(array $plugin_dependencies, arra return $intersect; } + /** + * Gets the default region. + * + * @return string + * The default region for this display. + */ + protected function getDefaultRegion() { + return 'content'; + } + /** * {@inheritdoc} */ diff --git a/core/lib/Drupal/Core/Field/BaseFieldDefinition.php b/core/lib/Drupal/Core/Field/BaseFieldDefinition.php index 594f31d14fbfad92fd2c25ae9d9eff934d8c1f88..dd96fc94a5d9f55e3341f1816a56b3da725d4469 100644 --- a/core/lib/Drupal/Core/Field/BaseFieldDefinition.php +++ b/core/lib/Drupal/Core/Field/BaseFieldDefinition.php @@ -414,7 +414,8 @@ public function setDisplayOptions($display_context, array $options) { public function setDisplayConfigurable($display_context, $configurable) { // If no explicit display options have been specified, default to 'hidden'. if (empty($this->definition['display'][$display_context])) { - $this->definition['display'][$display_context]['options'] = array('type' => 'hidden'); + // @todo Remove handling of 'type' in https://www.drupal.org/node/2799641. + $this->definition['display'][$display_context]['options'] = array('type' => 'hidden', 'region' => 'hidden'); } $this->definition['display'][$display_context]['configurable'] = $configurable; return $this; 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 e3c23cfb1370d54457f33536b73de3088a05ee76..e0232cfb51b738d2eddb0c259da19f46828c10df 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 @@ -11,20 +11,26 @@ content: checked: type: timestamp_ago weight: 1 + region: content settings: { } third_party_settings: { } label: inline description: weight: 3 + region: content feed_icon: weight: 5 + region: content image: weight: 2 + region: content items: weight: 0 + region: content link: type: uri_link weight: 4 + region: content settings: { } third_party_settings: { } label: inline 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 40425f2f54311c3794095a40229edf013f6d32ff..5e5e468ae5972b64dec5dd028bcaff799497c391 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 @@ -12,8 +12,10 @@ mode: summary content: items: weight: 0 + region: content more_link: weight: 1 + region: content 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 837bee0b6cb96485d9822aa3b8f0aa8bc046437c..8e29395a811259745cc478b048861d1e27dabeec 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 @@ -12,6 +12,7 @@ mode: summary content: timestamp: weight: 0 + region: content hidden: author: true description: true diff --git a/core/modules/book/config/install/core.entity_form_display.node.book.default.yml b/core/modules/book/config/install/core.entity_form_display.node.book.default.yml index 1ec4eb11b917510482a88638f7f9c1cad83a3423..58aba45d3629bb3e045dfa3f62eaba0fbae757eb 100644 --- a/core/modules/book/config/install/core.entity_form_display.node.book.default.yml +++ b/core/modules/book/config/install/core.entity_form_display.node.book.default.yml @@ -14,6 +14,7 @@ content: body: type: text_textarea_with_summary weight: 26 + region: content settings: rows: 9 summary_rows: 3 @@ -22,6 +23,7 @@ content: created: type: datetime_timestamp weight: 10 + region: content settings: { } third_party_settings: { } promote: @@ -29,16 +31,19 @@ content: settings: display_label: true weight: 15 + region: content third_party_settings: { } sticky: type: boolean_checkbox settings: display_label: true weight: 16 + region: content third_party_settings: { } title: type: string_textfield weight: -5 + region: content settings: size: 60 placeholder: '' @@ -46,6 +51,7 @@ content: uid: type: entity_reference_autocomplete weight: 5 + region: content settings: match_operator: CONTAINS size: 60 diff --git a/core/modules/book/config/install/core.entity_view_display.node.book.default.yml b/core/modules/book/config/install/core.entity_view_display.node.book.default.yml index 729516eb8b4b0097f8fedbd3654498ae6042d7b4..d6ef64df86218e8d141d698a179b838ba6895946 100644 --- a/core/modules/book/config/install/core.entity_view_display.node.book.default.yml +++ b/core/modules/book/config/install/core.entity_view_display.node.book.default.yml @@ -16,8 +16,10 @@ content: label: hidden type: text_default weight: 100 + region: content settings: { } third_party_settings: { } links: weight: 101 + region: content hidden: { } diff --git a/core/modules/book/config/install/core.entity_view_display.node.book.teaser.yml b/core/modules/book/config/install/core.entity_view_display.node.book.teaser.yml index fb22db6523898a420c5e19e27f3f4b100fc51e8b..77a62c35ab9dc84361ad5ff2cf348f91c62ab6e7 100644 --- a/core/modules/book/config/install/core.entity_view_display.node.book.teaser.yml +++ b/core/modules/book/config/install/core.entity_view_display.node.book.teaser.yml @@ -17,9 +17,11 @@ content: label: hidden type: text_summary_or_trimmed weight: 100 + region: content settings: trim_length: 600 third_party_settings: { } links: weight: 101 + region: content hidden: { } diff --git a/core/modules/field/src/Entity/FieldConfig.php b/core/modules/field/src/Entity/FieldConfig.php index d7ccc30360ec6bf08047ae6cbc871775fcfa71ec..1b79b6bd71643f9a610446bccc66a657cf6908ea 100644 --- a/core/modules/field/src/Entity/FieldConfig.php +++ b/core/modules/field/src/Entity/FieldConfig.php @@ -306,7 +306,8 @@ public function isDisplayConfigurable($context) { */ public function getDisplayOptions($display_context) { // Hide configurable fields by default. - return array('type' => 'hidden'); + // @todo Remove handling of 'type' in https://www.drupal.org/node/2799641. + return array('type' => 'hidden', 'region' => 'hidden'); } /** diff --git a/core/modules/field/tests/src/Kernel/Migrate/d6/MigrateFieldFormatterSettingsTest.php b/core/modules/field/tests/src/Kernel/Migrate/d6/MigrateFieldFormatterSettingsTest.php index 4c9cbf10ba9d29af9d19a7a713def72ba839e620..2ee3c779bc04bb9f1cd2b3e201f8d3f6e24e6d85 100644 --- a/core/modules/field/tests/src/Kernel/Migrate/d6/MigrateFieldFormatterSettingsTest.php +++ b/core/modules/field/tests/src/Kernel/Migrate/d6/MigrateFieldFormatterSettingsTest.php @@ -32,6 +32,7 @@ public function testEntityDisplaySettings() { 'type' => 'text_trimmed', 'settings' => array('trim_length' => 600), 'third_party_settings' => array(), + 'region' => 'content', ); // Can we load any entity display. diff --git a/core/modules/field/tests/src/Kernel/Migrate/d6/MigrateFieldWidgetSettingsTest.php b/core/modules/field/tests/src/Kernel/Migrate/d6/MigrateFieldWidgetSettingsTest.php index a16040fe951a33310d5a26c6822274966e71e45b..e7bb63621a0ba68d7c63b84f546ac24cde6e45d0 100644 --- a/core/modules/field/tests/src/Kernel/Migrate/d6/MigrateFieldWidgetSettingsTest.php +++ b/core/modules/field/tests/src/Kernel/Migrate/d6/MigrateFieldWidgetSettingsTest.php @@ -33,6 +33,7 @@ public function testWidgetSettings() { $expected = array('weight' => 1, 'type' => 'text_textfield'); $expected['settings'] = array('size' => 60, 'placeholder' => ''); $expected['third_party_settings'] = array(); + $expected['region'] = 'content'; $this->assertIdentical($expected, $component, 'Text field settings are correct.'); // Integer field. diff --git a/core/modules/field_ui/field_ui.js b/core/modules/field_ui/field_ui.js index a53553dc4841a965f552936d39f70c8844953fe0..c9ff216c5786d4c612b207fc47d8949bee659827 100644 --- a/core/modules/field_ui/field_ui.js +++ b/core/modules/field_ui/field_ui.js @@ -128,9 +128,14 @@ var refreshRows = {}; refreshRows[rowHandler.name] = $trigger.get(0); - // Handle region change. + // Handle region or type change. var region = rowHandler.getRegion(); - if (region !== rowHandler.region) { + // @todo Remove handling of 'type' in https://www.drupal.org/node/2799641. + var typeRegion = rowHandler.getType(); + if (region !== rowHandler.region || typeRegion !== rowHandler.region) { + if (region === rowHandler.region) { + region = typeRegion; + } // Remove parenting. $row.find('select.js-field-parent').val(''); // Let the row handler deal with the region change. @@ -270,6 +275,10 @@ this.$pluginSelect = $(row).find('select.field-plugin-type'); this.$pluginSelect.on('change', Drupal.fieldUIOverview.onChange); + // Attach change listener to the 'region' select. + this.$regionSelect = $(row).find('select.field-region'); + this.$regionSelect.on('change', Drupal.fieldUIOverview.onChange); + return this; }; @@ -282,6 +291,16 @@ * Either 'hidden' or 'content'. */ getRegion: function () { + return this.$regionSelect.val(); + }, + + /** + * Returns the region corresponding to the current form values of the row. + * + * @return {string} + * Either 'hidden' or 'content'. + */ + getType: function () { return (this.$pluginSelect.val() === 'hidden') ? 'hidden' : 'content'; }, @@ -305,14 +324,17 @@ * {@link Drupal.fieldUIOverview.AJAXRefreshRows}. */ regionChange: function (region) { + // Replace dashes with underscores. + region = region.replace(/-/g, '_'); + + // Set the region of the select list. + this.$regionSelect.val(region); // When triggered by a row drag, the 'format' select needs to be adjusted // to the new region. var currentValue = this.$pluginSelect.val(); var value; - // @TODO Check if this couldn't just be like - // if (region !== 'hidden') { - if (region === 'content') { + if (region !== 'hidden') { if (currentValue === 'hidden') { // Restore the formatter back to the default formatter. Pseudo-fields // do not have default formatters, we just return to 'visible' for diff --git a/core/modules/field_ui/src/Form/EntityDisplayFormBase.php b/core/modules/field_ui/src/Form/EntityDisplayFormBase.php index 586f1ef391909f533174352f354b2da5beb16ddc..f4fb39c310fddc2a01544997a338e63b185128c9 100644 --- a/core/modules/field_ui/src/Form/EntityDisplayFormBase.php +++ b/core/modules/field_ui/src/Form/EntityDisplayFormBase.php @@ -4,6 +4,7 @@ use Drupal\Component\Plugin\Factory\DefaultFactory; use Drupal\Component\Plugin\PluginManagerBase; +use Drupal\Core\Entity\Display\EntityDisplayInterface; use Drupal\Core\Entity\EntityForm; use Drupal\Core\Entity\EntityInterface; use Drupal\Core\Entity\EntityWithPluginCollectionInterface; @@ -172,6 +173,13 @@ public function form(array $form, FormStateInterface $form_state) { 'subgroup' => 'field-parent', 'source' => 'field-name', ), + array( + 'action' => 'match', + 'relationship' => 'parent', + 'group' => 'field-region', + 'subgroup' => 'field-region', + 'source' => 'field-name', + ), ), ); @@ -309,6 +317,15 @@ protected function buildFieldRow(FieldDefinitionInterface $field_definition, arr '#attributes' => array('class' => array('field-name')), ), ), + 'region' => array( + '#type' => 'select', + '#title' => $this->t('Region for @title', array('@title' => $label)), + '#title_display' => 'invisible', + '#options' => $this->getRegionOptions(), + '#empty_value' => 'hidden', + '#default_value' => isset($display_options['region']) ? $display_options['region'] : 'hidden', + '#attributes' => array('class' => array('field-region')), + ), ); $field_row['plugin'] = array( @@ -474,6 +491,15 @@ protected function buildExtraFieldRow($field_id, $extra_field) { '#attributes' => array('class' => array('field-name')), ), ), + 'region' => array( + '#type' => 'select', + '#title' => $this->t('Region for @title', array('@title' => $extra_field['label'])), + '#title_display' => 'invisible', + '#options' => $this->getRegionOptions(), + '#empty_value' => 'hidden', + '#default_value' => $display_options ? $display_options['region'] : 'hidden', + '#attributes' => array('class' => array('field-region')), + ), 'plugin' => array( 'type' => array( '#type' => 'select', @@ -548,44 +574,119 @@ protected function copyFormValuesToEntity(EntityInterface $entity, array $form, // Collect data for 'regular' fields. foreach ($form['#fields'] as $field_name) { - $values = $form_values['fields'][$field_name]; + $this->processFieldUpdates($field_name, $form_values['fields'][$field_name], $entity, $form_state); + } + + // Collect data for 'extra' fields. + foreach ($form['#extra'] as $name) { + $this->processFieldUpdates($name, $form_values['fields'][$name], $entity, $form_state); + } - if ($values['type'] == 'hidden') { - $entity->removeComponent($field_name); + $form_state->setTemporaryValue('entity_display_components_updated', TRUE); + } + + /** + * {@inheritdoc} + */ + public function save(array $form, FormStateInterface $form_state) { + $form_state->setTemporaryValue('entity_display_components_updated', NULL); + return parent::save($form, $form_state); + } + + /** + * Processes updates to the components for a given field. + * + * @param string $field_name + * The field name being processed. + * @param array $values + * The submitted form values. + * @param \Drupal\Core\Entity\Display\EntityDisplayInterface $entity + * The entity being updated. + * @param \Drupal\Core\Form\FormStateInterface $form_state + * The current state of the form. + */ + protected function processFieldUpdates($field_name, array $values, EntityDisplayInterface $entity, FormStateInterface $form_state) { + // If the component is not found, it is initially hidden. + $options = $entity->getComponent($field_name) ?: ['type' => 'hidden', 'region' => 'hidden']; + $remove_component = $options['region'] === 'hidden'; + if ($form_state->getTemporaryValue('entity_display_components_updated')) { + // Since the component has already been updated, replace $values with the + // relevant parts of $options. + $values = array_intersect_key($options, $values) + $values; + } + // @todo In https://www.drupal.org/node/2799641, remove this else statement. + else { + $remove_component = $this->determineComponentAction($options, $values); + } + + if ($remove_component) { + $entity->removeComponent($field_name); + } + else { + // Update field settings only if the submit handler told us to. + if ($form_state->get('plugin_settings_update') === $field_name) { + // Only store settings actually used by the selected plugin. + $default_settings = $this->pluginManager->getDefaultSettings($options['type']); + $options['settings'] = isset($values['settings_edit_form']['settings']) ? array_intersect_key($values['settings_edit_form']['settings'], $default_settings) : []; + $options['third_party_settings'] = isset($values['settings_edit_form']['third_party_settings']) ? $values['settings_edit_form']['third_party_settings'] : []; + $form_state->set('plugin_settings_update', NULL); } - else { - $options = $entity->getComponent($field_name); - - // Update field settings only if the submit handler told us to. - if ($form_state->get('plugin_settings_update') === $field_name) { - // Only store settings actually used by the selected plugin. - $default_settings = $this->pluginManager->getDefaultSettings($options['type']); - $options['settings'] = isset($values['settings_edit_form']['settings']) ? array_intersect_key($values['settings_edit_form']['settings'], $default_settings) : []; - $options['third_party_settings'] = isset($values['settings_edit_form']['third_party_settings']) ? $values['settings_edit_form']['third_party_settings'] : []; - $form_state->set('plugin_settings_update', NULL); - } + if (isset($values['type'])) { $options['type'] = $values['type']; - $options['weight'] = $values['weight']; - // Only formatters have configurable label visibility. - if (isset($values['label'])) { - $options['label'] = $values['label']; - } - $entity->setComponent($field_name, $options); } + $options['weight'] = $values['weight']; + if (isset($values['region'])) { + $options['region'] = $values['region']; + } + // Only formatters have configurable label visibility. + if (isset($values['label'])) { + $options['label'] = $values['label']; + } + $entity->setComponent($field_name, $options); } + } - // Collect data for 'extra' fields. - foreach ($form['#extra'] as $name) { - if ($form_values['fields'][$name]['type'] == 'hidden') { - $entity->removeComponent($name); + /** + * Determines whether a component should be updated or removed. + * + * @todo Remove handling of 'type' in https://www.drupal.org/node/2799641. + * + * @param array $old_values + * An array of the old values for a given component. + * @param array $new_values + * An array of the new values for a given component. + * + * @return bool + * TRUE if the component should be removed, FALSE if it should be updated. + */ + protected function determineComponentAction(array &$old_values, array &$new_values) { + $has_type_change = $new_values['type'] !== $old_values['type']; + $has_region_change = $new_values['region'] !== $old_values['region']; + // If the type and region both changed or neither changed, the action will + // be the same. Base the decision on whether the region is hidden. + if ($has_type_change === $has_region_change) { + $remove_component = $new_values['region'] === 'hidden'; + } + else { + if ($has_region_change) { + // If only the region changed, remove the component if it is now hidden. + $remove_component = $new_values['region'] === 'hidden'; + // If the region and type mismatch, remove the invalid type. + if ($new_values['region'] !== 'hidden' && $new_values['type'] === 'hidden') { + unset($new_values['type'], $old_values['type']); + } } else { - $entity->setComponent($name, array( - 'weight' => $form_values['fields'][$name]['weight'], - )); + // If only the type changed, remove the component if it is now hidden. + $remove_component = $new_values['type'] === 'hidden'; + // If the region and type mismatch, remove the invalid region. + if ($new_values['region'] === 'hidden' && $new_values['type'] !== 'hidden') { + unset($new_values['region'], $old_values['region']); + } } } + return $remove_component; } /** @@ -813,7 +914,7 @@ public function getRowRegion($row) { switch ($row['#row_type']) { case 'field': case 'extra_field': - return ($row['plugin']['type']['#value'] == 'hidden' ? 'hidden' : 'content'); + return $row['region']['#value'] ?: 'hidden'; } } diff --git a/core/modules/field_ui/src/Form/EntityFormDisplayEditForm.php b/core/modules/field_ui/src/Form/EntityFormDisplayEditForm.php index 741b98daea6c2b8d268edaee48eed4369b1621d9..af8e2edfc3073d052cf9cb5789f823d09c9ed1e6 100644 --- a/core/modules/field_ui/src/Form/EntityFormDisplayEditForm.php +++ b/core/modules/field_ui/src/Form/EntityFormDisplayEditForm.php @@ -94,6 +94,7 @@ protected function getTableHeader() { $this->t('Field'), $this->t('Weight'), $this->t('Parent'), + $this->t('Region'), array('data' => $this->t('Widget'), 'colspan' => 3), ); } diff --git a/core/modules/field_ui/src/Form/EntityViewDisplayEditForm.php b/core/modules/field_ui/src/Form/EntityViewDisplayEditForm.php index f273325f67394d476214bfd99400f8feab6032cd..174726f2c60b5e4eb2a431bf475f394f96de51ec 100644 --- a/core/modules/field_ui/src/Form/EntityViewDisplayEditForm.php +++ b/core/modules/field_ui/src/Form/EntityViewDisplayEditForm.php @@ -127,6 +127,7 @@ protected function getTableHeader() { $this->t('Field'), $this->t('Weight'), $this->t('Parent'), + $this->t('Region'), $this->t('Label'), array('data' => $this->t('Format'), 'colspan' => 3), ); diff --git a/core/modules/field_ui/src/Tests/ManageDisplayTest.php b/core/modules/field_ui/src/Tests/ManageDisplayTest.php index 4cd29016364d0453f841126d3994ba1bab777092..eaedda6612147d3be9395db82d6c45f1f4ac6993 100644 --- a/core/modules/field_ui/src/Tests/ManageDisplayTest.php +++ b/core/modules/field_ui/src/Tests/ManageDisplayTest.php @@ -3,6 +3,7 @@ namespace Drupal\field_ui\Tests; use Drupal\Component\Utility\Unicode; +use Drupal\Core\Entity\Entity\EntityFormDisplay; use Drupal\Core\Entity\Entity\EntityViewDisplay; use Drupal\Core\Entity\EntityInterface; use Drupal\Core\Language\LanguageInterface; @@ -98,6 +99,19 @@ function testFormatterUI() { ); $this->assertEqual($options, $expected_options, 'The expected formatter ordering is respected.'); + // Ensure that fields can be hidden directly by changing the region. + $this->drupalGet($manage_display); + $this->assertFieldByName('fields[field_test][region]', 'content'); + $edit = ['fields[field_test][region]' => 'hidden']; + $this->drupalPostForm($manage_display, $edit, t('Save')); + $this->assertFieldByName('fields[field_test][region]', 'hidden'); + $display = EntityViewDisplay::load("node.{$this->type}.default"); + $this->assertNull($display->getComponent('field_test')); + + // Restore the field to the content region. + $edit = ['fields[field_test][region]' => 'content']; + $this->drupalPostForm($manage_display, $edit, t('Save')); + // Change the formatter and check that the summary is updated. $edit = array('fields[field_test][type]' => 'field_test_multiple', 'refresh_rows' => 'field_test'); $this->drupalPostAjaxForm(NULL, $edit, array('op' => t('Refresh'))); @@ -284,6 +298,14 @@ public function testWidgetUI() { // Checks if the select elements contain the specified options. $this->assertFieldSelectOptions('fields[field_test][type]', array('test_field_widget', 'test_field_widget_multiple', 'hidden')); $this->assertFieldSelectOptions('fields[field_onewidgetfield][type]', array('test_field_widget', 'hidden')); + + // Ensure that fields can be hidden directly by changing the region. + $this->assertFieldByName('fields[field_test][region]', 'content'); + $edit = ['fields[field_test][region]' => 'hidden']; + $this->drupalPostForm(NULL, $edit, t('Save')); + $this->assertFieldByName('fields[field_test][region]', 'hidden'); + $display = EntityFormDisplay::load("node.{$this->type}.default"); + $this->assertNull($display->getComponent('field_test')); } /** diff --git a/core/modules/field_ui/tests/src/FunctionalJavascript/EntityDisplayTest.php b/core/modules/field_ui/tests/src/FunctionalJavascript/EntityDisplayTest.php new file mode 100644 index 0000000000000000000000000000000000000000..7cde00cc46bbb026d8f737cae7f12521d1d391de --- /dev/null +++ b/core/modules/field_ui/tests/src/FunctionalJavascript/EntityDisplayTest.php @@ -0,0 +1,95 @@ +<?php + +namespace Drupal\Tests\field_ui\FunctionalJavascript; + +use Drupal\entity_test\Entity\EntityTest; +use Drupal\FunctionalJavascriptTests\JavascriptTestBase; + +/** + * Tests the UI for entity displays. + * + * @group field_ui + */ +class EntityDisplayTest extends JavascriptTestBase { + + /** + * {@inheritdoc} + */ + public static $modules = ['field_ui', 'entity_test']; + + /** + * {@inheritdoc} + */ + protected function setUp() { + parent::setUp(); + + $entity = EntityTest::create([ + 'name' => 'The name for this entity', + 'field_test_text' => [[ + 'value' => 'The field test text value', + ]], + ]); + $entity->save(); + $this->drupalLogin($this->drupalCreateUser([ + 'access administration pages', + 'view test entity', + 'administer entity_test content', + 'administer entity_test fields', + 'administer entity_test display', + 'administer entity_test form display', + 'view the administration theme', + ])); + } + + /** + * Tests the use of regions for entity form displays. + */ + public function testEntityForm() { + $this->drupalGet('entity_test/manage/1/edit'); + $this->assertSession()->fieldExists('field_test_text[0][value]'); + + $this->drupalGet('entity_test/structure/entity_test/form-display'); + $this->assertTrue($this->assertSession()->optionExists('fields[field_test_text][region]', 'content')->isSelected()); + $this->assertFalse($this->assertSession()->optionExists('fields[field_test_text][type]', 'hidden')->isSelected()); + + $this->getSession()->getPage()->selectFieldOption('fields[field_test_text][region]', 'hidden'); + $this->assertSession()->assertWaitOnAjaxRequest(); + $this->assertTrue($this->assertSession()->optionExists('fields[field_test_text][region]', 'hidden')->isSelected()); + $this->assertTrue($this->assertSession()->optionExists('fields[field_test_text][type]', 'hidden')->isSelected()); + + $this->submitForm([], 'Save'); + $this->assertSession()->pageTextContains('Your settings have been saved.'); + $this->assertTrue($this->assertSession()->optionExists('fields[field_test_text][region]', 'hidden')->isSelected()); + $this->assertTrue($this->assertSession()->optionExists('fields[field_test_text][type]', 'hidden')->isSelected()); + + $this->drupalGet('entity_test/manage/1/edit'); + $this->assertSession()->fieldNotExists('field_test_text[0][value]'); + } + + /** + * Tests the use of regions for entity view displays. + */ + public function testEntityView() { + $this->drupalGet('entity_test/1'); + $this->assertSession()->elementNotExists('css', '.field--name-field-test-text'); + + $this->drupalGet('entity_test/structure/entity_test/display'); + $this->assertSession()->elementExists('css', '.region-content-message.region-empty'); + $this->assertTrue($this->assertSession()->optionExists('fields[field_test_text][region]', 'hidden')->isSelected()); + $this->assertTrue($this->assertSession()->optionExists('fields[field_test_text][type]', 'hidden')->isSelected()); + + $this->getSession()->getPage()->selectFieldOption('fields[field_test_text][region]', 'content'); + $this->assertSession()->assertWaitOnAjaxRequest(); + $this->assertTrue($this->assertSession()->optionExists('fields[field_test_text][region]', 'content')->isSelected()); + $this->assertFalse($this->assertSession()->optionExists('fields[field_test_text][type]', 'hidden')->isSelected()); + + $this->submitForm([], 'Save'); + $this->assertSession()->pageTextContains('Your settings have been saved.'); + $this->assertTrue($this->assertSession()->optionExists('fields[field_test_text][region]', 'content')->isSelected()); + $this->assertFalse($this->assertSession()->optionExists('fields[field_test_text][type]', 'hidden')->isSelected()); + + $this->drupalGet('entity_test/1'); + $this->assertSession()->elementExists('css', '.field--name-field-test-text'); + } + +} diff --git a/core/modules/field_ui/tests/src/Kernel/EntityDisplayTest.php b/core/modules/field_ui/tests/src/Kernel/EntityDisplayTest.php index be188db4f1b339760a0fd66e514bcd1c10de334c..756b9a79cf8f47f5f45ce463bfb1b99e75b3c0a4 100644 --- a/core/modules/field_ui/tests/src/Kernel/EntityDisplayTest.php +++ b/core/modules/field_ui/tests/src/Kernel/EntityDisplayTest.php @@ -53,15 +53,15 @@ public function testEntityDisplayCRUD() { // Check that providing no 'weight' results in the highest current weight // being assigned. The 'name' field's formatter has weight -5, therefore // these follow. - $expected['component_1'] = array('weight' => -4, 'settings' => array(), 'third_party_settings' => array()); - $expected['component_2'] = array('weight' => -3, 'settings' => array(), 'third_party_settings' => array()); + $expected['component_1'] = array('weight' => -4, 'settings' => array(), 'third_party_settings' => array(), 'region' => 'content'); + $expected['component_2'] = array('weight' => -3, 'settings' => array(), 'third_party_settings' => array(), 'region' => 'content'); $display->setComponent('component_1'); $display->setComponent('component_2'); $this->assertEqual($display->getComponent('component_1'), $expected['component_1']); $this->assertEqual($display->getComponent('component_2'), $expected['component_2']); // Check that arbitrary options are correctly stored. - $expected['component_3'] = array('weight' => 10, 'third_party_settings' => array('field_test' => array('foo' => 'bar')), 'settings' => array()); + $expected['component_3'] = array('weight' => 10, 'third_party_settings' => array('field_test' => array('foo' => 'bar')), 'settings' => array(), 'region' => 'content'); $display->setComponent('component_3', $expected['component_3']); $this->assertEqual($display->getComponent('component_3'), $expected['component_3']); @@ -86,6 +86,7 @@ public function testEntityDisplayCRUD() { 'link_to_entity' => FALSE, ), 'third_party_settings' => array(), + 'region' => 'content', ); $this->assertEqual($display->getComponents(), $expected); @@ -148,7 +149,7 @@ public function testEntityGetDisplay() { $display = entity_get_display('entity_test', 'entity_test', 'default'); $this->assertFalse($display->isNew()); $this->assertEqual($display->id(), 'entity_test.entity_test.default'); - $this->assertEqual($display->getComponent('component_1'), array( 'weight' => 10, 'settings' => array(), 'third_party_settings' => array())); + $this->assertEqual($display->getComponent('component_1'), array( 'weight' => 10, 'settings' => array(), 'third_party_settings' => array(), 'region' => 'content')); } /** @@ -164,14 +165,14 @@ public function testExtraFieldComponent() { // Check that the default visibility taken into account for extra fields // unknown in the display. - $this->assertEqual($display->getComponent('display_extra_field'), array('weight' => 5)); + $this->assertEqual($display->getComponent('display_extra_field'), array('weight' => 5, 'type' => 'visible', 'region' => 'content')); $this->assertNull($display->getComponent('display_extra_field_hidden')); // Check that setting explicit options overrides the defaults. $display->removeComponent('display_extra_field'); $display->setComponent('display_extra_field_hidden', array('weight' => 10)); $this->assertNull($display->getComponent('display_extra_field')); - $this->assertEqual($display->getComponent('display_extra_field_hidden'), array('weight' => 10, 'settings' => array(), 'third_party_settings' => array())); + $this->assertEqual($display->getComponent('display_extra_field_hidden'), array('weight' => 10, 'settings' => array(), 'third_party_settings' => array(), 'region' => 'content')); } /** @@ -209,6 +210,7 @@ public function testFieldComponent() { 'type' => $default_formatter, 'settings' => $formatter_settings, 'third_party_settings' => array(), + 'region' => 'content', ); $this->assertEqual($display->getComponent($field_name), $expected); @@ -258,6 +260,7 @@ public function testBaseFieldComponent() { 'settings' => $formatter_settings, 'third_party_settings' => array(), 'weight' => 10, + 'region' => 'content', ), 'test_display_non_configurable' => array( 'label' => 'above', @@ -265,6 +268,7 @@ public function testBaseFieldComponent() { 'settings' => $formatter_settings, 'third_party_settings' => array(), 'weight' => 11, + 'region' => 'content', ), ); foreach ($expected as $field_name => $options) { diff --git a/core/modules/field_ui/tests/src/Kernel/EntityFormDisplayTest.php b/core/modules/field_ui/tests/src/Kernel/EntityFormDisplayTest.php index e38db7cbb73553c0a06d5b921ce8988b2801bf4d..fb343fdf0e33f78cd6f316434e2234d87218369d 100644 --- a/core/modules/field_ui/tests/src/Kernel/EntityFormDisplayTest.php +++ b/core/modules/field_ui/tests/src/Kernel/EntityFormDisplayTest.php @@ -43,7 +43,7 @@ public function testEntityGetFromDisplay() { $form_display = entity_get_form_display('entity_test', 'entity_test', 'default'); $this->assertFalse($form_display->isNew()); $this->assertEqual($form_display->id(), 'entity_test.entity_test.default'); - $this->assertEqual($form_display->getComponent('component_1'), array('weight' => 10, 'settings' => array(), 'third_party_settings' => array())); + $this->assertEqual($form_display->getComponent('component_1'), array('weight' => 10, 'settings' => array(), 'third_party_settings' => array(), 'region' => 'content')); } /** @@ -80,6 +80,7 @@ public function testFieldComponent() { 'type' => $default_widget, 'settings' => $widget_settings, 'third_party_settings' => array(), + 'region' => 'content', ); $this->assertEqual($form_display->getComponent($field_name), $expected); @@ -134,12 +135,14 @@ public function testBaseFieldComponent() { 'settings' => $formatter_settings, 'third_party_settings' => array(), 'weight' => 10, + 'region' => 'content', ), 'test_display_non_configurable' => array( 'type' => 'text_textfield', 'settings' => $formatter_settings, 'third_party_settings' => array(), 'weight' => 11, + 'region' => 'content', ), ); foreach ($expected as $field_name => $options) { diff --git a/core/modules/forum/config/optional/core.entity_form_display.comment.comment_forum.default.yml b/core/modules/forum/config/optional/core.entity_form_display.comment.comment_forum.default.yml index a09c30b0069e83ed0871f84f2896ab8ab158a7f6..4738bbb43a489392ec3cb6b6e7b98ce930d112f2 100644 --- a/core/modules/forum/config/optional/core.entity_form_display.comment.comment_forum.default.yml +++ b/core/modules/forum/config/optional/core.entity_form_display.comment.comment_forum.default.yml @@ -13,9 +13,11 @@ mode: default content: author: weight: -2 + region: content comment_body: type: text_textarea weight: 11 + region: content settings: rows: 5 placeholder: '' @@ -23,6 +25,7 @@ content: subject: type: string_textfield weight: 10 + region: content settings: size: 60 placeholder: '' diff --git a/core/modules/forum/config/optional/core.entity_form_display.node.forum.default.yml b/core/modules/forum/config/optional/core.entity_form_display.node.forum.default.yml index c66ba236d174bff44cb6b16a125ae5e4fd6d8927..6773d32d23aae3bd4cb73bb8d0088767d4b95d2d 100644 --- a/core/modules/forum/config/optional/core.entity_form_display.node.forum.default.yml +++ b/core/modules/forum/config/optional/core.entity_form_display.node.forum.default.yml @@ -17,6 +17,7 @@ content: body: type: text_textarea_with_summary weight: 27 + region: content settings: rows: 9 summary_rows: 3 @@ -25,11 +26,13 @@ content: comment_forum: type: comment_default weight: 20 + region: content settings: { } third_party_settings: { } created: type: datetime_timestamp weight: 10 + region: content settings: { } third_party_settings: { } promote: @@ -37,21 +40,25 @@ content: settings: display_label: true weight: 15 + region: content third_party_settings: { } sticky: type: boolean_checkbox settings: display_label: true weight: 16 + region: content third_party_settings: { } taxonomy_forums: type: options_select weight: 26 + region: content settings: { } third_party_settings: { } title: type: string_textfield weight: -5 + region: content settings: size: 60 placeholder: '' @@ -59,6 +66,7 @@ content: uid: type: entity_reference_autocomplete weight: 5 + region: content settings: match_operator: CONTAINS size: 60 diff --git a/core/modules/forum/config/optional/core.entity_form_display.taxonomy_term.forums.default.yml b/core/modules/forum/config/optional/core.entity_form_display.taxonomy_term.forums.default.yml index b18c869becd033b7ce01eb2342ecfafb6d121ef0..50df98ac17120ad879ab39edc42e94b9f7f56d20 100644 --- a/core/modules/forum/config/optional/core.entity_form_display.taxonomy_term.forums.default.yml +++ b/core/modules/forum/config/optional/core.entity_form_display.taxonomy_term.forums.default.yml @@ -14,11 +14,13 @@ content: description: type: text_textfield weight: 0 + region: content settings: { } third_party_settings: { } name: type: string_textfield weight: -5 + region: content settings: size: 60 placeholder: '' 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 f4f0112f588b1c5ade63bffa7ec1d2577c85468c..befeba89ae88f70caf3648c81979c98f40e2ca3a 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 @@ -15,8 +15,10 @@ content: label: hidden type: text_default weight: 0 + region: content settings: { } third_party_settings: { } links: weight: 100 + region: content 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 b157c839e108ea236fc9bcaa1bb74e1f91c0f254..f3e8c5c613227545e2e9d532bf16cefbacf6e2d0 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 @@ -20,21 +20,25 @@ content: label: hidden type: text_default weight: 0 + region: content settings: { } third_party_settings: { } comment_forum: label: hidden type: comment_default weight: 20 + region: content settings: view_mode: default pager_id: 0 third_party_settings: { } links: weight: 100 + region: content taxonomy_forums: type: entity_reference_label weight: -1 + region: content label: above settings: link: true 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 4405e71f9ea47511c2bfde3037469b8ded6d91e2..7b174f442919dc1a4618729ae0722b860c3aca61 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 @@ -19,14 +19,17 @@ content: label: hidden type: text_summary_or_trimmed weight: 100 + region: content settings: trim_length: 600 third_party_settings: { } links: weight: 101 + region: content taxonomy_forums: type: entity_reference_label weight: 10 + region: content label: above settings: link: 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 d1242d99b6a0a43f67b1e2319dfb2ea6a3731939..b326039a46c34d858b5e01a847097b8bff76ffe1 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 @@ -14,6 +14,7 @@ content: description: type: text_default weight: 0 + region: content settings: { } third_party_settings: { } label: above diff --git a/core/modules/options/tests/options_config_install_test/config/install/core.entity_form_display.node.options_install_test.default.yml b/core/modules/options/tests/options_config_install_test/config/install/core.entity_form_display.node.options_install_test.default.yml index 19a2ef7076a0e93327697d2e1d855784a33850e6..ff5f0ec275c8ed251016292ddb7075d30954c6dd 100644 --- a/core/modules/options/tests/options_config_install_test/config/install/core.entity_form_display.node.options_install_test.default.yml +++ b/core/modules/options/tests/options_config_install_test/config/install/core.entity_form_display.node.options_install_test.default.yml @@ -14,6 +14,7 @@ content: title: type: string_textfield weight: -5 + region: content settings: size: 60 placeholder: '' @@ -21,6 +22,7 @@ content: uid: type: entity_reference_autocomplete weight: 5 + region: content settings: match_operator: CONTAINS size: 60 @@ -29,6 +31,7 @@ content: created: type: datetime_timestamp weight: 10 + region: content settings: { } third_party_settings: { } promote: @@ -36,16 +39,19 @@ content: settings: display_label: true weight: 15 + region: content third_party_settings: { } sticky: type: boolean_checkbox settings: display_label: true weight: 16 + region: content third_party_settings: { } body: type: text_textarea_with_summary weight: 26 + region: content settings: rows: 9 summary_rows: 3 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 c107b101753f675cfcc62461dc466bee07715ecc..aaea1cb90e9c056f75f5f86bc8e74cf0621d75b5 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 @@ -14,10 +14,12 @@ mode: default content: links: weight: 100 + region: content body: label: hidden type: text_default weight: 101 + region: content settings: { } third_party_settings: { } hidden: 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 3b472a7dcb2d2c718bb2d96ede901606fb276cf2..6e79af94ee5cfddb41e43ff72fa61b88d518b4b2 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 @@ -15,10 +15,12 @@ mode: teaser content: links: weight: 100 + region: content body: label: hidden type: text_summary_or_trimmed weight: 101 + region: content settings: trim_length: 600 third_party_settings: { } diff --git a/core/modules/system/src/Tests/Update/UpdateEntityDisplayTest.php b/core/modules/system/src/Tests/Update/UpdateEntityDisplayTest.php new file mode 100644 index 0000000000000000000000000000000000000000..0a2cbf45bbc0e79aa4765dc1c6c20d3d46dfcb03 --- /dev/null +++ b/core/modules/system/src/Tests/Update/UpdateEntityDisplayTest.php @@ -0,0 +1,49 @@ +<?php + +namespace Drupal\system\Tests\Update; + +use Drupal\Core\Entity\Entity\EntityFormDisplay; +use Drupal\Core\Entity\Entity\EntityViewDisplay; + +/** + * Tests system_post_update_add_region_to_entity_displays(). + * + * @group Update + */ +class UpdateEntityDisplayTest extends UpdatePathTestBase { + + /** + * {@inheritdoc} + */ + protected function setDatabaseDumpFiles() { + $this->databaseDumpFiles = [ + __DIR__ . '/../../../tests/fixtures/update/drupal-8.bare.standard.php.gz', + ]; + } + + /** + * Tests that entity displays are updated with regions for their fields. + */ + public function testUpdate() { + // No region key appears pre-update. + $entity_form_display = EntityFormDisplay::load('node.article.default'); + $options = $entity_form_display->getComponent('body'); + $this->assertFalse(array_key_exists('region', $options)); + + $entity_view_display = EntityViewDisplay::load('node.article.default'); + $options = $entity_view_display->getComponent('body'); + $this->assertFalse(array_key_exists('region', $options)); + + $this->runUpdates(); + + // The region key has been populated with 'content'. + $entity_form_display = EntityFormDisplay::load('node.article.default'); + $options = $entity_form_display->getComponent('body'); + $this->assertIdentical('content', $options['region']); + + $entity_view_display = EntityViewDisplay::load('node.article.default'); + $options = $entity_view_display->getComponent('body'); + $this->assertIdentical('content', $options['region']); + } + +} diff --git a/core/modules/system/system.post_update.php b/core/modules/system/system.post_update.php index b75625c32d0bab03a16fd61ed2453e59e25d51a2..1bd11a8fe6e5ef2eed47cdcb18b2f47fc3c51798 100644 --- a/core/modules/system/system.post_update.php +++ b/core/modules/system/system.post_update.php @@ -5,6 +5,10 @@ * Post update functions for System. */ +use Drupal\Core\Entity\Display\EntityDisplayInterface; +use Drupal\Core\Entity\Entity\EntityFormDisplay; +use Drupal\Core\Entity\Entity\EntityViewDisplay; + /** * @addtogroup updates-8.0.0-beta * @{ @@ -41,3 +45,18 @@ function system_post_update_recalculate_configuration_entity_dependencies(&$sand /** * @} End of "addtogroup updates-8.0.0-beta". */ + +/** + * Update entity displays to contain the region for each field. + */ +function system_post_update_add_region_to_entity_displays() { + $entity_save = function (EntityDisplayInterface $entity) { + foreach ($entity->getComponents() as $name => $component) { + // setComponent() will fill in the correct region based on the 'type'. + $entity->setComponent($name, $component); + } + $entity->save(); + }; + array_map($entity_save, EntityViewDisplay::loadMultiple()); + array_map($entity_save, EntityFormDisplay::loadMultiple()); +} diff --git a/core/profiles/standard/config/install/core.entity_form_display.block_content.basic.default.yml b/core/profiles/standard/config/install/core.entity_form_display.block_content.basic.default.yml index ee0c1384d950a2962eb0f1698a8e3e78c213fe19..7ccb5b0ad374dac3f1c5e9e98d5d8035c6d4d7c3 100644 --- a/core/profiles/standard/config/install/core.entity_form_display.block_content.basic.default.yml +++ b/core/profiles/standard/config/install/core.entity_form_display.block_content.basic.default.yml @@ -14,6 +14,7 @@ content: body: type: text_textarea_with_summary weight: -4 + region: content settings: rows: 9 summary_rows: 3 @@ -22,6 +23,7 @@ content: info: type: string_textfield weight: -5 + region: content settings: size: 60 placeholder: '' diff --git a/core/profiles/standard/config/install/core.entity_form_display.comment.comment.default.yml b/core/profiles/standard/config/install/core.entity_form_display.comment.comment.default.yml index fa5d834ce799cc6144a0179eac572bfca66c5539..1010be292488569234b654548ba3234bf8d9ae1e 100644 --- a/core/profiles/standard/config/install/core.entity_form_display.comment.comment.default.yml +++ b/core/profiles/standard/config/install/core.entity_form_display.comment.comment.default.yml @@ -13,9 +13,11 @@ mode: default content: author: weight: -2 + region: content comment_body: type: text_textarea weight: 11 + region: content settings: rows: 5 placeholder: '' @@ -23,6 +25,7 @@ content: subject: type: string_textfield weight: 10 + region: content settings: size: 60 placeholder: '' diff --git a/core/profiles/standard/config/install/core.entity_form_display.node.article.default.yml b/core/profiles/standard/config/install/core.entity_form_display.node.article.default.yml index 79156b2e876f36bd76fc74f488d9e52046538d22..c94e36e38831c23116940b5a653d819984539f81 100644 --- a/core/profiles/standard/config/install/core.entity_form_display.node.article.default.yml +++ b/core/profiles/standard/config/install/core.entity_form_display.node.article.default.yml @@ -21,6 +21,7 @@ content: body: type: text_textarea_with_summary weight: 1 + region: content settings: rows: 9 summary_rows: 3 @@ -29,16 +30,19 @@ content: comment: type: comment_default weight: 20 + region: content settings: { } third_party_settings: { } created: type: datetime_timestamp weight: 10 + region: content settings: { } third_party_settings: { } field_image: type: image_image weight: 4 + region: content settings: progress_indicator: throbber preview_image_style: thumbnail @@ -46,11 +50,13 @@ content: field_tags: type: entity_reference_autocomplete_tags weight: 3 + region: content settings: { } third_party_settings: { } path: type: path weight: 30 + region: content settings: { } third_party_settings: { } promote: @@ -58,16 +64,19 @@ content: settings: display_label: true weight: 15 + region: content third_party_settings: { } sticky: type: boolean_checkbox settings: display_label: true weight: 16 + region: content third_party_settings: { } title: type: string_textfield weight: 0 + region: content settings: size: 60 placeholder: '' @@ -75,6 +84,7 @@ content: uid: type: entity_reference_autocomplete weight: 5 + region: content settings: match_operator: CONTAINS size: 60 diff --git a/core/profiles/standard/config/install/core.entity_form_display.node.page.default.yml b/core/profiles/standard/config/install/core.entity_form_display.node.page.default.yml index 1fef06d1e8a9c4309348272fc1857c42e1643492..0b7ffd133cf103af27d8e742cc33eb0eeb6744f4 100644 --- a/core/profiles/standard/config/install/core.entity_form_display.node.page.default.yml +++ b/core/profiles/standard/config/install/core.entity_form_display.node.page.default.yml @@ -15,6 +15,7 @@ content: body: type: text_textarea_with_summary weight: 31 + region: content settings: rows: 9 summary_rows: 3 @@ -23,11 +24,13 @@ content: created: type: datetime_timestamp weight: 10 + region: content settings: { } third_party_settings: { } path: type: path weight: 30 + region: content settings: { } third_party_settings: { } promote: @@ -35,16 +38,19 @@ content: settings: display_label: true weight: 15 + region: content third_party_settings: { } sticky: type: boolean_checkbox settings: display_label: true weight: 16 + region: content third_party_settings: { } title: type: string_textfield weight: -5 + region: content settings: size: 60 placeholder: '' @@ -52,6 +58,7 @@ content: uid: type: entity_reference_autocomplete weight: 5 + region: content settings: match_operator: CONTAINS size: 60 diff --git a/core/profiles/standard/config/install/core.entity_form_display.user.user.default.yml b/core/profiles/standard/config/install/core.entity_form_display.user.user.default.yml index 466b6e0b38e311c316c1735184eadbd49d7b3efa..683222926890d865dd06fcdc618b1fd8bfa1c352 100644 --- a/core/profiles/standard/config/install/core.entity_form_display.user.user.default.yml +++ b/core/profiles/standard/config/install/core.entity_form_display.user.user.default.yml @@ -14,12 +14,16 @@ mode: default content: account: weight: -10 + region: content contact: weight: 5 + region: content language: weight: 0 + region: content timezone: weight: 6 + region: content user_picture: type: image_image settings: @@ -27,4 +31,5 @@ content: preview_image_style: thumbnail third_party_settings: { } weight: -1 + region: content hidden: { } 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 bd52f77507d0831d8627e3f66a8fe20ee17f8f7e..e494882d40a8836a0c0f4fce17dd91ee55271284 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 @@ -15,6 +15,7 @@ content: label: hidden type: text_default weight: 0 + region: content settings: { } third_party_settings: { } 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 1ed49ce2f6c5cc05b427782101107df5a8e81ea1..6ae213d3ee0d356970eb84dffb2ace80b6a79c93 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 @@ -15,8 +15,10 @@ content: label: hidden type: text_default weight: 0 + region: content settings: { } third_party_settings: { } links: weight: 100 + region: content 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 98a2de8ab04bb9d6520f42720acdb43bf6a330c5..5c432527bee86267a4124e9ef46f19ceb8f85480 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 @@ -22,12 +22,14 @@ content: body: type: text_default weight: 0 + region: content settings: { } third_party_settings: { } label: hidden comment: type: comment_default weight: 110 + region: content label: above settings: view_mode: default @@ -36,6 +38,7 @@ content: field_image: type: image weight: -1 + region: content settings: image_style: large image_link: '' @@ -44,12 +47,14 @@ content: field_tags: type: entity_reference_label weight: 10 + region: content label: above settings: link: true third_party_settings: { } links: weight: 100 + region: content 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 75a14a3fb456b11f129e12aec5f7488cda729b26..84660b6d2bd2b6ba734139b2472b1c54124652df 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 @@ -17,6 +17,7 @@ mode: rss content: links: weight: 100 + region: content 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 43ee079e1f86758f8c0c25043fbd0661aa4607ee..7b96908bed1c460a0525aadebde8847400954d00 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 @@ -21,6 +21,7 @@ content: body: type: text_summary_or_trimmed weight: 0 + region: content settings: trim_length: 600 third_party_settings: { } @@ -28,6 +29,7 @@ content: field_image: type: image weight: -1 + region: content settings: image_style: medium image_link: content @@ -36,12 +38,14 @@ content: field_tags: type: entity_reference_label weight: 10 + region: content settings: link: true third_party_settings: { } label: above links: weight: 100 + region: content 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 dcb2d3eceee91406fb351003ef74bdb10f789c3b..8afd9423ec6b1ff096449348c92b555d415c9877 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 @@ -16,8 +16,10 @@ content: label: hidden type: text_default weight: 100 + region: content settings: { } third_party_settings: { } links: weight: 101 + region: content 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 f235a10eded6e473b259d06a95cea5a4dd90541d..bc7a68c5b5fd3f69ad1b1685cfc9efde1e6cd799 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 @@ -17,9 +17,11 @@ content: label: hidden type: text_summary_or_trimmed weight: 100 + region: content settings: trim_length: 600 third_party_settings: { } links: weight: 101 + region: content 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 4c13792449077d5a69223bee3a5d58ce6abf54db..2ff13ad10f0254e00544b55d8b1700fe94a1d310 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 @@ -16,6 +16,7 @@ content: user_picture: type: image weight: 0 + region: content settings: image_style: thumbnail image_link: content 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 9e4621d5e497ec74d7e53e6a7a161733371e6fb2..ef1fdd79ce427965d90c62b99925f2c8b41aff1e 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 @@ -14,9 +14,11 @@ mode: default content: member_for: weight: 5 + region: content user_picture: type: image weight: 0 + region: content settings: image_style: thumbnail image_link: content diff --git a/core/tests/Drupal/KernelTests/Core/Entity/EntityDisplayFormBaseTest.php b/core/tests/Drupal/KernelTests/Core/Entity/EntityDisplayFormBaseTest.php new file mode 100644 index 0000000000000000000000000000000000000000..09124c0f0218c929f86868ffaf19479417c647b2 --- /dev/null +++ b/core/tests/Drupal/KernelTests/Core/Entity/EntityDisplayFormBaseTest.php @@ -0,0 +1,296 @@ +<?php + +namespace Drupal\KernelTests\Core\Entity; + +use Drupal\Core\Entity\Display\EntityDisplayInterface; +use Drupal\Core\Form\FormState; +use Drupal\field_ui\Form\EntityViewDisplayEditForm; +use Drupal\KernelTests\KernelTestBase; + +/** + * @coversDefaultClass \Drupal\field_ui\Form\EntityDisplayFormBase + * + * @group Entity + */ +class EntityDisplayFormBaseTest extends KernelTestBase { + + /** + * @covers ::copyFormValuesToEntity + */ + public function testCopyFormValuesToEntity() { + $field_values = []; + $entity = $this->prophesize(EntityDisplayInterface::class); + $entity->getPluginCollections()->willReturn([]); + + // A field with no initial values, with mismatched submitted values, type is + // hidden. + $entity->getComponent('new_field_mismatch_type_hidden')->willReturn([]); + $field_values['new_field_mismatch_type_hidden'] = [ + 'weight' => 0, + 'type' => 'hidden', + 'region' => 'content', + ]; + $entity + ->setComponent('new_field_mismatch_type_hidden', [ + 'weight' => 0, + 'region' => 'content', + ]) + ->will(function($args) { + // On subsequent calls, getComponent() will return the newly set values, + // plus the updated type value. + $args[1] += ['type' => 'textfield']; + $this->getComponent($args[0])->willReturn($args[1]); + $this->setComponent($args[0], $args[1])->shouldBeCalled(); + }) + ->shouldBeCalled(); + + // A field with no initial values, with mismatched submitted values, type is + // visible. + $entity->getComponent('new_field_mismatch_type_visible')->willReturn([]); + $field_values['new_field_mismatch_type_visible'] = [ + 'weight' => 0, + 'type' => 'textfield', + 'region' => 'hidden', + ]; + $entity + ->setComponent('new_field_mismatch_type_visible', [ + 'weight' => 0, + 'type' => 'textfield', + ]) + ->will(function($args) { + // On subsequent calls, getComponent() will return the newly set values, + // plus the updated region value. + $args[1] += ['region' => 'content']; + $this->getComponent($args[0])->willReturn($args[1]); + $this->setComponent($args[0], $args[1])->shouldBeCalled(); + }) + ->shouldBeCalled(); + + // An initially hidden field, with identical submitted values. + $entity->getComponent('field_hidden_no_changes') + ->willReturn([ + 'weight' => 0, + 'type' => 'hidden', + 'region' => 'hidden', + ]); + $field_values['field_hidden_no_changes'] = [ + 'weight' => 0, + 'type' => 'hidden', + 'region' => 'hidden', + ]; + $entity->removeComponent('field_hidden_no_changes') + ->will(function ($args) { + // On subsequent calls, getComponent() will return an empty array. + $this->getComponent($args[0])->willReturn([]); + }) + ->shouldBeCalled(); + + // An initially visible field, with identical submitted values. + $entity->getComponent('field_visible_no_changes') + ->willReturn([ + 'weight' => 0, + 'type' => 'textfield', + 'region' => 'content', + ]); + $field_values['field_visible_no_changes'] = [ + 'weight' => 0, + 'type' => 'textfield', + 'region' => 'content', + ]; + $entity + ->setComponent('field_visible_no_changes', [ + 'weight' => 0, + 'type' => 'textfield', + 'region' => 'content', + ]) + ->shouldBeCalled(); + + // An initially hidden field, with a submitted type change. + $entity->getComponent('field_start_hidden_change_type') + ->willReturn([ + 'weight' => 0, + 'type' => 'hidden', + 'region' => 'hidden', + ]); + $field_values['field_start_hidden_change_type'] = [ + 'weight' => 0, + 'type' => 'textfield', + 'region' => 'hidden', + ]; + $entity + ->setComponent('field_start_hidden_change_type', [ + 'weight' => 0, + 'type' => 'textfield', + ]) + ->will(function($args) { + // On subsequent calls, getComponent() will return the newly set values, + // plus the updated region value. + $args[1] += ['region' => 'content']; + $this->getComponent($args[0])->willReturn($args[1]); + $this->setComponent($args[0], $args[1])->shouldBeCalled(); + }) + ->shouldBeCalled(); + + // An initially hidden field, with a submitted region change. + $entity->getComponent('field_start_hidden_change_region') + ->willReturn([ + 'weight' => 0, + 'type' => 'hidden', + 'region' => 'hidden', + ]); + $field_values['field_start_hidden_change_region'] = [ + 'weight' => 0, + 'type' => 'hidden', + 'region' => 'content', + ]; + $entity + ->setComponent('field_start_hidden_change_region', [ + 'weight' => 0, + 'region' => 'content', + ]) + ->will(function($args) { + // On subsequent calls, getComponent() will return the newly set values, + // plus the updated type value. + $args[1] += ['type' => 'textfield']; + $this->getComponent($args[0])->willReturn($args[1]); + $this->setComponent($args[0], $args[1])->shouldBeCalled(); + }) + ->shouldBeCalled(); + + // An initially hidden field, with a submitted region and type change. + $entity->getComponent('field_start_hidden_change_both') + ->willReturn([ + 'weight' => 0, + 'type' => 'hidden', + 'region' => 'hidden', + ]); + $field_values['field_start_hidden_change_both'] = [ + 'weight' => 0, + 'type' => 'textfield', + 'region' => 'content', + ]; + $entity + ->setComponent('field_start_hidden_change_both', [ + 'weight' => 0, + 'type' => 'textfield', + 'region' => 'content', + ]) + ->will(function($args) { + // On subsequent calls, getComponent() will return the newly set values. + $this->getComponent($args[0])->willReturn($args[1]); + }) + ->shouldBeCalled(); + + // An initially visible field, with a submitted type change. + $entity->getComponent('field_start_visible_change_type') + ->willReturn([ + 'weight' => 0, + 'type' => 'textfield', + 'region' => 'content', + ]); + $field_values['field_start_visible_change_type'] = [ + 'weight' => 0, + 'type' => 'hidden', + 'region' => 'content', + ]; + $entity->removeComponent('field_start_visible_change_type') + ->will(function ($args) { + // On subsequent calls, getComponent() will return an empty array. + $this->getComponent($args[0])->willReturn([]); + }) + ->shouldBeCalled(); + + // An initially visible field, with a submitted region change. + $entity->getComponent('field_start_visible_change_region') + ->willReturn([ + 'weight' => 0, + 'type' => 'textfield', + 'region' => 'content', + ]); + $field_values['field_start_visible_change_region'] = [ + 'weight' => 0, + 'type' => 'textfield', + 'region' => 'hidden', + ]; + $entity->removeComponent('field_start_visible_change_region') + ->will(function ($args) { + // On subsequent calls, getComponent() will return an empty array. + $this->getComponent($args[0])->willReturn([]); + }) + ->shouldBeCalled(); + + // An initially visible field, with a submitted region and type change. + $entity->getComponent('field_start_visible_change_both') + ->willReturn([ + 'weight' => 0, + 'type' => 'textfield', + 'region' => 'content', + ]); + $field_values['field_start_visible_change_both'] = [ + 'weight' => 0, + 'type' => 'hidden', + 'region' => 'hidden', + ]; + $entity->removeComponent('field_start_visible_change_both') + ->will(function ($args) { + // On subsequent calls, getComponent() will return an empty array. + $this->getComponent($args[0])->willReturn([]); + }) + ->shouldBeCalled(); + + // A field that is flagged for plugin settings update on the second build. + $entity->getComponent('field_plugin_settings_update') + ->willReturn([ + 'weight' => 0, + 'type' => 'textfield', + 'region' => 'content', + ]); + $field_values['field_plugin_settings_update'] = [ + 'weight' => 0, + 'type' => 'textfield', + 'region' => 'content', + 'settings_edit_form' => [ + 'third_party_settings' => [ + 'foo' => 'bar', + ], + ], + ]; + $entity + ->setComponent('field_plugin_settings_update', [ + 'weight' => 0, + 'type' => 'textfield', + 'region' => 'content', + ]) + ->will(function ($args) { + // On subsequent calls, getComponent() will return the newly set values. + $this->getComponent($args[0])->willReturn($args[1]); + $args[1] += [ + 'settings' => [], + 'third_party_settings' => [ + 'foo' => 'bar', + ], + ]; + $this->setComponent($args[0], $args[1])->shouldBeCalled(); + }) + ->shouldBeCalled(); + + $form_object = new EntityViewDisplayEditForm($this->container->get('plugin.manager.field.field_type'), $this->container->get('plugin.manager.field.formatter')); + $form_object->setEntity($entity->reveal()); + + $form = [ + '#fields' => array_keys($field_values), + '#extra' => [], + ]; + $form_state = new FormState(); + $form_state->setValues(['fields' => $field_values]); + + $form_object->buildEntity($form, $form_state); + + // Flag one field for updating plugin settings. + $form_state->set('plugin_settings_update', 'field_plugin_settings_update'); + // During form submission, buildEntity() will be called twice. Simulate that + // here to prove copyFormValuesToEntity() is idempotent. + $form_object->buildEntity($form, $form_state); + } + +}