diff --git a/core/composer.json b/core/composer.json index f38de18..10c166b 100644 --- a/core/composer.json +++ b/core/composer.json @@ -99,7 +99,6 @@ "drupal/editor": "self.version", "drupal/entity_reference": "self.version", "drupal/field": "self.version", - "drupal/field_layout": "self.version", "drupal/field_ui": "self.version", "drupal/file": "self.version", "drupal/filter": "self.version", diff --git a/core/config/schema/core.entity.schema.yml b/core/config/schema/core.entity.schema.yml index df2a2fd..de79700 100644 --- a/core/config/schema/core.entity.schema.yml +++ b/core/config/schema/core.entity.schema.yml @@ -84,6 +84,12 @@ core.entity_view_display.*.*.*: sequence: type: boolean label: 'Value' + layout_id: + type: string + label: 'Layout ID' + layout_settings: + type: layout_plugin.settings.[%parent.layout_id] + label: 'Layout settings' # Overview configuration information for form mode displays. core.entity_form_display.*.*.*: @@ -135,6 +141,12 @@ core.entity_form_display.*.*.*: sequence: type: boolean label: 'Component' + layout_id: + type: string + label: 'Layout ID' + layout_settings: + type: layout_plugin.settings.[%parent.layout_id] + label: 'Layout settings' # Default schema for entity display field with undefined type. field.formatter.settings.*: diff --git a/core/core.services.yml b/core/core.services.yml index fc0d99f..e46d0f0 100644 --- a/core/core.services.yml +++ b/core/core.services.yml @@ -645,6 +645,9 @@ services: plugin.manager.display_variant: class: Drupal\Core\Display\VariantManager parent: default_plugin_manager + plugin.manager.core.layout: + class: Drupal\Core\Layout\LayoutPluginManager + arguments: ['@container.namespaces', '@cache.discovery', '@module_handler', '@theme_handler'] plugin.manager.queue_worker: class: Drupal\Core\Queue\QueueWorkerManager parent: default_plugin_manager diff --git a/core/includes/theme.inc b/core/includes/theme.inc index 4cc6424..83412d9 100644 --- a/core/includes/theme.inc +++ b/core/includes/theme.inc @@ -1702,6 +1702,19 @@ function template_preprocess_breadcrumb(&$variables) { } /** + * Prepares variables for layout templates. + * + * @param array &$variables + * An associative array containing: + * - content: An associative array containing the properties of the element. + * Properties used: #settings, #layout. + */ +function template_preprocess_layout(&$variables) { + $variables['settings'] = isset($variables['content']['#settings']) ? $variables['content']['#settings'] : []; + $variables['layout'] = isset($variables['content']['#layout']) ? $variables['content']['#layout'] : []; +} + +/** * Callback for usort() within template_preprocess_field_multiple_value_form(). * * Sorts using ['_weight']['#value'] diff --git a/core/lib/Drupal/Core/Entity/Display/EntityDisplayInterface.php b/core/lib/Drupal/Core/Entity/Display/EntityDisplayInterface.php index 38baa15..46c9ffc 100644 --- a/core/lib/Drupal/Core/Entity/Display/EntityDisplayInterface.php +++ b/core/lib/Drupal/Core/Entity/Display/EntityDisplayInterface.php @@ -128,4 +128,25 @@ public function getTargetBundle(); */ public function setTargetBundle($bundle); + /** + * Gets the field's portion of the fully built entity display render array. + * + * @param string $field_name + * The field name. + * @param array $build + * The full render array. + * + * @return array + * The portion of the render array corresponding to the given field name. + */ + public function &getFieldFromBuild($field_name, array &$build); + + /** + * Gets the default region. + * + * @return string + * The default region for this display. + */ + public function getDefaultRegion(); + } diff --git a/core/modules/field_layout/src/Display/EntityDisplayWithLayoutInterface.php b/core/lib/Drupal/Core/Entity/Display/EntityDisplayWithLayoutInterface.php similarity index 55% rename from core/modules/field_layout/src/Display/EntityDisplayWithLayoutInterface.php rename to core/lib/Drupal/Core/Entity/Display/EntityDisplayWithLayoutInterface.php index 3bee65e..588b21e 100644 --- a/core/modules/field_layout/src/Display/EntityDisplayWithLayoutInterface.php +++ b/core/lib/Drupal/Core/Entity/Display/EntityDisplayWithLayoutInterface.php @@ -1,8 +1,7 @@ getFields($element)) { + $layout = $this->getLayout(); + $fill = []; + $fill['#process'][] = '\Drupal\Core\Render\Element\RenderElement::processGroup'; + $fill['#pre_render'][] = '\Drupal\Core\Render\Element\RenderElement::preRenderGroup'; + $regions = array_fill_keys($layout->getPluginDefinition()->getRegionNames(), $fill); + foreach ($fields as $name => $field) { + // As this is a form, #group can be used to relocate the fields. This + // avoids breaking hook_form_alter() implementations by not actually + // moving the field in the form structure. If a #group is already set, + // do not overwrite it. + if (!isset($element[$name]['#group'])) { + $element[$name]['#group'] = $field['region']; + } + } + // Ensure this will not conflict with any existing array elements by + // prefixing with an underscore. + $element['_layout'] = $layout->build($regions); + } + return $element; + } + + /** * {@inheritdoc} */ public function extractFormValues(FieldableEntityInterface $entity, array &$form, FormStateInterface $form_state) { @@ -326,7 +355,7 @@ public function getPluginCollections() { } } - return [ + return parent::getPluginCollections() + [ 'widgets' => new EntityDisplayPluginCollection($this->pluginManager, $configurations) ]; } diff --git a/core/lib/Drupal/Core/Entity/Entity/EntityViewDisplay.php b/core/lib/Drupal/Core/Entity/Entity/EntityViewDisplay.php index 74b15b6..38cc7ab 100644 --- a/core/lib/Drupal/Core/Entity/Entity/EntityViewDisplay.php +++ b/core/lib/Drupal/Core/Entity/Entity/EntityViewDisplay.php @@ -28,6 +28,8 @@ * "mode", * "content", * "hidden", + * "layout_id", + * "layout_settings", * } * ) */ @@ -278,12 +280,36 @@ public function buildMultiple(array $entities) { 'display' => $this, ]; \Drupal::moduleHandler()->alter('entity_display_build', $build_list[$id], $context); + $this->applyLayout($build_list[$id]); } return $build_list; } /** + * Applies the layout to the build. + * + * @param array $build + * Render array. + */ + protected function applyLayout(array &$build) { + if ($fields = $this->getFields($build)) { + $regions = []; + foreach ($fields as $name => $field) { + // Move the field from the top-level of $build into a region-specific + // section. + // @todo Ideally the array structure would remain unchanged, see + // https://www.drupal.org/node/2846393. + $regions[$field['region']][$name] = $build[$name]; + unset($build[$name]); + } + // Ensure this will not conflict with any existing array elements by + // prefixing with an underscore. + $build['_layout'] = $this->getLayout()->build($regions); + } + } + + /** * {@inheritdoc} */ public function getPluginCollections() { @@ -297,7 +323,7 @@ public function getPluginCollections() { } } - return [ + return parent::getPluginCollections() + [ 'formatters' => new EntityDisplayPluginCollection($this->pluginManager, $configurations) ]; } diff --git a/core/lib/Drupal/Core/Entity/EntityDisplayBase.php b/core/lib/Drupal/Core/Entity/EntityDisplayBase.php index ca106b2..bdf290d 100644 --- a/core/lib/Drupal/Core/Entity/EntityDisplayBase.php +++ b/core/lib/Drupal/Core/Entity/EntityDisplayBase.php @@ -4,13 +4,16 @@ use Drupal\Core\Config\Entity\ConfigEntityBase; use Drupal\Core\Config\Entity\ConfigEntityInterface; +use Drupal\Core\Entity\Display\EntityDisplayWithLayoutInterface; use Drupal\Core\Field\FieldDefinitionInterface; use Drupal\Core\Entity\Display\EntityDisplayInterface; +use Drupal\Core\Layout\LayoutInterface; +use Drupal\Core\Plugin\DefaultSingleLazyPluginCollection; /** * Provides a common base class for entity view and form displays. */ -abstract class EntityDisplayBase extends ConfigEntityBase implements EntityDisplayInterface { +abstract class EntityDisplayBase extends ConfigEntityBase implements EntityDisplayInterface, EntityDisplayWithLayoutInterface { /** * The 'mode' for runtime EntityDisplay objects used to render entities with @@ -50,6 +53,13 @@ protected $fieldDefinitions; /** + * An array of information about the extra fields for this display context. + * + * @var array + */ + protected $extraFields; + + /** * View or form mode to be displayed. * * @var string @@ -79,6 +89,27 @@ protected $hidden = []; /** + * The layout ID. + * + * @var string + */ + protected $layout_id = 'layout_default'; + + /** + * The layout settings. + * + * @var array + */ + protected $layout_settings = []; + + /** + * The plugin collection that holds the layout plugin for this entity. + * + * @var \Drupal\Core\Plugin\DefaultSingleLazyPluginCollection + */ + protected $pluginCollection; + + /** * The original view or form mode that was requested (case of view/form modes * being configured to fall back to the 'default' display). * @@ -156,9 +187,7 @@ protected function init() { 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); - $extra_fields = isset($extra_fields[$context]) ? $extra_fields[$context] : []; + $extra_fields = $this->getExtraFields(); foreach ($extra_fields as $name => $definition) { if (!isset($this->content[$name]) && !isset($this->hidden[$name])) { // Extra fields are visible by default unless they explicitly say so. @@ -412,6 +441,22 @@ public function getHighestWeight() { } /** + * Gets the extra fields for this display. + * + * @return array + * An array of extra field info for the entity type and bundle used by this + * entity display. + */ + protected function getExtraFields() { + if (!isset($this->extraFields)) { + $context = $this->displayContext == 'view' ? 'display' : $this->displayContext; + $extra_fields = \Drupal::service('entity_field.manager')->getExtraFields($this->targetEntityType, $this->bundle); + $this->extraFields = isset($extra_fields[$context]) ? $extra_fields[$context] : []; + } + return $this->extraFields; + } + + /** * Gets the field definition of a field. */ protected function getFieldDefinition($field_name) { @@ -424,7 +469,7 @@ protected function getFieldDefinition($field_name) { */ protected function getFieldDefinitions() { if (!isset($this->fieldDefinitions)) { - $definitions = \Drupal::entityManager()->getFieldDefinitions($this->targetEntityType, $this->bundle); + $definitions = \Drupal::service('entity_field.manager')->getFieldDefinitions($this->targetEntityType, $this->bundle); // For "official" view modes and form modes, ignore fields whose // definition states they should not be displayed. if ($this->mode !== static::CUSTOM_MODE) { @@ -545,13 +590,112 @@ protected function getPluginRemovedDependencies(array $plugin_dependencies, arra } /** + * Gets the fields that need to be processed. + * + * @param array $build + * A renderable array representing the entity content or form. + * + * @return array + * An array of configurable fields present in the build. + */ + protected function getFields(array $build) { + $components = $this->getComponents(); + + // Ignore any extra fields from the list of field definitions. Field + // definitions can have a non-configurable display, but all extra fields are + // always displayed. + $field_definitions = array_diff_key($this->getFieldDefinitions(), $this->getExtraFields()); + + $fields_to_exclude = array_filter($field_definitions, function (FieldDefinitionInterface $field_definition) { + // Remove fields with a non-configurable display. + return !$field_definition->isDisplayConfigurable($this->displayContext); + }); + $components = array_diff_key($components, $fields_to_exclude); + + // Only include fields present in the build. + $components = array_intersect_key($components, $build); + + return $components; + } + + /** * Gets the default region. * * @return string * The default region for this display. */ - protected function getDefaultRegion() { - return 'content'; + public function getDefaultRegion() { + return $this->getLayout()->getPluginDefinition()->getDefaultRegion(); + } + + /** + * {@inheritdoc} + */ + public function getLayoutId() { + return $this->layout_id; + } + + /** + * {@inheritdoc} + */ + public function getLayoutSettings() { + return $this->layout_settings; + } + + /** + * {@inheritdoc} + */ + public function setLayoutFromId($layout_id, array $layout_settings = []) { + $original_layout_id = $this->getLayoutId(); + $this->layout_id = $layout_id; + $this->getPluginCollection()->addInstanceID($layout_id, $layout_settings); + + if ($original_layout_id !== $layout_id) { + // @todo Devise a mechanism for mapping old regions to new ones in + // https://www.drupal.org/node/2796877. + $layout_definition = $this->getLayout()->getPluginDefinition(); + $new_region = $layout_definition->getDefaultRegion(); + $layout_regions = $layout_definition->getRegions(); + foreach ($this->getComponents() as $name => $component) { + if (!isset($component['region']) || !isset($layout_regions[$component['region']])) { + $component['region'] = $new_region; + $this->setComponent($name, $component); + } + } + } + return $this; + } + + /** + * {@inheritdoc} + */ + public function setLayout(LayoutInterface $layout) { + $this->setLayoutFromId($layout->getPluginId(), $layout->getConfiguration()); + return $this; + } + + /** + * {@inheritdoc} + */ + public function getLayout() { + return $this->getPluginCollection()->get($this->getLayoutId()); + } + + /** + * {@inheritdoc} + */ + public function &getFieldFromBuild($field_name, array &$build) { + $field_component = $this->getComponent($field_name); + if (isset($build[$field_name])) { + $output = &$build[$field_name]; + } + elseif (isset($field_component['region']) && isset($build['_layout'][$field_component['region']][$field_name])) { + $output = &$build['_layout'][$field_component['region']][$field_name]; + } + else { + $output = []; + } + return $output; } /** @@ -595,4 +739,26 @@ protected function getLogger() { return \Drupal::logger('system'); } + /** + * {@inheritdoc} + */ + public function getPluginCollections() { + return [ + 'layout_settings' => $this->getPluginCollection(), + ]; + } + + /** + * Encapsulates the creation of the layout plugin collection. + * + * @return \Drupal\Core\Plugin\DefaultSingleLazyPluginCollection + * The layout plugin collection. + */ + protected function getPluginCollection() { + if (!$this->pluginCollection) { + $this->pluginCollection = new DefaultSingleLazyPluginCollection(\Drupal::service('plugin.manager.core.layout'), $this->getLayoutId(), $this->getLayoutSettings()); + } + return $this->pluginCollection; + } + } diff --git a/core/lib/Drupal/Core/Entity/EntityViewBuilder.php b/core/lib/Drupal/Core/Entity/EntityViewBuilder.php index 2c3340e..778c152 100644 --- a/core/lib/Drupal/Core/Entity/EntityViewBuilder.php +++ b/core/lib/Drupal/Core/Entity/EntityViewBuilder.php @@ -399,13 +399,8 @@ public function viewField(FieldItemListInterface $items, $display_options = []) $field_name = $items->getFieldDefinition()->getName(); $display = $this->getSingleFieldDisplay($entity, $field_name, $display_options); - $output = []; $build = $display->build($entity); - if (isset($build[$field_name])) { - $output = $build[$field_name]; - } - - return $output; + return $display->getFieldFromBuild($field_name, $build); } /** @@ -465,11 +460,14 @@ protected function getSingleFieldDisplay($entity, $field_name, $display_options) $bundle = $entity->bundle(); $key = $entity_type_id . ':' . $bundle . ':' . $field_name . ':' . Crypt::hashBase64(serialize($display_options)); if (!isset($this->singleFieldDisplays[$key])) { - $this->singleFieldDisplays[$key] = EntityViewDisplay::create([ + $display = EntityViewDisplay::create([ 'targetEntityType' => $entity_type_id, 'bundle' => $bundle, 'status' => TRUE, - ])->setComponent($field_name, $display_options); + ]); + $display_options += ['region' => $display->getDefaultRegion()]; + $display->setComponent($field_name, $display_options); + $this->singleFieldDisplays[$key] = $display; } $display = $this->singleFieldDisplays[$key]; } diff --git a/core/lib/Drupal/Core/Layout/Annotation/Layout.php b/core/lib/Drupal/Core/Layout/Annotation/Layout.php index 1cb5ff0..0b3c303 100644 --- a/core/lib/Drupal/Core/Layout/Annotation/Layout.php +++ b/core/lib/Drupal/Core/Layout/Annotation/Layout.php @@ -14,11 +14,6 @@ * * Plugin namespace: Plugin\Layout * - * @internal - * The layout system is currently experimental and should only be leveraged by - * experimental modules and development releases of contributed modules. - * See https://www.drupal.org/core/experimental for more information. - * * @see \Drupal\Core\Layout\LayoutInterface * @see \Drupal\Core\Layout\LayoutDefault * @see \Drupal\Core\Layout\LayoutPluginManager @@ -72,9 +67,9 @@ class Layout extends Plugin { /** * The template file to render this layout (relative to the 'path' given). * - * If specified, then the layout_discovery module will register the template - * with hook_theme() and the module or theme registering this layout does not - * need to do it. + * If specified, then the system module will register the template with + * hook_theme() and the module or theme registering this layout does not need + * to do it. * * @var string optional * diff --git a/core/lib/Drupal/Core/Layout/LayoutDefault.php b/core/lib/Drupal/Core/Layout/LayoutDefault.php index 22edb85..6e53d00 100644 --- a/core/lib/Drupal/Core/Layout/LayoutDefault.php +++ b/core/lib/Drupal/Core/Layout/LayoutDefault.php @@ -7,11 +7,6 @@ /** * Provides a default class for Layout plugins. - * - * @internal - * The layout system is currently experimental and should only be leveraged by - * experimental modules and development releases of contributed modules. - * See https://www.drupal.org/core/experimental for more information. */ class LayoutDefault extends PluginBase implements LayoutInterface { diff --git a/core/lib/Drupal/Core/Layout/LayoutDefinition.php b/core/lib/Drupal/Core/Layout/LayoutDefinition.php index afbce7e..686cf81 100644 --- a/core/lib/Drupal/Core/Layout/LayoutDefinition.php +++ b/core/lib/Drupal/Core/Layout/LayoutDefinition.php @@ -10,11 +10,6 @@ /** * Provides an implementation of a layout definition and its metadata. - * - * @internal - * The layout system is currently experimental and should only be leveraged by - * experimental modules and development releases of contributed modules. - * See https://www.drupal.org/core/experimental for more information. */ class LayoutDefinition extends PluginDefinition implements PluginDefinitionInterface, DerivablePluginDefinitionInterface, DependentPluginDefinitionInterface { diff --git a/core/lib/Drupal/Core/Layout/LayoutInterface.php b/core/lib/Drupal/Core/Layout/LayoutInterface.php index bb60df0..32ee74d 100644 --- a/core/lib/Drupal/Core/Layout/LayoutInterface.php +++ b/core/lib/Drupal/Core/Layout/LayoutInterface.php @@ -8,11 +8,6 @@ /** * Provides an interface for static Layout plugins. - * - * @internal - * The layout system is currently experimental and should only be leveraged by - * experimental modules and development releases of contributed modules. - * See https://www.drupal.org/core/experimental for more information. */ interface LayoutInterface extends PluginInspectionInterface, DerivativeInspectionInterface, ConfigurablePluginInterface { diff --git a/core/lib/Drupal/Core/Layout/LayoutPluginManager.php b/core/lib/Drupal/Core/Layout/LayoutPluginManager.php index 0a28785..179d1ed 100644 --- a/core/lib/Drupal/Core/Layout/LayoutPluginManager.php +++ b/core/lib/Drupal/Core/Layout/LayoutPluginManager.php @@ -15,11 +15,6 @@ /** * Provides a plugin manager for layouts. - * - * @internal - * The layout system is currently experimental and should only be leveraged by - * experimental modules and development releases of contributed modules. - * See https://www.drupal.org/core/experimental for more information. */ class LayoutPluginManager extends DefaultPluginManager implements LayoutPluginManagerInterface { diff --git a/core/lib/Drupal/Core/Layout/LayoutPluginManagerInterface.php b/core/lib/Drupal/Core/Layout/LayoutPluginManagerInterface.php index df15be0..c0e606d 100644 --- a/core/lib/Drupal/Core/Layout/LayoutPluginManagerInterface.php +++ b/core/lib/Drupal/Core/Layout/LayoutPluginManagerInterface.php @@ -6,11 +6,6 @@ /** * Provides the interface for a plugin manager of layouts. - * - * @internal - * The layout system is currently experimental and should only be leveraged by - * experimental modules and development releases of contributed modules. - * See https://www.drupal.org/core/experimental for more information. */ interface LayoutPluginManagerInterface extends CategorizingPluginManagerInterface { diff --git a/core/lib/Drupal/Core/Layout/Plugin/Layout/DefaultLayout.php b/core/lib/Drupal/Core/Layout/Plugin/Layout/DefaultLayout.php new file mode 100644 index 0000000..2d65047 --- /dev/null +++ b/core/lib/Drupal/Core/Layout/Plugin/Layout/DefaultLayout.php @@ -0,0 +1,33 @@ +container->get('router.builder')->rebuild(); $this->installEntitySchema('entity_test_rev'); $this->createFieldWithStorage(); + + EntityViewMode::create([ + 'id' => 'entity_test.full', + 'targetEntityType' => 'entity_test', + 'status' => TRUE, + 'enabled' => TRUE, + 'label' => 'Full', + ])->save(); } /** @@ -60,6 +69,7 @@ public function testEntityDisplayBuild() { ], ]; $display->setComponent($this->fieldTestData->field_name_2, $display_options_2); + $display->save(); // View all fields. $content = $display->build($entity); @@ -77,6 +87,7 @@ public function testEntityDisplayBuild() { $entity = clone($entity_init); $display_options['label'] = 'hidden'; $display->setComponent($this->fieldTestData->field_name, $display_options); + $display->save(); $content = $display->build($entity); $this->render($content); $this->assertNoRaw($this->fieldTestData->field->getLabel(), "Hidden label: label is not displayed."); @@ -84,6 +95,7 @@ public function testEntityDisplayBuild() { // Field hidden. $entity = clone($entity_init); $display->removeComponent($this->fieldTestData->field_name); + $display->save(); $content = $display->build($entity); $this->render($content); $this->assertNoRaw($this->fieldTestData->field->getLabel(), "Hidden field: label is not displayed."); @@ -101,6 +113,7 @@ public function testEntityDisplayBuild() { 'test_formatter_setting_multiple' => $formatter_setting, ], ]); + $display->save(); $content = $display->build($entity); $this->render($content); $expected_output = $formatter_setting; @@ -119,6 +132,7 @@ public function testEntityDisplayBuild() { 'test_formatter_setting_additional' => $formatter_setting, ], ]); + $display->save(); $content = $display->build($entity); $this->render($content); foreach ($values as $delta => $value) { @@ -139,6 +153,7 @@ public function testEntityDisplayViewMultiple() { ->setComponent($this->fieldTestData->field_name, [ 'type' => 'field_test_with_prepare_view', ]); + $display->save(); // Create two entities. $entity1 = EntityTest::create(['id' => 1, 'type' => 'entity_test']); diff --git a/core/modules/field_layout/config/schema/field_layout.schema.yml b/core/modules/field_layout/config/schema/field_layout.schema.yml deleted file mode 100644 index 185fb4e..0000000 --- a/core/modules/field_layout/config/schema/field_layout.schema.yml +++ /dev/null @@ -1,16 +0,0 @@ -core.entity_view_display.*.*.*.third_party.field_layout: - type: field_layout.third_party_settings - -core.entity_form_display.*.*.*.third_party.field_layout: - type: field_layout.third_party_settings - -field_layout.third_party_settings: - type: mapping - label: 'Per-view-mode field layout settings' - mapping: - id: - type: string - label: 'Layout ID' - settings: - type: layout_plugin.settings.[%parent.id] - label: 'Layout settings' diff --git a/core/modules/field_layout/field_layout.info.yml b/core/modules/field_layout/field_layout.info.yml index 237f18d..62f225a 100644 --- a/core/modules/field_layout/field_layout.info.yml +++ b/core/modules/field_layout/field_layout.info.yml @@ -4,5 +4,3 @@ description: 'Adds layout capabilities to the Field UI.' package: Core (Experimental) version: VERSION core: 8.x -dependencies: - - layout_discovery diff --git a/core/modules/field_layout/field_layout.install b/core/modules/field_layout/field_layout.install deleted file mode 100644 index 5956bf1..0000000 --- a/core/modules/field_layout/field_layout.install +++ /dev/null @@ -1,42 +0,0 @@ -ensureLayout()->save(); - }; - array_map($entity_save, EntityViewDisplay::loadMultiple()); - array_map($entity_save, EntityFormDisplay::loadMultiple()); - - // Invalidate the render cache since all content will now have a layout. - Cache::invalidateTags(['rendered']); -} - -/** - * Implements hook_uninstall(). - */ -function field_layout_uninstall() { - // Reset each entity display to use the one-column layout to best approximate - // the absence of layouts. - $entity_save = function (EntityDisplayWithLayoutInterface $entity) { - $entity->setLayoutId('layout_onecol')->save(); - }; - array_map($entity_save, EntityViewDisplay::loadMultiple()); - array_map($entity_save, EntityFormDisplay::loadMultiple()); - - // Invalidate the render cache since all content will no longer have a layout. - Cache::invalidateTags(['rendered']); -} diff --git a/core/modules/field_layout/field_layout.module b/core/modules/field_layout/field_layout.module index 5a5fa11..1607a8a 100644 --- a/core/modules/field_layout/field_layout.module +++ b/core/modules/field_layout/field_layout.module @@ -5,15 +5,7 @@ * Provides hook implementations for Field Layout. */ -use Drupal\Core\Entity\ContentEntityFormInterface; -use Drupal\Core\Entity\Display\EntityViewDisplayInterface; -use Drupal\Core\Entity\EntityInterface; -use Drupal\Core\Form\FormStateInterface; use Drupal\Core\Routing\RouteMatchInterface; -use Drupal\field_layout\Display\EntityDisplayWithLayoutInterface; -use Drupal\field_layout\Entity\FieldLayoutEntityFormDisplay; -use Drupal\field_layout\Entity\FieldLayoutEntityViewDisplay; -use Drupal\field_layout\FieldLayoutBuilder; use Drupal\field_layout\Form\FieldLayoutEntityFormDisplayEditForm; use Drupal\field_layout\Form\FieldLayoutEntityViewDisplayEditForm; @@ -35,35 +27,9 @@ function field_layout_help($route_name, RouteMatchInterface $route_match) { */ function field_layout_entity_type_alter(array &$entity_types) { /** @var $entity_types \Drupal\Core\Entity\EntityTypeInterface[] */ - $entity_types['entity_view_display']->setClass(FieldLayoutEntityViewDisplay::class); - $entity_types['entity_form_display']->setClass(FieldLayoutEntityFormDisplay::class); - // The form classes are only needed when Field UI is installed. if (\Drupal::moduleHandler()->moduleExists('field_ui')) { $entity_types['entity_view_display']->setFormClass('edit', FieldLayoutEntityViewDisplayEditForm::class); $entity_types['entity_form_display']->setFormClass('edit', FieldLayoutEntityFormDisplayEditForm::class); } } - -/** - * Implements hook_entity_view_alter(). - */ -function field_layout_entity_view_alter(array &$build, EntityInterface $entity, EntityViewDisplayInterface $display) { - if ($display instanceof EntityDisplayWithLayoutInterface) { - \Drupal::classResolver()->getInstanceFromDefinition(FieldLayoutBuilder::class) - ->buildView($build, $display); - } -} - -/** - * Implements hook_form_alter(). - */ -function field_layout_form_alter(&$form, FormStateInterface $form_state, $form_id) { - $form_object = $form_state->getFormObject(); - if ($form_object instanceof ContentEntityFormInterface && $display = $form_object->getFormDisplay($form_state)) { - if ($display instanceof EntityDisplayWithLayoutInterface) { - \Drupal::classResolver()->getInstanceFromDefinition(FieldLayoutBuilder::class) - ->buildForm($form, $display); - } - } -} diff --git a/core/modules/field_layout/src/Entity/FieldLayoutEntityDisplayTrait.php b/core/modules/field_layout/src/Entity/FieldLayoutEntityDisplayTrait.php deleted file mode 100644 index 59377be..0000000 --- a/core/modules/field_layout/src/Entity/FieldLayoutEntityDisplayTrait.php +++ /dev/null @@ -1,153 +0,0 @@ -getDefinition($layout_id); - } - - /** - * Implements \Drupal\field_layout\Display\EntityDisplayWithLayoutInterface::getLayoutId(). - */ - public function getLayoutId() { - return $this->getThirdPartySetting('field_layout', 'id'); - } - - /** - * Implements \Drupal\field_layout\Display\EntityDisplayWithLayoutInterface::getLayoutSettings(). - */ - public function getLayoutSettings() { - return $this->getThirdPartySetting('field_layout', 'settings', []); - } - - /** - * Implements \Drupal\field_layout\Display\EntityDisplayWithLayoutInterface::setLayoutId(). - */ - public function setLayoutId($layout_id, array $layout_settings = []) { - if ($this->getLayoutId() !== $layout_id) { - // @todo Devise a mechanism for mapping old regions to new ones in - // https://www.drupal.org/node/2796877. - $layout_definition = $this->getLayoutDefinition($layout_id); - $new_region = $layout_definition->getDefaultRegion(); - $layout_regions = $layout_definition->getRegions(); - foreach ($this->getComponents() as $name => $component) { - if (isset($component['region']) && !isset($layout_regions[$component['region']])) { - $component['region'] = $new_region; - $this->setComponent($name, $component); - } - } - } - $this->setThirdPartySetting('field_layout', 'id', $layout_id); - // Instantiate the plugin and consult it for the updated plugin - // configuration. Once layouts are no longer stored as third party settings, - // this will be handled by the code in - // \Drupal\Core\Config\Entity\ConfigEntityBase::set() that handles - // \Drupal\Core\Entity\EntityWithPluginCollectionInterface. - $layout_settings = $this->doGetLayout($layout_id, $layout_settings)->getConfiguration(); - $this->setThirdPartySetting('field_layout', 'settings', $layout_settings); - return $this; - } - - /** - * Implements \Drupal\field_layout\Display\EntityDisplayWithLayoutInterface::setLayout(). - */ - public function setLayout(LayoutInterface $layout) { - $this->setLayoutId($layout->getPluginId(), $layout->getConfiguration()); - return $this; - } - - /** - * Implements \Drupal\field_layout\Display\EntityDisplayWithLayoutInterface::getLayout(). - */ - public function getLayout() { - return $this->doGetLayout($this->getLayoutId(), $this->getLayoutSettings()); - } - - /** - * Gets the layout plugin. - * - * @param string $layout_id - * A layout plugin ID. - * @param array $layout_settings - * An array of settings. - * - * @return \Drupal\Core\Layout\LayoutInterface - * The layout plugin. - */ - protected function doGetLayout($layout_id, array $layout_settings) { - return \Drupal::service('plugin.manager.core.layout')->createInstance($layout_id, $layout_settings); - } - - /** - * Overrides \Drupal\Core\Entity\EntityDisplayBase::init(). - */ - protected function init() { - $this->ensureLayout(); - parent::init(); - } - - /** - * Overrides \Drupal\Core\Entity\EntityDisplayBase::preSave(). - */ - public function preSave(EntityStorageInterface $storage) { - parent::preSave($storage); - - // Ensure the plugin configuration is updated. Once layouts are no longer - // stored as third party settings, this will be handled by the code in - // \Drupal\Core\Config\Entity\ConfigEntityBase::preSave() that handles - // \Drupal\Core\Entity\EntityWithPluginCollectionInterface. - if ($this->getLayoutId()) { - $this->setLayout($this->getLayout()); - } - } - - /** - * {@inheritdoc} - */ - public function ensureLayout($default_layout_id = 'layout_onecol') { - if (!$this->getLayoutId()) { - $this->setLayoutId($default_layout_id); - } - - return $this; - } - - /** - * Overrides \Drupal\Core\Entity\EntityDisplayBase::calculateDependencies(). - * - * Ensure the plugin dependencies are included. Once layouts are no longer - * stored as third party settings, this will be handled by the code in - * \Drupal\Core\Config\Entity\ConfigEntityBase::calculateDependencies() that - * handles \Drupal\Core\Entity\EntityWithPluginCollectionInterface. - */ - public function calculateDependencies() { - parent::calculateDependencies(); - - // This can be called during uninstallation, so check for a valid ID first. - if ($this->getLayoutId()) { - $this->calculatePluginDependencies($this->getLayout()); - } - } - -} diff --git a/core/modules/field_layout/src/Entity/FieldLayoutEntityFormDisplay.php b/core/modules/field_layout/src/Entity/FieldLayoutEntityFormDisplay.php deleted file mode 100644 index c58938e..0000000 --- a/core/modules/field_layout/src/Entity/FieldLayoutEntityFormDisplay.php +++ /dev/null @@ -1,24 +0,0 @@ -getLayoutDefinition($this->getLayoutId())->getDefaultRegion(); - } - -} diff --git a/core/modules/field_layout/src/Entity/FieldLayoutEntityViewDisplay.php b/core/modules/field_layout/src/Entity/FieldLayoutEntityViewDisplay.php deleted file mode 100644 index 4f0c274..0000000 --- a/core/modules/field_layout/src/Entity/FieldLayoutEntityViewDisplay.php +++ /dev/null @@ -1,24 +0,0 @@ -getLayoutDefinition($this->getLayoutId())->getDefaultRegion(); - } - -} diff --git a/core/modules/field_layout/src/FieldLayoutBuilder.php b/core/modules/field_layout/src/FieldLayoutBuilder.php deleted file mode 100644 index 8ef5d04..0000000 --- a/core/modules/field_layout/src/FieldLayoutBuilder.php +++ /dev/null @@ -1,153 +0,0 @@ -layoutPluginManager = $layout_plugin_manager; - $this->entityFieldManager = $entity_field_manager; - } - - /** - * {@inheritdoc} - */ - public static function create(ContainerInterface $container) { - return new static( - $container->get('plugin.manager.core.layout'), - $container->get('entity_field.manager') - ); - } - - /** - * Applies the layout to an entity build. - * - * @param array $build - * A renderable array representing the entity content or form. - * @param \Drupal\field_layout\Display\EntityDisplayWithLayoutInterface $display - * The entity display holding the display options configured for the entity - * components. - */ - public function buildView(array &$build, EntityDisplayWithLayoutInterface $display) { - $layout_definition = $this->layoutPluginManager->getDefinition($display->getLayoutId(), FALSE); - if ($layout_definition && $fields = $this->getFields($build, $display, 'view')) { - // Add the regions to the $build in the correct order. - $regions = array_fill_keys($layout_definition->getRegionNames(), []); - - foreach ($fields as $name => $field) { - // Move the field from the top-level of $build into a region-specific - // section. - // @todo Ideally the array structure would remain unchanged, see - // https://www.drupal.org/node/2846393. - $regions[$field['region']][$name] = $build[$name]; - unset($build[$name]); - } - // Ensure this will not conflict with any existing array elements by - // prefixing with an underscore. - $build['_field_layout'] = $display->getLayout()->build($regions); - } - } - - /** - * Applies the layout to an entity form. - * - * @param array $build - * A renderable array representing the entity content or form. - * @param \Drupal\field_layout\Display\EntityDisplayWithLayoutInterface $display - * The entity display holding the display options configured for the entity - * components. - */ - public function buildForm(array &$build, EntityDisplayWithLayoutInterface $display) { - $layout_definition = $this->layoutPluginManager->getDefinition($display->getLayoutId(), FALSE); - if ($layout_definition && $fields = $this->getFields($build, $display, 'form')) { - $fill = []; - $fill['#process'][] = '\Drupal\Core\Render\Element\RenderElement::processGroup'; - $fill['#pre_render'][] = '\Drupal\Core\Render\Element\RenderElement::preRenderGroup'; - // Add the regions to the $build in the correct order. - $regions = array_fill_keys($layout_definition->getRegionNames(), $fill); - - foreach ($fields as $name => $field) { - // As this is a form, #group can be used to relocate the fields. This - // avoids breaking hook_form_alter() implementations by not actually - // moving the field in the form structure. If a #group is already set, - // do not overwrite it. - if (!isset($build[$name]['#group'])) { - $build[$name]['#group'] = $field['region']; - } - } - // Ensure this will not conflict with any existing array elements by - // prefixing with an underscore. - $build['_field_layout'] = $display->getLayout()->build($regions); - } - } - - /** - * Gets the fields that need to be processed. - * - * @param array $build - * A renderable array representing the entity content or form. - * @param \Drupal\field_layout\Display\EntityDisplayWithLayoutInterface $display - * The entity display holding the display options configured for the entity - * components. - * @param string $display_context - * The display context, either 'form' or 'view'. - * - * @return array - * An array of configurable fields present in the build. - */ - protected function getFields(array $build, EntityDisplayWithLayoutInterface $display, $display_context) { - $components = $display->getComponents(); - - // Ignore any extra fields from the list of field definitions. Field - // definitions can have a non-configurable display, but all extra fields are - // always displayed. - $field_definitions = array_diff_key( - $this->entityFieldManager->getFieldDefinitions($display->getTargetEntityTypeId(), $display->getTargetBundle()), - $this->entityFieldManager->getExtraFields($display->getTargetEntityTypeId(), $display->getTargetBundle()) - ); - - $fields_to_exclude = array_filter($field_definitions, function (FieldDefinitionInterface $field_definition) use ($display_context) { - // Remove fields with a non-configurable display. - return !$field_definition->isDisplayConfigurable($display_context); - }); - $components = array_diff_key($components, $fields_to_exclude); - - // Only include fields present in the build. - $components = array_intersect_key($components, $build); - - return $components; - } - -} diff --git a/core/modules/field_layout/src/Form/FieldLayoutEntityDisplayFormTrait.php b/core/modules/field_layout/src/Form/FieldLayoutEntityDisplayFormTrait.php index 043e5c7..1616861 100644 --- a/core/modules/field_layout/src/Form/FieldLayoutEntityDisplayFormTrait.php +++ b/core/modules/field_layout/src/Form/FieldLayoutEntityDisplayFormTrait.php @@ -5,7 +5,7 @@ use Drupal\Core\Form\FormStateInterface; use Drupal\Core\Form\SubformState; use Drupal\Core\Plugin\PluginFormInterface; -use Drupal\field_layout\Display\EntityDisplayWithLayoutInterface; +use Drupal\Core\Entity\Display\EntityDisplayWithLayoutInterface; /** * Provides shared code for entity display forms. @@ -99,7 +99,7 @@ public function form(array $form, FormStateInterface $form_state) { /** * Gets the layout plugin for the currently selected field layout. * - * @param \Drupal\field_layout\Display\EntityDisplayWithLayoutInterface $entity + * @param \Drupal\Core\Entity\Display\EntityDisplayWithLayoutInterface $entity * The current form entity. * @param \Drupal\Core\Form\FormStateInterface $form_state * The current state of the form. @@ -169,7 +169,7 @@ public function submitForm(array &$form, FormStateInterface $form_state) { /** * Gets the form entity. * - * @return \Drupal\field_layout\Display\EntityDisplayWithLayoutInterface + * @return \Drupal\Core\Entity\Display\EntityDisplayWithLayoutInterface * The current form entity. */ abstract public function getEntity(); diff --git a/core/modules/field_layout/tests/modules/field_layout_test/field_layout_test.module b/core/modules/field_layout/tests/modules/field_layout_test/field_layout_test.module index 6905c3d..f4c5864 100644 --- a/core/modules/field_layout/tests/modules/field_layout_test/field_layout_test.module +++ b/core/modules/field_layout/tests/modules/field_layout_test/field_layout_test.module @@ -10,7 +10,7 @@ */ function field_layout_test_layout_alter(&$definitions) { /** @var \Drupal\Core\Layout\LayoutDefinition[] $definitions */ - if (\Drupal::state()->get('field_layout_test.alter_regions') && isset($definitions['layout_onecol'])) { - $definitions['layout_onecol']->setRegions(['foo' => ['label' => 'Foo']]); + if (\Drupal::state()->get('field_layout_test.alter_regions') && isset($definitions['layout_default'])) { + $definitions['layout_default']->setRegions(['foo' => ['label' => 'Foo']]); } } diff --git a/core/modules/field_layout/tests/src/Functional/FieldLayoutTest.php b/core/modules/field_layout/tests/src/Functional/FieldLayoutTest.php index 058e6c3..e290bc7 100644 --- a/core/modules/field_layout/tests/src/Functional/FieldLayoutTest.php +++ b/core/modules/field_layout/tests/src/Functional/FieldLayoutTest.php @@ -44,20 +44,6 @@ protected function setUp() { } /** - * Tests an entity type that has fields shown by default. - */ - public function testNodeView() { - // By default, the one-column layout is used. - $this->drupalGet('node/1'); - $this->assertSession()->elementExists('css', '.layout--onecol'); - $this->assertSession()->elementExists('css', '.layout__region--content .field--name-body'); - - $this->drupalGet('admin/structure/types/manage/article/display'); - $this->assertEquals(['Content', 'Disabled'], $this->getRegionTitles()); - $this->assertSession()->optionExists('fields[body][region]', 'content'); - } - - /** * Tests that changes to the regions still leave the fields visible. */ public function testRegionChanges() { diff --git a/core/modules/field_layout/tests/src/FunctionalJavascript/FieldLayoutTest.php b/core/modules/field_layout/tests/src/FunctionalJavascript/FieldLayoutTest.php index 8ddbeaf..33852bb 100644 --- a/core/modules/field_layout/tests/src/FunctionalJavascript/FieldLayoutTest.php +++ b/core/modules/field_layout/tests/src/FunctionalJavascript/FieldLayoutTest.php @@ -15,7 +15,7 @@ class FieldLayoutTest extends JavascriptTestBase { /** * {@inheritdoc} */ - public static $modules = ['field_layout', 'field_ui', 'field_layout_test', 'layout_test']; + public static $modules = ['field_layout', 'field_ui', 'field_layout_test', 'layout_test', 'layout_discovery']; /** * {@inheritdoc} @@ -47,9 +47,22 @@ protected function setUp() { public function testEntityViewModes() { // By default, the field is not visible. $this->drupalGet('entity_test/1/test'); - $this->assertSession()->elementNotExists('css', '.layout__region--content .field--name-field-test-text'); + $this->assertSession()->elementNotExists('css', '.field--name-field-test-text'); $this->drupalGet('entity_test/1'); - $this->assertSession()->elementNotExists('css', '.layout__region--content .field--name-field-test-text'); + $this->assertSession()->elementNotExists('css', '.field--name-field-test-text'); + + // Show the field. + $this->drupalGet('entity_test/structure/entity_test/display'); + $this->getSession()->getPage()->pressButton('Show row weights'); + $this->getSession()->getPage()->selectFieldOption('fields[field_test_text][region]', 'content'); + $this->assertSession()->assertWaitOnAjaxRequest(); + $this->submitForm([], 'Save'); + + // Switch the layout to one column. + $this->click('#edit-field-layouts'); + $this->getSession()->getPage()->selectFieldOption('field_layout', 'layout_test_1col'); + $this->assertSession()->assertWaitOnAjaxRequest(); + $this->submitForm([], 'Save'); // Change the layout for the "test" view mode. See // core.entity_view_mode.entity_test.test.yml. @@ -58,26 +71,28 @@ public function testEntityViewModes() { $this->getSession()->getPage()->checkField('display_modes_custom[test]'); $this->submitForm([], 'Save'); $this->clickLink('configure them'); + $this->click('#edit-field-layouts'); + $this->getSession()->getPage()->selectFieldOption('field_layout', 'layout_test_2col'); + $this->assertSession()->assertWaitOnAjaxRequest(); + $this->submitForm([], 'Save'); + + // Move the field to a new region. $this->getSession()->getPage()->pressButton('Show row weights'); - $this->getSession()->getPage()->selectFieldOption('fields[field_test_text][region]', 'content'); + $this->getSession()->getPage()->selectFieldOption('fields[field_test_text][region]', 'right'); $this->assertSession()->assertWaitOnAjaxRequest(); $this->submitForm([], 'Save'); // Each view mode has a different layout. $this->drupalGet('entity_test/1/test'); - $this->assertSession()->elementExists('css', '.layout__region--content .field--name-field-test-text'); + $this->assertSession()->elementExists('css', '.layout-example-2col .region-right .field--name-field-test-text'); $this->drupalGet('entity_test/1'); - $this->assertSession()->elementNotExists('css', '.layout__region--content .field--name-field-test-text'); + $this->assertSession()->elementExists('css', '.layout-example-1col .region-top .field--name-field-test-text'); } /** * Tests the use of field layout for entity form displays. */ public function testEntityForm() { - // By default, the one-column layout is used. - $this->drupalGet('entity_test/manage/1/edit'); - $this->assertFieldInRegion('field_test_text[0][value]', 'content'); - // The one-column layout is in use. $this->drupalGet('entity_test/structure/entity_test/form-display'); $this->assertEquals(['Content', 'Disabled'], $this->getRegionTitles()); diff --git a/core/modules/field_layout/tests/src/Unit/FieldLayoutBuilderTest.php b/core/modules/field_layout/tests/src/Unit/FieldLayoutBuilderTest.php deleted file mode 100644 index 8e6bf95..0000000 --- a/core/modules/field_layout/tests/src/Unit/FieldLayoutBuilderTest.php +++ /dev/null @@ -1,308 +0,0 @@ -pluginDefinition = new LayoutDefinition([ - 'library' => 'field_layout/drupal.layout.twocol', - 'theme_hook' => 'layout__twocol', - 'regions' => [ - 'left' => [ - 'label' => 'Left', - ], - 'right' => [ - 'label' => 'Right', - ], - ], - ]); - $this->layoutPlugin = new LayoutDefault([], 'two_column', $this->pluginDefinition); - - $this->layoutPluginManager = $this->prophesize(LayoutPluginManagerInterface::class); - $this->layoutPluginManager->getDefinition('unknown', FALSE)->willReturn(NULL); - $this->layoutPluginManager->getDefinition('two_column', FALSE)->willReturn($this->pluginDefinition); - - $this->entityFieldManager = $this->prophesize(EntityFieldManagerInterface::class); - - $this->fieldLayoutBuilder = new FieldLayoutBuilder($this->layoutPluginManager->reveal(), $this->entityFieldManager->reveal()); - } - - /** - * @covers ::buildView - * @covers ::getFields - */ - public function testBuildView() { - $definitions = []; - $non_configurable_field_definition = $this->prophesize(FieldDefinitionInterface::class); - $non_configurable_field_definition->isDisplayConfigurable('view')->willReturn(FALSE); - $definitions['non_configurable_field'] = $non_configurable_field_definition->reveal(); - $definitions['non_configurable_field_with_extra_field'] = $non_configurable_field_definition->reveal(); - $this->entityFieldManager->getFieldDefinitions('the_entity_type_id', 'the_entity_type_bundle')->willReturn($definitions); - $extra_fields = []; - $extra_fields['non_configurable_field_with_extra_field'] = [ - 'label' => 'This non-configurable field is also defined in hook_entity_extra_field_info()', - ]; - $this->entityFieldManager->getExtraFields('the_entity_type_id', 'the_entity_type_bundle')->willReturn($extra_fields); - - $build = [ - 'test1' => [ - '#markup' => 'Test1', - ], - 'non_configurable_field' => [ - '#markup' => 'Non-configurable', - ], - 'non_configurable_field_with_extra_field' => [ - '#markup' => 'Non-configurable with extra field', - ], - ]; - - $display = $this->prophesize(EntityDisplayWithLayoutInterface::class); - $display->getTargetEntityTypeId()->willReturn('the_entity_type_id'); - $display->getTargetBundle()->willReturn('the_entity_type_bundle'); - $display->getLayout()->willReturn($this->layoutPlugin); - $display->getLayoutId()->willReturn('two_column'); - $display->getLayoutSettings()->willReturn([]); - $display->getComponents()->willReturn([ - 'test1' => [ - 'region' => 'right', - ], - 'non_configurable_field' => [ - 'region' => 'left', - ], - 'non_configurable_field_with_extra_field' => [ - 'region' => 'left', - ], - ]); - - $expected = [ - 'non_configurable_field' => [ - '#markup' => 'Non-configurable', - ], - '_field_layout' => [ - 'left' => [ - 'non_configurable_field_with_extra_field' => [ - '#markup' => 'Non-configurable with extra field', - ], - ], - 'right' => [ - 'test1' => [ - '#markup' => 'Test1', - ], - ], - '#settings' => [], - '#layout' => $this->pluginDefinition, - '#theme' => 'layout__twocol', - '#attached' => [ - 'library' => [ - 'field_layout/drupal.layout.twocol', - ], - ], - ], - ]; - $this->fieldLayoutBuilder->buildView($build, $display->reveal()); - $this->assertEquals($expected, $build); - $this->assertSame($expected, $build); - } - - /** - * @covers ::buildForm - * @covers ::getFields - */ - public function testBuildForm() { - $definitions = []; - $non_configurable_field_definition = $this->prophesize(FieldDefinitionInterface::class); - $non_configurable_field_definition->isDisplayConfigurable('form')->willReturn(FALSE); - $definitions['non_configurable_field'] = $non_configurable_field_definition->reveal(); - $this->entityFieldManager->getFieldDefinitions('the_entity_type_id', 'the_entity_type_bundle')->willReturn($definitions); - $this->entityFieldManager->getExtraFields('the_entity_type_id', 'the_entity_type_bundle')->willReturn([]); - - $build = [ - 'test1' => [ - '#markup' => 'Test1', - ], - 'test2' => [ - '#markup' => 'Test2', - '#group' => 'existing_group', - ], - 'field_layout' => [ - '#markup' => 'Field created through the UI happens to be named "Layout"', - ], - 'non_configurable_field' => [ - '#markup' => 'Non-configurable', - ], - ]; - - $display = $this->prophesize(EntityDisplayWithLayoutInterface::class); - $display->getTargetEntityTypeId()->willReturn('the_entity_type_id'); - $display->getTargetBundle()->willReturn('the_entity_type_bundle'); - $display->getLayout()->willReturn($this->layoutPlugin); - $display->getLayoutId()->willReturn('two_column'); - $display->getLayoutSettings()->willReturn([]); - $display->getComponents()->willReturn([ - 'test1' => [ - 'region' => 'right', - ], - 'test2' => [ - 'region' => 'left', - ], - 'field_layout' => [ - 'region' => 'right', - ], - 'non_configurable_field' => [ - 'region' => 'left', - ], - ]); - - $expected = [ - 'test1' => [ - '#markup' => 'Test1', - '#group' => 'right', - ], - 'test2' => [ - '#markup' => 'Test2', - '#group' => 'existing_group', - ], - 'field_layout' => [ - '#markup' => 'Field created through the UI happens to be named "Layout"', - '#group' => 'right', - ], - 'non_configurable_field' => [ - '#markup' => 'Non-configurable', - ], - '_field_layout' => [ - 'left' => [ - '#process' => ['\Drupal\Core\Render\Element\RenderElement::processGroup'], - '#pre_render' => ['\Drupal\Core\Render\Element\RenderElement::preRenderGroup'], - ], - 'right' => [ - '#process' => ['\Drupal\Core\Render\Element\RenderElement::processGroup'], - '#pre_render' => ['\Drupal\Core\Render\Element\RenderElement::preRenderGroup'], - ], - '#settings' => [], - '#layout' => $this->pluginDefinition, - '#theme' => 'layout__twocol', - '#attached' => [ - 'library' => [ - 'field_layout/drupal.layout.twocol', - ], - ], - ], - ]; - $this->fieldLayoutBuilder->buildForm($build, $display->reveal()); - $this->assertEquals($expected, $build); - $this->assertSame($expected, $build); - } - - /** - * @covers ::buildForm - */ - public function testBuildFormEmpty() { - $definitions = []; - $non_configurable_field_definition = $this->prophesize(FieldDefinitionInterface::class); - $non_configurable_field_definition->isDisplayConfigurable('form')->willReturn(FALSE); - $definitions['non_configurable_field'] = $non_configurable_field_definition->reveal(); - $this->entityFieldManager->getFieldDefinitions('the_entity_type_id', 'the_entity_type_bundle')->willReturn($definitions); - $this->entityFieldManager->getExtraFields('the_entity_type_id', 'the_entity_type_bundle')->willReturn([]); - - $build = [ - 'non_configurable_field' => [ - '#markup' => 'Non-configurable', - ], - ]; - - $display = $this->prophesize(EntityDisplayWithLayoutInterface::class); - $display->getTargetEntityTypeId()->willReturn('the_entity_type_id'); - $display->getTargetBundle()->willReturn('the_entity_type_bundle'); - $display->getLayout()->willReturn($this->layoutPlugin); - $display->getLayoutId()->willReturn('two_column'); - $display->getLayoutSettings()->willReturn([]); - $display->getComponents()->willReturn([ - 'test1' => [ - 'region' => 'right', - ], - 'non_configurable_field' => [ - 'region' => 'left', - ], - ]); - - $expected = [ - 'non_configurable_field' => [ - '#markup' => 'Non-configurable', - ], - ]; - $this->fieldLayoutBuilder->buildForm($build, $display->reveal()); - $this->assertSame($expected, $build); - } - - /** - * @covers ::buildForm - */ - public function testBuildFormNoLayout() { - $this->entityFieldManager->getFieldDefinitions(Argument::any(), Argument::any())->shouldNotBeCalled(); - - $build = [ - 'test1' => [ - '#markup' => 'Test1', - ], - ]; - - $display = $this->prophesize(EntityDisplayWithLayoutInterface::class); - $display->getLayoutId()->willReturn('unknown'); - $display->getLayoutSettings()->willReturn([]); - $display->getComponents()->shouldNotBeCalled(); - - $expected = [ - 'test1' => [ - '#markup' => 'Test1', - ], - ]; - $this->fieldLayoutBuilder->buildForm($build, $display->reveal()); - $this->assertSame($expected, $build); - } - -} 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 4738bbb..b426ba2 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 @@ -31,3 +31,5 @@ content: placeholder: '' third_party_settings: { } hidden: { } +layout_id: layout_default +layout_settings: { } 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 6773d32..5b09113 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 @@ -73,3 +73,5 @@ content: placeholder: '' third_party_settings: { } hidden: { } +layout_id: layout_default +layout_settings: { } 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 50df98a..434cbda 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 @@ -27,3 +27,5 @@ content: third_party_settings: { } hidden: forum_container: true +layout_id: layout_default +layout_settings: { } diff --git a/core/modules/forum/config/optional/core.entity_view_display.comment.comment_forum.default.yml b/core/modules/forum/config/optional/core.entity_view_display.comment.comment_forum.default.yml index befeba8..a7fe3d8 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 @@ -22,3 +22,5 @@ content: weight: 100 region: content hidden: { } +layout_id: layout_default +layout_settings: { } diff --git a/core/modules/forum/config/optional/core.entity_view_display.node.forum.default.yml b/core/modules/forum/config/optional/core.entity_view_display.node.forum.default.yml index f3e8c5c..6ff22bb 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 @@ -44,3 +44,5 @@ content: link: true third_party_settings: { } hidden: { } +layout_id: layout_default +layout_settings: { } diff --git a/core/modules/forum/config/optional/core.entity_view_display.node.forum.teaser.yml b/core/modules/forum/config/optional/core.entity_view_display.node.forum.teaser.yml index 7b174f4..c9bd86d 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 @@ -36,3 +36,5 @@ content: third_party_settings: { } hidden: comment_forum: true +layout_id: layout_default +layout_settings: { } diff --git a/core/modules/forum/config/optional/core.entity_view_display.taxonomy_term.forums.default.yml b/core/modules/forum/config/optional/core.entity_view_display.taxonomy_term.forums.default.yml index b326039..4835e73 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 @@ -20,3 +20,5 @@ content: label: above hidden: forum_container: true +layout_id: layout_default +layout_settings: { } diff --git a/core/modules/image/tests/src/Kernel/ImageFormatterTest.php b/core/modules/image/tests/src/Kernel/ImageFormatterTest.php index e994f12..ad93a52 100644 --- a/core/modules/image/tests/src/Kernel/ImageFormatterTest.php +++ b/core/modules/image/tests/src/Kernel/ImageFormatterTest.php @@ -96,6 +96,8 @@ public function testImageFormatterCacheTags() { // Generate the render array to verify if the cache tags are as expected. $build = $this->display->build($entity); + // Build a fake array that provides the structure assumed by the test. + $build = [$this->fieldName => $this->display->getFieldFromBuild($this->fieldName, $build)]; $this->assertEquals($entity->{$this->fieldName}[0]->entity->getCacheTags(), $build[$this->fieldName][0]['#cache']['tags'], 'First image cache tags is as expected'); $this->assertEquals($entity->{$this->fieldName}[1]->entity->getCacheTags(), $build[$this->fieldName][1]['#cache']['tags'], 'Second image cache tags is as expected'); @@ -144,6 +146,8 @@ public function testImageFormatterSvg() { $this->display->setComponent($this->fieldName, $component)->save(); $build = $this->display->build($entity); + // Build a fake array that provides the structure assumed by the test. + $build = [$this->fieldName => $this->display->getFieldFromBuild($this->fieldName, $build)]; // The first image is a PNG, so it is supported by the GD image toolkit. // The image style should be applied with its cache tags, image derivative diff --git a/core/modules/layout_discovery/layout_discovery.info.yml b/core/modules/layout_discovery/layout_discovery.info.yml index a9a4139..d0c8d3a 100644 --- a/core/modules/layout_discovery/layout_discovery.info.yml +++ b/core/modules/layout_discovery/layout_discovery.info.yml @@ -1,6 +1,6 @@ name: 'Layout Discovery' type: module description: 'Provides a way for modules or themes to register layouts.' -package: Core (Experimental) +package: Core version: VERSION core: 8.x diff --git a/core/modules/layout_discovery/layout_discovery.module b/core/modules/layout_discovery/layout_discovery.module index 3cffee9..bacfff8 100644 --- a/core/modules/layout_discovery/layout_discovery.module +++ b/core/modules/layout_discovery/layout_discovery.module @@ -17,23 +17,3 @@ function layout_discovery_help($route_name) { return $output; } } - -/** - * Implements hook_theme(). - */ -function layout_discovery_theme() { - return \Drupal::service('plugin.manager.core.layout')->getThemeImplementations(); -} - -/** - * Prepares variables for layout templates. - * - * @param array &$variables - * An associative array containing: - * - content: An associative array containing the properties of the element. - * Properties used: #settings, #layout. - */ -function template_preprocess_layout(&$variables) { - $variables['settings'] = isset($variables['content']['#settings']) ? $variables['content']['#settings'] : []; - $variables['layout'] = isset($variables['content']['#layout']) ? $variables['content']['#layout'] : []; -} diff --git a/core/modules/layout_discovery/tests/src/Kernel/LayoutTest.php b/core/modules/layout_discovery/tests/src/Kernel/LayoutTest.php index fa8fdd6..b859797 100644 --- a/core/modules/layout_discovery/tests/src/Kernel/LayoutTest.php +++ b/core/modules/layout_discovery/tests/src/Kernel/LayoutTest.php @@ -80,6 +80,17 @@ protected function render(array &$elements) { * Data provider for testRenderLayout(). */ public function renderLayoutData() { + $data['layout_default'] = [ + 'layout_default', + [], + [ + 'content' => [ + '#markup' => "This is the content\n", + ], + ], + ['This is the content'], + ]; + $html = []; $html[] = '