summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorwebchick2012-11-14 08:08:01 -0800
committerwebchick2012-11-14 08:08:01 -0800
commit75b5ee12589a2857967c70bb6ab0c2cd16ead9e2 (patch)
tree5d132fbe41e89d6d02d72637bce10a72074f5f6c
parentb28e80dd59a27342d899b0a07b955c051e2bbdd0 (diff)
Issue #1817044 by sdboyer, effulgentsia, tim.plunkett, Gábor Hojtsy, yched: Implement Display, a type of config for use by layouts, et. all.
-rw-r--r--core/modules/layout/layouts/static/one-col/one-col.yml4
-rw-r--r--core/modules/layout/layouts/static/twocol/two-col.yml8
-rw-r--r--core/modules/layout/lib/Drupal/layout/Config/BoundDisplayInterface.php88
-rw-r--r--core/modules/layout/lib/Drupal/layout/Config/DisplayBase.php138
-rw-r--r--core/modules/layout/lib/Drupal/layout/Config/DisplayInterface.php85
-rw-r--r--core/modules/layout/lib/Drupal/layout/Config/UnboundDisplayInterface.php46
-rw-r--r--core/modules/layout/lib/Drupal/layout/Plugin/Core/Entity/Display.php186
-rw-r--r--core/modules/layout/lib/Drupal/layout/Plugin/Core/Entity/UnboundDisplay.php57
-rw-r--r--core/modules/layout/lib/Drupal/layout/Plugin/LayoutInterface.php4
-rw-r--r--core/modules/layout/lib/Drupal/layout/Plugin/layout/layout/StaticLayout.php4
-rw-r--r--core/modules/layout/lib/Drupal/layout/Tests/DisplayInternalLogicTest.php132
-rw-r--r--core/modules/layout/tests/config/display.bound.test_onecol.yml13
-rw-r--r--core/modules/layout/tests/config/display.bound.test_twocol.yml17
-rw-r--r--core/modules/layout/tests/config/display.unbound.test_unbound_display.yml13
-rw-r--r--core/modules/layout/tests/layout_test.module5
-rw-r--r--core/modules/layout/tests/layouts/static/one-col/one-col.yml4
-rw-r--r--core/modules/layout/tests/themes/layout_test_theme/layouts/static/two-col/two-col.yml8
17 files changed, 802 insertions, 10 deletions
diff --git a/core/modules/layout/layouts/static/one-col/one-col.yml b/core/modules/layout/layouts/static/one-col/one-col.yml
index deaa7f8..ea550ca 100644
--- a/core/modules/layout/layouts/static/one-col/one-col.yml
+++ b/core/modules/layout/layouts/static/one-col/one-col.yml
@@ -2,4 +2,6 @@ title: One column
category: Columns: 1
template: one-col
regions:
- content: 'Content'
+ content:
+ label: Middle column
+ type: content \ No newline at end of file
diff --git a/core/modules/layout/layouts/static/twocol/two-col.yml b/core/modules/layout/layouts/static/twocol/two-col.yml
index 9df31fb..e6c435e 100644
--- a/core/modules/layout/layouts/static/twocol/two-col.yml
+++ b/core/modules/layout/layouts/static/twocol/two-col.yml
@@ -4,5 +4,9 @@ template: two-col
stylesheets:
- two-col.css
regions:
- first: 'First column'
- second: 'Second column'
+ first:
+ label: Left side
+ type: content
+ second:
+ label: Right side
+ type: aside
diff --git a/core/modules/layout/lib/Drupal/layout/Config/BoundDisplayInterface.php b/core/modules/layout/lib/Drupal/layout/Config/BoundDisplayInterface.php
new file mode 100644
index 0000000..125c34e
--- /dev/null
+++ b/core/modules/layout/lib/Drupal/layout/Config/BoundDisplayInterface.php
@@ -0,0 +1,88 @@
+<?php
+/**
+ * @file
+ * Definition of Drupal\layout\Config\BoundDisplayInterface
+ */
+
+namespace Drupal\layout\Config;
+
+use Drupal\layout\Plugin\LayoutInterface;
+
+/**
+ * Interface for a Display object that is coupled to a specific layout.
+ *
+ * Bound displays contains references both to block instances and a specific
+ * layout, and the blocks are assigned to specific regions in that layout. Bound
+ * displays are used to serve real pages at request time.
+ *
+ * @see \Drupal\layout\Config\DisplayInterface
+ */
+interface BoundDisplayInterface extends DisplayInterface {
+
+ /**
+ * Sets the layout to be used by this display.
+ *
+ * @param string $layout_id
+ * The id of the desired layout.
+ */
+ public function setLayout($layout_id);
+
+ /**
+ * Returns the blocks in the requested region, ordered by weight.
+ *
+ * @param string $region
+ * The region from which to return the set of blocks.
+ *
+ * @return array
+ * The list of blocks, ordered by their weight within this display. Each
+ * value in the list is the configuration object name of the block.
+ */
+ public function getSortedBlocksByRegion($region);
+
+ /**
+ * Returns this display's blocks, organized by region and ordered by weight.
+ *
+ * @return array
+ * An array keyed by region name. For each region, the value is the same as
+ * what is returned by getSortedBlocksByRegion().
+ *
+ * @see getSortedBlocksByRegion()
+ */
+ public function getAllSortedBlocks();
+
+ /**
+ * Returns the instantiated layout object to be used by this display.
+ *
+ * @return \Drupal\layout\Plugin\LayoutInterface
+ */
+ public function getLayoutInstance();
+
+ /**
+ * Adjusts this display's block placement to work with the provided layout.
+ *
+ * Essentially a shortcut that calls DisplayInterface::mapBlocksToLayout(),
+ * saves the result in the appropriate object property, and finally calls
+ * BoundDisplayInterface::setLayout().
+ *
+ * @param \Drupal\layout\Plugin\LayoutInterface $layout
+ * The new layout to which blocks should be remapped.
+ *
+ * @see \Drupal\layout\Config\DisplayInterface::mapBlocksToLayout()
+ */
+ public function remapToLayout(LayoutInterface $layout);
+
+ /**
+ * Returns an entity with the non-layout-specific configuration of this one.
+ *
+ * @param string $id
+ * The entity id to assign to the newly created entity.
+ *
+ * @param string $entity_type
+ * The type of entity to create. The PHP class for this entity type must
+ * implement \Drupal\layout\Config\UnboundDisplayInterface.
+ *
+ * @return \Drupal\layout\Config\UnboundDisplayInterface
+ * The newly-created unbound display.
+ */
+ public function generateUnboundDisplay($id, $entity_type = 'unbound_display');
+}
diff --git a/core/modules/layout/lib/Drupal/layout/Config/DisplayBase.php b/core/modules/layout/lib/Drupal/layout/Config/DisplayBase.php
new file mode 100644
index 0000000..afe3229
--- /dev/null
+++ b/core/modules/layout/lib/Drupal/layout/Config/DisplayBase.php
@@ -0,0 +1,138 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\layout\Config\DisplayBase.
+ */
+
+namespace Drupal\layout\Config;
+
+use Drupal\Core\Config\Entity\ConfigEntityBase;
+use Drupal\layout\Plugin\LayoutInterface;
+
+/**
+ * Base class for 'display' and 'unbound_display' configuration entities.
+ *
+ * @see \Drupal\layout\Config\DisplayInterface
+ */
+abstract class DisplayBase extends ConfigEntityBase implements DisplayInterface {
+
+ /**
+ * The ID (config name) identifying a specific display object.
+ *
+ * @var string
+ */
+ public $id;
+
+ /**
+ * The UUID identifying a specific display object.
+ *
+ * @var string
+ */
+ public $uuid;
+
+ /**
+ * Contains all block configuration.
+ *
+ * There are two levels to the configuration contained herein: display-level
+ * block configuration, and then block instance configuration.
+ *
+ * Block instance configuration is stored in a separate config object. This
+ * array is keyed by the config name that uniquely identifies each block
+ * instance. At runtime, various object methods will retrieve this additional
+ * config and return it to calling code.
+ *
+ * Display-level block configuration is data that determines the behavior of
+ * a block *in this display*. The most important examples of this are the
+ * region to which the block is assigned, and its weighting in that region.
+ *
+ * @code
+ * array(
+ * 'block1-configkey' => array(
+ * 'region' => 'content',
+ * // store the region type name here so that we can do type conversion w/out
+ * // needing to have access to the original layout plugin
+ * 'region-type' => 'content',
+ * // increment by 100 so there is ALWAYS plenty of space for manual insertion
+ * 'weight' => -100,
+ * ),
+ * 'block2-configkey' => array(
+ * 'region' => 'sidebar_first',
+ * 'region-type' => 'aside',
+ * 'weight' => -100,
+ * ),
+ * 'block2-configkey' => array(
+ * 'region' => 'sidebar_first',
+ * 'region-type' => 'aside',
+ * 'weight' => 0,
+ * ),
+ * 'maincontent' => array(
+ * 'region' => 'content',
+ * 'region-type' => 'content',
+ * 'weight' => -200,
+ * ),
+ * );
+ * @endcode
+ *
+ * @var array
+ */
+ protected $blockInfo = array();
+
+ /**
+ * Implements DisplayInterface::getAllBlockInfo().
+ */
+ public function getAllBlockInfo() {
+ return $this->blockInfo;
+ }
+
+ /**
+ * Implements DisplayInterface::mapBlocksToLayout().
+ *
+ * @todo Decouple this implementation from this class, so that it could be
+ * more easily customized.
+ */
+ public function mapBlocksToLayout(LayoutInterface $layout) {
+ $types = array();
+
+ $layout_regions = $layout->getRegions();
+ $layout_regions_indexed = array_keys($layout_regions);
+ foreach ($layout_regions as $name => $info) {
+ $types[$info['type']][] = $name;
+ }
+
+ $remapped_config = array();
+ foreach ($this->blockInfo as $name => $info) {
+ // First, if there's a direct region name match, use that.
+ if (!empty($info['region']) && isset($layout_regions[$info['region']])) {
+ // No need to do anything.
+ }
+ // Then, try to remap using region types.
+ else if (!empty($types[$info['region-type']])) {
+ $info['region'] = reset($types[$info['region-type']]);
+ }
+ // Finally, fall back to dumping everything in the layout's first region.
+ else {
+ if (!isset($first_region)) {
+ reset($layout_regions);
+ $first_region = key($layout_regions);
+ }
+ $info['region'] = $first_region;
+ }
+
+ $remapped_config[$name] = $info;
+ }
+
+ return $remapped_config;
+ }
+
+ /**
+ * Implements DisplayInterface::getAllRegionTypes().
+ */
+ public function getAllRegionTypes() {
+ $types = array();
+ foreach ($this->blockInfo as $info) {
+ $types[] = $info['region-type'];
+ }
+ return array_unique($types);
+ }
+}
diff --git a/core/modules/layout/lib/Drupal/layout/Config/DisplayInterface.php b/core/modules/layout/lib/Drupal/layout/Config/DisplayInterface.php
new file mode 100644
index 0000000..871aadc
--- /dev/null
+++ b/core/modules/layout/lib/Drupal/layout/Config/DisplayInterface.php
@@ -0,0 +1,85 @@
+<?php
+/**
+ * @file
+ * Definition of Drupal\layout\Config\DisplayInterface
+ */
+
+namespace Drupal\layout\Config;
+
+use Drupal\layout\Plugin\LayoutInterface;
+
+/**
+ * Interface describing a Display configuration object.
+ *
+ * Displays are configuration that describe the placement of block instances
+ * in regions. Drupal includes two types of Display objects:
+ * - Bound displays include a reference to a specific layout, and each block is
+ * specified to display in a specific region of that layout. Bound displays
+ * are used to serve real pages at request time.
+ * - Unbound displays do not include a reference to any layout, and each block
+ * is assigned a region type, but not a specific region. Developers including
+ * default displays with their modules or distributions are encouraged to use
+ * unbound displays in order to minimize dependencies on specific layouts and
+ * allow site-specific configuration to dictate the layout.
+ *
+ * This interface defines what is common to all displays, whether bound or
+ * unbound.
+ *
+ * @see \Drupal\layout\Config\BoundDisplayInterface
+ * @see \Drupal\layout\Config\UnboundDisplayInterface
+ */
+interface DisplayInterface {
+
+ /**
+ * Returns the display-specific configuration of all blocks in this display.
+ *
+ * For each block that exists in Drupal (e.g., the "Who's Online" block),
+ * multiple "configured instances" can be created (e.g., a "Who's been online
+ * in the last 5 minutes" instance and a "Who's been online in the last 60
+ * minutes" instance). Each configured instance can be referenced by multiple
+ * displays (e.g., by a "regular" page, by an administrative page, and within
+ * one or more dashboards). This function returns the block instances that
+ * have been added to this display. Each key of the returned array is the
+ * block instance's configuration object name, and config() may be called on
+ * it in order to retrieve the full configuration that is shared across all
+ * displays. For each key, the value is an array of display-specific
+ * configuration, primarily the 'region' and 'weight', and anything else that
+ * affects the placement of the block within the layout rather than only the
+ * contents of the block.
+ *
+ * @return array
+ * An array keyed on each block's configuration object name. Each value is
+ * an array of information that determines the placement of the block within
+ * a layout, including:
+ * - region: The region in which to display the block (for bound displays
+ * only).
+ * - region-type: The type of region that is most appropriate for the block.
+ * Usually one of 'header', 'footer', 'nav', 'content', 'aside', or
+ * 'system', though custom region types are also allowed. This is
+ * primarily specified by unbound displays, where specifying a specific
+ * region name is impossible, because different layouts come with
+ * different regions.
+ * - weight: Within a region, blocks are rendered from low to high weight.
+ */
+ public function getAllBlockInfo();
+
+ /**
+ * Maps the contained block info to the provided layout.
+ *
+ * @param \Drupal\layout\Plugin\LayoutInterface $layout
+ *
+ * @return array
+ * An array containing block configuration info, identical to that which
+ * is returned by DisplayInterface::getAllBlockInfo().
+ */
+ public function mapBlocksToLayout(LayoutInterface $layout);
+
+ /**
+ * Returns the names of all region types to which blocks are assigned.
+ *
+ * @return array
+ * An indexed array of unique region type names, or an empty array if no
+ * region types were assigned.
+ */
+ public function getAllRegionTypes();
+}
diff --git a/core/modules/layout/lib/Drupal/layout/Config/UnboundDisplayInterface.php b/core/modules/layout/lib/Drupal/layout/Config/UnboundDisplayInterface.php
new file mode 100644
index 0000000..34ba2f8
--- /dev/null
+++ b/core/modules/layout/lib/Drupal/layout/Config/UnboundDisplayInterface.php
@@ -0,0 +1,46 @@
+<?php
+/**
+ * @file
+ * Definition of Drupal\layout\Config\UnboundDisplayInterface
+ */
+
+namespace Drupal\layout\Config;
+
+use Drupal\layout\Plugin\LayoutInterface;
+
+/**
+ * Interface for a Display that is not coupled with any layout.
+ *
+ * Unbound displays contain references to blocks, but not to any particular
+ * layout. Their primary use case is to express a set of relative block
+ * placements without necessitating any particular layout be present. This
+ * allows upstream (module and distribution) developers to express a visual
+ * composition of blocks without knowing anything about the layouts a
+ * particular site has available.
+ *
+ * @see \Drupal\layout\Config\DisplayInterface
+ */
+interface UnboundDisplayInterface extends DisplayInterface {
+
+ /**
+ * Returns a bound display entity by binding a layout to this unbound display.
+ *
+ * This will DisplayInterface::mapBlocksToLayout() using the provided layout,
+ * then create and return a new Display object with the output. This is just
+ * a factory - calling code is responsible for saving the returned object.
+ *
+ * @param \Drupal\layout\Plugin\LayoutInterface $layout
+ * The desired layout.
+ *
+ * @param string $id
+ * The entity id to assign to the newly created entity.
+ *
+ * @param string $entity_type
+ * The type of entity to create. The PHP class for this entity type must
+ * implement \Drupal\layout\Config\BoundDisplayInterface.
+ *
+ * @return \Drupal\layout\Config\BoundDisplayInterface
+ * The newly created entity.
+ */
+ public function generateDisplay(LayoutInterface $layout, $id, $entity_type = 'display');
+}
diff --git a/core/modules/layout/lib/Drupal/layout/Plugin/Core/Entity/Display.php b/core/modules/layout/lib/Drupal/layout/Plugin/Core/Entity/Display.php
new file mode 100644
index 0000000..d954832
--- /dev/null
+++ b/core/modules/layout/lib/Drupal/layout/Plugin/Core/Entity/Display.php
@@ -0,0 +1,186 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\layout\Plugin\Core\Entity\Display.
+ */
+
+namespace Drupal\layout\Plugin\Core\Entity;
+
+use Drupal\layout\Config\DisplayBase;
+use Drupal\layout\Config\BoundDisplayInterface;
+use Drupal\layout\Config\UnboundDisplayInterface;
+use Drupal\layout\Plugin\LayoutInterface;
+use Drupal\Core\Annotation\Plugin;
+use Drupal\Core\Annotation\Translation;
+
+/**
+ * Defines the display entity.
+ *
+ * @Plugin(
+ * id = "display",
+ * label = @Translation("Display"),
+ * module = "layout",
+ * controller_class = "Drupal\Core\Config\Entity\ConfigStorageController",
+ * config_prefix = "display.bound",
+ * entity_keys = {
+ * "id" = "id",
+ * "uuid" = "uuid"
+ * }
+ * )
+ */
+class Display extends DisplayBase implements BoundDisplayInterface {
+
+ /**
+ * A two-level array expressing block ordering within regions.
+ *
+ * The outer array is associative, keyed on region name. Each inner array is
+ * indexed, with the config address of a block as values and sorted according
+ * to order in which those blocks should appear in that region.
+ *
+ * This property is not stored statically in config, but is derived at runtime
+ * by DisplayBase::sortBlocks(). It is not stored statically because that
+ * would make using weights for ordering more difficult, and weights make
+ * external mass manipulation of displays much easier.
+ *
+ * @var array
+ */
+ protected $blocksInRegions;
+
+ /**
+ * The layout instance being used to serve this page.
+ *
+ * @var \Drupal\layout\Plugin\LayoutInterface
+ */
+ protected $layoutInstance;
+
+ /**
+ * The name of the layout plugin to use.
+ *
+ * @var string
+ */
+ public $layout;
+
+ /**
+ * The settings with which to instantiate the layout plugin.
+ *
+ * @var array
+ */
+ public $layoutSettings = array();
+
+ /**
+ * Implements BoundDisplayInterface::getSortedBlocksByRegion().
+ *
+ * @throws \Exception
+ */
+ public function getSortedBlocksByRegion($region) {
+ if ($this->blocksInRegions === NULL) {
+ $this->sortBlocks();
+ }
+
+ if (!isset($this->blocksInRegions[$region])) {
+ throw new \Exception(sprintf("Region %region does not exist in layout %layout", array('%region' => $region, '%layout' => $this->getLayoutInstance()->name)), E_RECOVERABLE_ERROR);
+ }
+
+ return $this->blocksInRegions[$region];
+ }
+
+ /**
+ * Implements BoundDisplayInterface::getAllSortedBlocks().
+ */
+ public function getAllSortedBlocks() {
+ if ($this->blocksInRegions === NULL) {
+ $this->sortBlocks();
+ }
+
+ return $this->blocksInRegions;
+ }
+
+ /**
+ * Transform the stored blockConfig into a sorted, region-oriented array.
+ */
+ protected function sortBlocks() {
+ $layout_instance = $this->getLayoutInstance();
+ if ($this->layout !== $layout_instance->getPluginId()) {
+ $block_config = $this->mapBlocksToLayout($layout_instance);
+ }
+ else {
+ $block_config = $this->blockInfo;
+ }
+
+ $this->blocksInRegions = array();
+
+ $regions = array_fill_keys(array_keys($layout_instance->getRegions()), array());
+ foreach ($block_config as $config_name => $info) {
+ $regions[$info['region']][$config_name] = $info;
+ }
+
+ foreach ($regions as $region_name => &$blocks) {
+ uasort($blocks, 'drupal_sort_weight');
+ $this->blocksInRegions[$region_name] = array_keys($blocks);
+ }
+ }
+
+ /**
+ * Implements BoundDisplayInterface::remapToLayout().
+ */
+ public function remapToLayout(LayoutInterface $layout) {
+ $this->blockInfo = $this->mapBlocksToLayout($layout);
+ $this->setLayout($layout->getPluginId());
+ }
+
+ /**
+ * Set the contained layout plugin.
+ *
+ * @param string $plugin_id
+ * The plugin id of the desired layout plugin.
+ */
+ public function setLayout($plugin_id) {
+ // @todo verification?
+ $this->layout = $plugin_id;
+ $this->layoutInstance = NULL;
+ $this->blocksInRegions = NULL;
+ }
+
+ /**
+ * Implements BoundDisplayInterface::generateUnboundDisplay().
+ *
+ * @throws \Exception
+ */
+ public function generateUnboundDisplay($id, $entity_type = 'unbound_display') {
+ $block_info = $this->getAllBlockInfo();
+ foreach ($block_info as &$info) {
+ unset($info['region']);
+ }
+
+ $values = array(
+ 'blockInfo' => $block_info,
+ 'id' => $id,
+ );
+
+ $entity = entity_create($entity_type, $values);
+ if (!$entity instanceof UnboundDisplayInterface) {
+ throw new \Exception(sprintf('Attempted to create an unbound display using an invalid entity type.'), E_RECOVERABLE_ERROR);
+ }
+
+ return $entity;
+ }
+
+ /**
+ * Returns the instantiated layout object.
+ *
+ * @throws \Exception
+ */
+ public function getLayoutInstance() {
+ if ($this->layoutInstance === NULL) {
+ if (empty($this->layout)) {
+ throw new \Exception(sprintf('Display "%id" had no layout plugin attached.', array('%id' => $this->id())), E_RECOVERABLE_ERROR);
+ }
+
+ $this->layoutInstance = layout_manager()->createInstance($this->layout, $this->layoutSettings);
+ // @todo add handling for remapping if the layout could not be found
+ }
+
+ return $this->layoutInstance;
+ }
+}
diff --git a/core/modules/layout/lib/Drupal/layout/Plugin/Core/Entity/UnboundDisplay.php b/core/modules/layout/lib/Drupal/layout/Plugin/Core/Entity/UnboundDisplay.php
new file mode 100644
index 0000000..5450e2c
--- /dev/null
+++ b/core/modules/layout/lib/Drupal/layout/Plugin/Core/Entity/UnboundDisplay.php
@@ -0,0 +1,57 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\layout\Plugin\Core\Entity\Display.
+ */
+
+namespace Drupal\layout\Plugin\Core\Entity;
+
+use Drupal\layout\Config\DisplayBase;
+use Drupal\layout\Config\BoundDisplayInterface;
+use Drupal\layout\Config\UnboundDisplayInterface;
+use Drupal\layout\Plugin\LayoutInterface;
+use Drupal\Core\Annotation\Plugin;
+use Drupal\Core\Annotation\Translation;
+
+/**
+ * Defines the unbound_display entity.
+ *
+ * Unbound displays contain blocks that are not 'bound' to a specific layout,
+ * and their contained blocks are mapped only to region types, not regions.
+ *
+ * @Plugin(
+ * id = "unbound_display",
+ * label = @Translation("Unbound Display"),
+ * module = "layout",
+ * controller_class = "Drupal\Core\Config\Entity\ConfigStorageController",
+ * config_prefix = "display.unbound",
+ * entity_keys = {
+ * "id" = "id",
+ * "uuid" = "uuid"
+ * }
+ * )
+ */
+class UnboundDisplay extends DisplayBase implements UnboundDisplayInterface {
+
+ /**
+ * Implements UnboundDisplayInterface::generateDisplay().
+ *
+ * @throws \Exception
+ */
+ public function generateDisplay(LayoutInterface $layout, $id, $entity_type = 'display') {
+ $values = array(
+ 'layout' => $layout->getPluginId(),
+ 'blockInfo' => $this->mapBlocksToLayout($layout),
+ 'id' => $id,
+ );
+
+ $entity = entity_create($entity_type, $values);
+
+ if (!$entity instanceof BoundDisplayInterface) {
+ throw new \Exception(sprintf('Attempted to bind an unbound display but provided an invalid entity type.'), E_RECOVERABLE_ERROR);
+ }
+
+ return $entity;
+ }
+}
diff --git a/core/modules/layout/lib/Drupal/layout/Plugin/LayoutInterface.php b/core/modules/layout/lib/Drupal/layout/Plugin/LayoutInterface.php
index 5b874f6..7521c57 100644
--- a/core/modules/layout/lib/Drupal/layout/Plugin/LayoutInterface.php
+++ b/core/modules/layout/lib/Drupal/layout/Plugin/LayoutInterface.php
@@ -7,10 +7,12 @@
namespace Drupal\layout\Plugin;
+use Drupal\Component\Plugin\PluginInspectionInterface;
+
/**
* Defines the shared interface for all layout plugins.
*/
-interface LayoutInterface {
+interface LayoutInterface extends PluginInspectionInterface {
/**
* Returns a list of regions.
diff --git a/core/modules/layout/lib/Drupal/layout/Plugin/layout/layout/StaticLayout.php b/core/modules/layout/lib/Drupal/layout/Plugin/layout/layout/StaticLayout.php
index 4819595..3ded379 100644
--- a/core/modules/layout/lib/Drupal/layout/Plugin/layout/layout/StaticLayout.php
+++ b/core/modules/layout/lib/Drupal/layout/Plugin/layout/layout/StaticLayout.php
@@ -88,11 +88,11 @@ class StaticLayout extends PluginBase implements LayoutInterface {
);
// Render all regions needed for this layout.
- foreach ($this->getRegions() as $region => $title) {
+ foreach ($this->getRegions() as $region => $info) {
// @todo This is just stub code to fill in regions with stuff for now.
// When blocks are related to layouts and not themes, we can make this
// really be filled in with blocks.
- $build['#content'][$region] = '<h3>' . $title . '</h3>';
+ $build['#content'][$region] = '<h3>' . $info['label'] . '</h3>';
}
// Fill in attached CSS and JS files based on metadata.
diff --git a/core/modules/layout/lib/Drupal/layout/Tests/DisplayInternalLogicTest.php b/core/modules/layout/lib/Drupal/layout/Tests/DisplayInternalLogicTest.php
new file mode 100644
index 0000000..07a77bf
--- /dev/null
+++ b/core/modules/layout/lib/Drupal/layout/Tests/DisplayInternalLogicTest.php
@@ -0,0 +1,132 @@
+<?php
+
+/**
+ * @file
+ * Definition of \Drupal\layout\Tests\DisplayInternalLogicTest.
+ */
+
+namespace Drupal\layout\Tests;
+
+use Drupal\simpletest\WebTestBase;
+use Drupal\layout\Plugin\Core\Entity\Display;
+use Drupal\layout\Plugin\Core\Entity\UnboundDisplay;
+
+/**
+ * Tests the API and internal logic offered by Displays.
+ */
+class DisplayInternalLogicTest extends WebTestBase {
+
+ /**
+ * Modules to enable.
+ *
+ * @var array
+ */
+ public static $modules = array('layout', 'layout_test');
+
+ /**
+ * The twocol test display.
+ *
+ * @var \Drupal\layout\Plugin\Core\Entity\Display
+ */
+ public $twocol;
+
+ /**
+ * The onecol test display.
+ *
+ * @var \Drupal\layout\Plugin\Core\Entity\Display
+ */
+ public $onecol;
+
+ /**
+ * The unbound test display.
+ *
+ * @var \Drupal\layout\Plugin\Core\Entity\UnboundDisplay
+ */
+ public $unbound;
+
+ public static function getInfo() {
+ return array(
+ 'name' => 'Display behaviors',
+ 'description' => 'Tests internal behaviors of DisplayInterface implementations, such as layout remapping.',
+ 'group' => 'Display',
+ );
+ }
+
+ public function setUp() {
+ parent::setUp();
+ $this->twocol = entity_load('display', 'test_twocol');
+ $this->onecol = entity_load('display', 'test_onecol');
+ $this->unbound = entity_load('unbound_display', 'test_unbound_display');
+ }
+
+ /**
+ * Tests block sorting within regions.
+ */
+ public function testBlockSorting() {
+ $expected = array(
+ 'left' => array('block.test_block_3', 'block.test_block_1'),
+ 'right' => array('block.test_block_2'),
+ );
+ $this->assertIdentical($this->twocol->getSortedBlocksByRegion('left'), $expected['left']);
+ $this->assertIdentical($this->twocol->getSortedBlocksByRegion('right'), $expected['right']);
+ $this->assertIdentical($this->twocol->getAllSortedBlocks(), $expected);
+ }
+
+ /**
+ * Test the various block remapping scenarios allowed for by the assorted
+ * Display types.
+ *
+ * This includes remapping a Display's blocks to a new layout, binding an
+ * UnboundDisplay with a layout to generate a new Display, and releasing a
+ * Display from its layout binding to generate an UnboundDisplay.
+ */
+ public function testBlockMapping() {
+ // Remap from twocol to onecol. All blocks are expected to move to the one
+ // and only region and be sorted by their original weights.
+ $expected = array(
+ 'middle' => array('block.test_block_3', 'block.test_block_2', 'block.test_block_1'),
+ );
+ $two_to_one = clone($this->twocol);
+ $two_to_one->remapToLayout($this->onecol->getLayoutInstance());
+ $this->assertIdentical($two_to_one->getAllSortedBlocks(), $expected);
+
+ // Remap from onecol to twocol. Since the blocks are assigned the 'content'
+ // region type, and twocol's 'left' region has that type, the blocks are
+ // expected to move to there and be sorted by their original weights.
+ $expected = array(
+ 'left' => array('block.test_block_2', 'block.test_block_1'),
+ 'right' => array(),
+ );
+ $one_to_two = clone($this->onecol);
+ $one_to_two->remapToLayout($this->twocol->getLayoutInstance());
+ $this->assertIdentical($one_to_two->getAllSortedBlocks(), $expected);
+
+ // Bind the unbound display to the twocol layout:
+ // - Block 1 is assigned the 'content' region type, so is expected to be
+ // mapped to the 'left' region, which has that type.
+ // - Block 2 is assigned the 'aside' region type, so is expected to be
+ // mapped to the 'right' region, which has that type.
+ // - Block 3 is assigned the 'nav' region type, and there is no twocol
+ // region with that type, so it is expected to be mapped to twocol's
+ // first region, which is 'left'.
+ $expected = array(
+ 'left' => array('block.test_block_1', 'block.test_block_3'),
+ 'right' => array('block.test_block_2'),
+ );
+ $unbound_to_twocol = $this->unbound->generateDisplay($this->twocol->getLayoutInstance(), 'unbound_to_twocol');
+ $this->assertTrue($unbound_to_twocol instanceof Display, 'Binding the unbound display successfully created a Display object');
+ $this->assertIdentical($unbound_to_twocol->getAllSortedBlocks(), $expected);
+
+ // Generate an unbound display from the twocol display.
+ $expected = array(
+ 'block.test_block_1' => array('region-type' => 'content', 'weight' => 100),
+ 'block.test_block_2' => array('region-type' => 'aside', 'weight' => 0),
+ 'block.test_block_3' => array('region-type' => 'content', 'weight' => -100),
+ );
+ $twocol_to_unbound = $this->twocol->generateUnboundDisplay('twocol_to_unbound');
+ $this->assertTrue($twocol_to_unbound instanceof UnboundDisplay, 'Unbinding the twocol display successfully created an UnboundDisplay object');
+ // We can use Equal instead of Identical, because for this, array order and
+ // integer vs. string data types do not matter.
+ $this->assertEqual($twocol_to_unbound->getAllBlockInfo(), $expected);
+ }
+}
diff --git a/core/modules/layout/tests/config/display.bound.test_onecol.yml b/core/modules/layout/tests/config/display.bound.test_onecol.yml
new file mode 100644
index 0000000..a156355
--- /dev/null
+++ b/core/modules/layout/tests/config/display.bound.test_onecol.yml
@@ -0,0 +1,13 @@
+id: test_onecol
+label: Onecol testing display
+layout: static_layout:layout_test__one-col
+layoutSettings: { }
+blockInfo:
+ block.test_block_1:
+ region: middle
+ region-type: content
+ weight: 100
+ block.test_block_2:
+ region: middle
+ region-type: content
+ weight: -100
diff --git a/core/modules/layout/tests/config/display.bound.test_twocol.yml b/core/modules/layout/tests/config/display.bound.test_twocol.yml
new file mode 100644
index 0000000..3c47e09
--- /dev/null
+++ b/core/modules/layout/tests/config/display.bound.test_twocol.yml
@@ -0,0 +1,17 @@
+id: test_twocol
+label: Twocol testing display
+layout: static_layout:layout_test_theme__two-col
+layoutSettings: { }
+blockInfo:
+ block.test_block_1:
+ region: left
+ region-type: content
+ weight: 100
+ block.test_block_2:
+ region: right
+ region-type: aside
+ weight: 0
+ block.test_block_3:
+ region: left
+ region-type: content
+ weight: -100
diff --git a/core/modules/layout/tests/config/display.unbound.test_unbound_display.yml b/core/modules/layout/tests/config/display.unbound.test_unbound_display.yml
new file mode 100644
index 0000000..8dfd4c8
--- /dev/null
+++ b/core/modules/layout/tests/config/display.unbound.test_unbound_display.yml
@@ -0,0 +1,13 @@
+id: test_unbound_display
+label: Unbound display test
+layoutSettings: { }
+blockInfo:
+ block.test_block_1:
+ region-type: content
+ weight: -100
+ block.test_block_2:
+ region-type: aside
+ weight: -100
+ block.test_block_3:
+ region-type: nav
+ weight: 0
diff --git a/core/modules/layout/tests/layout_test.module b/core/modules/layout/tests/layout_test.module
index 52ab4a3..b9eb63b 100644
--- a/core/modules/layout/tests/layout_test.module
+++ b/core/modules/layout/tests/layout_test.module
@@ -27,7 +27,10 @@ function layout_test_page() {
global $theme;
$theme = 'layout_test_theme';
theme_enable(array($theme));
- $layout = layout_manager()->createInstance('static_layout:layout_test_theme__two-col');
+ $display = entity_load('display', 'test_twocol');
+ $layout = $display->getLayoutInstance();
+ // @todo This tests that the layout can render its regions, but does not test
+ // block rendering: http://drupal.org/node/1812720.
return $layout->renderLayout();
}
diff --git a/core/modules/layout/tests/layouts/static/one-col/one-col.yml b/core/modules/layout/tests/layouts/static/one-col/one-col.yml
index 27d7d06..9048e14 100644
--- a/core/modules/layout/tests/layouts/static/one-col/one-col.yml
+++ b/core/modules/layout/tests/layouts/static/one-col/one-col.yml
@@ -2,4 +2,6 @@ title: Single column
category: Columns: 1
template: one-col
regions:
- middle: 'Middle column'
+ middle:
+ label: Middle column
+ type: content
diff --git a/core/modules/layout/tests/themes/layout_test_theme/layouts/static/two-col/two-col.yml b/core/modules/layout/tests/themes/layout_test_theme/layouts/static/two-col/two-col.yml
index 7ee126f..01b9e86 100644
--- a/core/modules/layout/tests/themes/layout_test_theme/layouts/static/two-col/two-col.yml
+++ b/core/modules/layout/tests/themes/layout_test_theme/layouts/static/two-col/two-col.yml
@@ -4,5 +4,9 @@ template: two-col
stylesheets:
- two-col.css
regions:
- left: 'Left side'
- right: 'Right side'
+ left:
+ label: Left side
+ type: content
+ right:
+ label: Right side
+ type: aside \ No newline at end of file