diff --git a/core/modules/field_layout/field_layout.layouts.yml b/core/modules/field_layout/field_layout.layouts.yml new file mode 100644 index 0000000..0aaa87b --- /dev/null +++ b/core/modules/field_layout/field_layout.layouts.yml @@ -0,0 +1,21 @@ +onecol: + label: 'One column' + path: layouts/onecol + theme: field_layout__onecol + category: 'Columns: 1' + default_region: content + regions: + content: + label: Content +twocol: + label: 'Two column' + path: layouts/twocol + theme: field_layout__twocol + library: field_layout/drupal.field_layout.twocol + category: 'Columns: 2' + default_region: left + regions: + left: + label: Left + right: + label: Right diff --git a/core/modules/field_layout/field_layout.services.yml b/core/modules/field_layout/field_layout.services.yml index e491e99..fb8f2bb 100644 --- a/core/modules/field_layout/field_layout.services.yml +++ b/core/modules/field_layout/field_layout.services.yml @@ -1,4 +1,4 @@ services: field_layout.layout_plugin_manager: class: Drupal\field_layout\LayoutPluginManager - arguments: ['@module_handler', '@theme_handler', '@string_translation'] + arguments: ['@container.namespaces', '@cache.discovery', '@module_handler', '@theme_handler', '@string_translation'] diff --git a/core/modules/field_layout/src/Annotation/Layout.php b/core/modules/field_layout/src/Annotation/Layout.php new file mode 100644 index 0000000..31b3ceb --- /dev/null +++ b/core/modules/field_layout/src/Annotation/Layout.php @@ -0,0 +1,90 @@ +layoutPluginManager->createInstance($display->getLayoutId())->build($regions); } } diff --git a/core/modules/field_layout/src/LayoutPluginManager.php b/core/modules/field_layout/src/LayoutPluginManager.php index 4f904b1..feea18e 100644 --- a/core/modules/field_layout/src/LayoutPluginManager.php +++ b/core/modules/field_layout/src/LayoutPluginManager.php @@ -2,24 +2,24 @@ namespace Drupal\field_layout; -use Drupal\Component\Plugin\Discovery\DiscoveryTrait; use Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException; +use Drupal\Core\Cache\CacheBackendInterface; use Drupal\Core\Extension\ModuleHandlerInterface; use Drupal\Core\Extension\ThemeHandlerInterface; use Drupal\Core\Plugin\CategorizingPluginManagerTrait; +use Drupal\Core\Plugin\DefaultPluginManager; +use Drupal\Core\Plugin\Discovery\YamlDiscoveryDecorator; use Drupal\Core\StringTranslation\TranslationInterface; +use Drupal\field_layout\Annotation\Layout; +use Drupal\field_layout\Plugin\LayoutDefault; +use Drupal\field_layout\Plugin\LayoutInterface; /** * Provides a plugin manager for layouts. */ -class LayoutPluginManager implements LayoutPluginManagerInterface { +class LayoutPluginManager extends DefaultPluginManager implements LayoutPluginManagerInterface { - use DiscoveryTrait; - use CategorizingPluginManagerTrait { - getModuleHandler as protected; - getCategories as protected; - getSortedDefinitions as protected; - } + use CategorizingPluginManagerTrait; /** * The module handler. @@ -36,8 +36,22 @@ class LayoutPluginManager implements LayoutPluginManagerInterface { protected $themeHandler; /** + * {@inheritdoc} + */ + protected $defaults = [ + // Used for plugins defined in layouts.yml that do not specify a class + // themselves. + 'class' => LayoutDefault::class, + ]; + + /** * LayoutPluginManager constructor. * + * @param \Traversable $namespaces + * An object that implements \Traversable which contains the root paths + * keyed by the corresponding namespace to look for plugin implementations. + * @param \Drupal\Core\Cache\CacheBackendInterface $cache_backend + * Cache backend instance to use. * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler * The module handler. * @param \Drupal\Core\Extension\ThemeHandlerInterface $theme_handler @@ -45,7 +59,8 @@ class LayoutPluginManager implements LayoutPluginManagerInterface { * @param \Drupal\Core\StringTranslation\TranslationInterface $string_translation * The string translation service. */ - public function __construct(ModuleHandlerInterface $module_handler, ThemeHandlerInterface $theme_handler, TranslationInterface $string_translation) { + public function __construct(\Traversable $namespaces, CacheBackendInterface $cache_backend, ModuleHandlerInterface $module_handler, ThemeHandlerInterface $theme_handler, TranslationInterface $string_translation) { + parent::__construct('Plugin/Layout', $namespaces, $module_handler, LayoutInterface::class, Layout::class); $this->moduleHandler = $module_handler; $this->themeHandler = $theme_handler; $this->stringTranslation = $string_translation; @@ -54,50 +69,27 @@ public function __construct(ModuleHandlerInterface $module_handler, ThemeHandler /** * {@inheritdoc} */ - public function getDefinitions() { - // @todo Replace with layout_plugin in https://www.drupal.org/node/2296423. - $layouts = [ - 'onecol' => [ - 'label' => $this->t('One column'), - 'theme' => 'field_layout__onecol', - 'path' => 'layouts/onecol', - 'provider' => 'field_layout', - 'category' => $this->t('Columns: 1'), - 'default_region' => 'content', - 'regions' => [ - 'content' => [ - 'label' => $this->t('Content'), - ], - ], - ], - 'twocol' => [ - 'label' => $this->t('Two column'), - 'library' => 'field_layout/drupal.field_layout.twocol', - 'theme' => 'field_layout__twocol', - 'path' => 'layouts/twocol', - 'provider' => 'field_layout', - 'category' => $this->t('Columns: 2'), - 'default_region' => 'left', - 'regions' => [ - 'left' => [ - 'label' => $this->t('Left'), - ], - 'right' => [ - 'label' => $this->t('Right'), - ], - ], - ], - ]; - foreach ($layouts as $layout_id => &$layout) { - $this->processDefinition($layout, $layout_id); + protected function providerExists($provider) { + return $this->moduleHandler->moduleExists($provider) || $this->themeHandler->themeExists($provider); + } + + /** + * {@inheritdoc} + */ + protected function getDiscovery() { + if (!$this->discovery) { + $discovery = parent::getDiscovery(); + $this->discovery = new YamlDiscoveryDecorator($discovery, 'layouts', $this->moduleHandler->getModuleDirectories() + $this->themeHandler->getThemeDirectories()); } - return $layouts; + return $this->discovery; } /** - * Performs extra processing on layout definitions. + * {@inheritdoc} */ - protected function processDefinition(&$definition, $plugin_id) { + public function processDefinition(&$definition, $plugin_id) { + parent::processDefinition($definition, $plugin_id); + // Add the module or theme path to the 'path'. if ($this->moduleHandler->moduleExists($definition['provider'])) { $definition['provider_type'] = 'module'; @@ -123,10 +115,6 @@ protected function processDefinition(&$definition, $plugin_id) { elseif (empty($definition['theme'])) { $definition['theme'] = strtr($definition['template'], '-', '_'); } - - // @todo Remove once layout_plugin is done, this will be added by the plugin - // system in https://www.drupal.org/node/2296423. - $definition['id'] = $plugin_id; } /** diff --git a/core/modules/field_layout/src/LayoutPluginManagerInterface.php b/core/modules/field_layout/src/LayoutPluginManagerInterface.php index 2c95561..a85b8d0 100644 --- a/core/modules/field_layout/src/LayoutPluginManagerInterface.php +++ b/core/modules/field_layout/src/LayoutPluginManagerInterface.php @@ -2,32 +2,12 @@ namespace Drupal\field_layout; -use Drupal\Component\Plugin\Discovery\DiscoveryInterface; +use Drupal\Component\Plugin\CategorizingPluginManagerInterface; /** * Provides the interface for a plugin manager of layouts. - * - * @todo Replace with layout_plugin in https://www.drupal.org/node/2296423. */ -interface LayoutPluginManagerInterface extends DiscoveryInterface { - - /** - * Gets sorted plugin definitions grouped by category. - * - * In addition to grouping, both categories and its entries are sorted, - * whereas plugin definitions are sorted by label. - * - * @see \Drupal\Component\Plugin\CategorizingPluginManagerInterface() - * - * @param array[]|null $definitions - * (optional) The plugin definitions to group. If omitted, all plugin - * definitions are used. - * - * @return array[] - * Keys are category names, and values are arrays of which the keys are - * plugin IDs and the values are plugin definitions. - */ - public function getGroupedDefinitions(array $definitions = NULL); +interface LayoutPluginManagerInterface extends CategorizingPluginManagerInterface { /** * Gets theme implementations for layouts. diff --git a/core/modules/field_layout/src/Plugin/LayoutBase.php b/core/modules/field_layout/src/Plugin/LayoutBase.php new file mode 100644 index 0000000..2ba18ed --- /dev/null +++ b/core/modules/field_layout/src/Plugin/LayoutBase.php @@ -0,0 +1,87 @@ +setConfiguration($configuration); + } + + /** + * {@inheritdoc} + */ + public function build(array $regions) { + $build = array_intersect_key($regions, $this->pluginDefinition['regions']); + $build['#theme'] = $this->pluginDefinition['theme']; + if (isset($this->pluginDefinition['library'])) { + $build['#attached']['library'][] = $this->pluginDefinition['library']; + } + return $build; + } + + /** + * {@inheritdoc} + */ + public function buildConfigurationForm(array $form, FormStateInterface $form_state) { + return $form; + } + + /** + * {@inheritdoc} + */ + public function validateConfigurationForm(array &$form, FormStateInterface $form_state) { + } + + /** + * {@inheritdoc} + */ + public function submitConfigurationForm(array &$form, FormStateInterface $form_state) { + $this->configuration = $form_state->getValues(); + } + + /** + * {@inheritdoc} + */ + public function getConfiguration() { + return $this->configuration; + } + + /** + * {@inheritdoc} + */ + public function setConfiguration(array $configuration) { + $this->configuration = NestedArray::mergeDeep( + $this->defaultConfiguration(), + $configuration + ); + } + + /** + * {@inheritdoc} + */ + public function defaultConfiguration() { + return []; + } + + /** + * {@inheritdoc} + */ + public function calculateDependencies() { + return []; + } + +} diff --git a/core/modules/field_layout/src/Plugin/LayoutDefault.php b/core/modules/field_layout/src/Plugin/LayoutDefault.php new file mode 100644 index 0000000..a130700 --- /dev/null +++ b/core/modules/field_layout/src/Plugin/LayoutDefault.php @@ -0,0 +1,10 @@ +layoutPluginManager = $this->prophesize(LayoutPluginManagerInterface::class); $this->layoutPluginManager->getDefinition('unknown', FALSE)->willReturn([]); - $this->layoutPluginManager->getDefinition('twocol', FALSE)->willReturn([ + + $twocol_definition = [ 'library' => 'field_layout/drupal.field_layout.twocol', 'theme' => 'field_layout__twocol', 'regions' => [ @@ -50,8 +52,13 @@ protected function setUp() { 'label' => 'Right', ], ], - ]); + ]; + $layout_plugin = new LayoutDefault([], 'twocol', $twocol_definition); + $this->layoutPluginManager->createInstance('twocol')->willReturn($layout_plugin); + $this->layoutPluginManager->getDefinition('twocol', FALSE)->willReturn($twocol_definition); + $this->entityFieldManager = $this->prophesize(EntityFieldManagerInterface::class); + $this->fieldLayoutBuilder = new FieldLayoutBuilder($this->layoutPluginManager->reveal(), $this->entityFieldManager->reveal()); } diff --git a/core/modules/field_layout/tests/src/Unit/LayoutPluginManagerTest.php b/core/modules/field_layout/tests/src/Unit/LayoutPluginManagerTest.php index 10e053e..4735938 100644 --- a/core/modules/field_layout/tests/src/Unit/LayoutPluginManagerTest.php +++ b/core/modules/field_layout/tests/src/Unit/LayoutPluginManagerTest.php @@ -2,9 +2,11 @@ namespace Drupal\Tests\field_layout\Unit; +use Drupal\Core\Cache\CacheBackendInterface; use Drupal\Core\Extension\Extension; use Drupal\Core\Extension\ModuleHandlerInterface; use Drupal\Core\Extension\ThemeHandlerInterface; +use Drupal\field_layout\Plugin\LayoutDefault; use Drupal\field_layout\LayoutPluginManager; use Drupal\Tests\UnitTestCase; @@ -25,6 +27,13 @@ class LayoutPluginManagerTest extends UnitTestCase { protected $themeHandler; /** + * Cache backend instance. + * + * @var \Drupal\Core\Cache\CacheBackendInterface + */ + protected $cacheBackend; + + /** * @var \Drupal\field_layout\LayoutPluginManager */ protected $layoutPluginManager; @@ -39,10 +48,14 @@ protected function setUp() { $this->moduleHandler->moduleExists('field_layout')->willReturn(TRUE); $extension = new Extension('/', 'module', 'core/modules/field_layout/field_layout.info.yml'); $this->moduleHandler->getModule('field_layout')->willReturn($extension); + $this->moduleHandler->getModuleDirectories()->willReturn(['field_layout' => $this->root . '/core/modules/field_layout']); $this->themeHandler = $this->prophesize(ThemeHandlerInterface::class); + $this->themeHandler->getThemeDirectories()->willReturn([]); + + $this->cacheBackend = $this->prophesize(CacheBackendInterface::class); - $this->layoutPluginManager = new LayoutPluginManager($this->moduleHandler->reveal(), $this->themeHandler->reveal(), $this->getStringTranslationStub()); + $this->layoutPluginManager = new LayoutPluginManager(new \ArrayObject(), $this->cacheBackend->reveal(), $this->moduleHandler->reveal(), $this->themeHandler->reveal(), $this->getStringTranslationStub()); } /** @@ -78,6 +91,7 @@ public function testGetDefinition() { 'library' => 'field_layout/drupal.field_layout.twocol', 'provider_type' => 'module', 'id' => 'twocol', + 'class' => LayoutDefault::class, ]; $layout_definition = $this->layoutPluginManager->getDefinition('twocol'); $this->assertEquals($expected, $layout_definition);