Issue #1817044 by sdboyer, effulgentsia, tim.plunkett, Gábor Hojtsy, yched: Implement Display, a type of config for use by layouts, et. all.
parent
b28e80dd59
commit
75b5ee1258
|
@ -2,4 +2,6 @@ title: One column
|
|||
category: Columns: 1
|
||||
template: one-col
|
||||
regions:
|
||||
content: 'Content'
|
||||
content:
|
||||
label: Middle column
|
||||
type: content
|
|
@ -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
|
||||
|
|
|
@ -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');
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
|
@ -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');
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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.
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
|
@ -2,4 +2,6 @@ title: Single column
|
|||
category: Columns: 1
|
||||
template: one-col
|
||||
regions:
|
||||
middle: 'Middle column'
|
||||
middle:
|
||||
label: Middle column
|
||||
type: content
|
||||
|
|
|
@ -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
|
Loading…
Reference in New Issue