diff --git a/core/lib/Drupal/Core/Config/Entity/Display.php b/core/lib/Drupal/Core/Config/Entity/Display.php index 09c1221..5b65111 100644 --- a/core/lib/Drupal/Core/Config/Entity/Display.php +++ b/core/lib/Drupal/Core/Config/Entity/Display.php @@ -13,7 +13,7 @@ * Configuration encapsulator that provides all the data needed by block-driven * controllers to render a page. */ -class Display extends ConfigEntityBase { +class Display extends ConfigEntityBase implements DisplayInterface { /** * The ID (config name) identifying a specific display object. @@ -40,7 +40,7 @@ class Display extends ConfigEntityBase { /** * The layout plugin instance being used to serve this page. * - * @var Drupal\layout\Plugin\LayoutInterface + * @var \Drupal\layout\Plugin\LayoutInterface */ protected $layoutPlugin; @@ -119,70 +119,113 @@ class Display extends ConfigEntityBase { */ public $blockConfig = array(); + protected $blocksInRegions; + public function __construct(array $values, $entity_type) { $this->layout = $values['layout']; // @todo oh so much, but really, blocks for now. parent::__construct($values, 'display'); } - /** - * Sets the controller that should be used to serve the main page content. - * - * "Main page content" is analogous to the callback specified in - * 'page callback' in Drupal 6 and 7. In a blocks-driven world, such direct - * callbacks run a bit against the grain. However, the simplicity of - * implementing and understanding it is so beneficial to developer experience - * that we provide this 'main' content option, which simulates the old - * behavior by injecting a virtualized 'main' block that passes through - * to a specified callback. - * - * We normalize the main content block here in the configuration object so - * that various controllers consuming the display need not repeatedly - * implement this special handling for the main callback. - * - * @param Callable $callback - * Any form of callable. @todo PHP 5.4 allows type hinting as Callable. - * @param array $args - * An array of arguments to pass to the callable. - */ public function setMainContent($callback, array $args = array()) { // @todo need to wrap the callback up in a passthru block // @todo consider throwing an exception if this display instance doesn't appear to support main content } + public function getAllBlockInfo() { + return $this->blockConfig; + } + + 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->getLayoutPluginInstance()->name)), E_RECOVERABLE_ERROR); + } + + return $this->blocksInRegions[$region]; + } + + public function getAllSortedBlocks() { + if ($this->blocksInRegions === NULL) { + $this->sortBlocks(); + } + + return $this->blocksInRegions; + } /** - * Returns the config info about all blocks on this display. + * Transform the stored blockConfig into a sorted, region-oriented array. + */ + protected function sortBlocks() { + $layout_instance = $this->getLayoutPluginInstance(); + if ($this->layout !== $layout_instance->name) { + $block_config = $this->remapBlocksToLayout($layout_instance); + } + else { + $block_config = $this->blockConfig; + } + + $this->blocksInRegions = array_fill_keys(array_keys($layout_instance->getRegions()), array()); + foreach ($block_config as $config_name => $info) { + $this->blocksInRegions[$info['weight']] = $config_name; + } + + foreach ($this->blocksInRegions as &$region) { + krsort($region); + } + } + + /** + * Remap the the saved blockConfig to work in a different layout. * - * There are two levels of configuration that are being captured here: the - * configuration for the block itself (i.e., config generated by a user saving - * the block's edit form), and configuration for how the particular block - * instance behaves in *this* display. The former is typically its own config - * object, and only a reference to that config key is stored directly on this - * object. The most important examples of the latter are the region in which - * the block is placed, and its weighting within the region. + * @todo this logic is a good default, but should be alterable. * - * @todo we need to explore a *lot* more just how freestanding we make blocks. + * @param \Drupal\layout\Plugin\LayoutInterface $layout * * @return array - * An array of block info, keyed on each block's config name. - * - * @todo implement batch-loading logic to try to minimize discrete queries. + * An array containing block configuration info, per the blockConfig property. */ - public function getAllBlockInfo() { + protected function remapBlocksToLayout(LayoutInterface $layout) { + $roles = array(); + $layout_regions = $layout->getRegions(); + $layout_regions_indexed = array_keys($layout_regions); + foreach ($layout_regions as $name => $info) { + $roles[$info['role']][] = $name; + } + + $remapped_config = array(); + foreach ($this->blockConfig as $name => $info) { + // First, if there's a direct region name match, use that. + if (!empty($layout_regions[$info['region']])) { + // No need to do anything. + } + // Then, try to remap using region roles. + else if (!empty($roles[$info['region-role']])) { + $info['region'] = $roles[$info['region-role']]; + } + // Finally, fall back to dumping everything in the layout's first region. + else { + $info['region'] = reset($layout_regions_indexed); + } + + $remapped_config[$name] = $info; + } + + return $remapped_config; } - /** - * Returns the layout plugin instance to be used with this display. - * - * @return Drupal\layout\Plugin\LayoutInterface - */ public function getLayoutPluginInstance() { if ($this->layoutPlugin === NULL) { - // @todo if we're doing any magic resolution/hotswapping of layouts, this - // is a place it could happen. + if (empty($this->layout)) { + throw new \Exception(sprintf('Display with id %id had no layout plugin attached.', array('%id' => $this->id)), E_RECOVERABLE_ERROR); + } + $this->layoutPlugin = layout_manager()->createInstance($this->layout); + // @todo add handling for remapping if the layout could not be found } return $this->layoutPlugin; diff --git a/core/lib/Drupal/Core/Config/Entity/DisplayInterface.php b/core/lib/Drupal/Core/Config/Entity/DisplayInterface.php new file mode 100644 index 0000000..f114c1b --- /dev/null +++ b/core/lib/Drupal/Core/Config/Entity/DisplayInterface.php @@ -0,0 +1,84 @@ +