179d1ede9ab7392aaaae32c3d08a7875131d3f4b
[yaffs-website] / web / core / lib / Drupal / Core / Layout / LayoutPluginManager.php
1 <?php
2
3 namespace Drupal\Core\Layout;
4
5 use Drupal\Component\Annotation\Plugin\Discovery\AnnotationBridgeDecorator;
6 use Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException;
7 use Drupal\Core\Cache\CacheBackendInterface;
8 use Drupal\Core\Extension\ModuleHandlerInterface;
9 use Drupal\Core\Extension\ThemeHandlerInterface;
10 use Drupal\Core\Plugin\DefaultPluginManager;
11 use Drupal\Core\Plugin\Discovery\AnnotatedClassDiscovery;
12 use Drupal\Core\Plugin\Discovery\ContainerDerivativeDiscoveryDecorator;
13 use Drupal\Core\Plugin\Discovery\YamlDiscoveryDecorator;
14 use Drupal\Core\Layout\Annotation\Layout;
15
16 /**
17  * Provides a plugin manager for layouts.
18  */
19 class LayoutPluginManager extends DefaultPluginManager implements LayoutPluginManagerInterface {
20
21   /**
22    * The theme handler.
23    *
24    * @var \Drupal\Core\Extension\ThemeHandlerInterface
25    */
26   protected $themeHandler;
27
28   /**
29    * LayoutPluginManager constructor.
30    *
31    * @param \Traversable $namespaces
32    *   An object that implements \Traversable which contains the root paths
33    *   keyed by the corresponding namespace to look for plugin implementations.
34    * @param \Drupal\Core\Cache\CacheBackendInterface $cache_backend
35    *   Cache backend instance to use.
36    * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
37    *   The module handler to invoke the alter hook with.
38    * @param \Drupal\Core\Extension\ThemeHandlerInterface $theme_handler
39    *   The theme handler to invoke the alter hook with.
40    */
41   public function __construct(\Traversable $namespaces, CacheBackendInterface $cache_backend, ModuleHandlerInterface $module_handler, ThemeHandlerInterface $theme_handler) {
42     parent::__construct('Plugin/Layout', $namespaces, $module_handler, LayoutInterface::class, Layout::class);
43     $this->themeHandler = $theme_handler;
44
45     $this->setCacheBackend($cache_backend, 'layout');
46     $this->alterInfo('layout');
47   }
48
49   /**
50    * {@inheritdoc}
51    */
52   protected function providerExists($provider) {
53     return $this->moduleHandler->moduleExists($provider) || $this->themeHandler->themeExists($provider);
54   }
55
56   /**
57    * {@inheritdoc}
58    */
59   protected function getDiscovery() {
60     if (!$this->discovery) {
61       $discovery = new AnnotatedClassDiscovery($this->subdir, $this->namespaces, $this->pluginDefinitionAnnotationName, $this->additionalAnnotationNamespaces);
62       $discovery = new YamlDiscoveryDecorator($discovery, 'layouts', $this->moduleHandler->getModuleDirectories() + $this->themeHandler->getThemeDirectories());
63       $discovery = new AnnotationBridgeDecorator($discovery, $this->pluginDefinitionAnnotationName);
64       $discovery = new ContainerDerivativeDiscoveryDecorator($discovery);
65       $this->discovery = $discovery;
66     }
67     return $this->discovery;
68   }
69
70   /**
71    * {@inheritdoc}
72    */
73   public function processDefinition(&$definition, $plugin_id) {
74     parent::processDefinition($definition, $plugin_id);
75
76     if (!$definition instanceof LayoutDefinition) {
77       throw new InvalidPluginDefinitionException($plugin_id, sprintf('The "%s" layout definition must extend %s', $plugin_id, LayoutDefinition::class));
78     }
79
80     // Add the module or theme path to the 'path'.
81     $provider = $definition->getProvider();
82     if ($this->moduleHandler->moduleExists($provider)) {
83       $base_path = $this->moduleHandler->getModule($provider)->getPath();
84     }
85     elseif ($this->themeHandler->themeExists($provider)) {
86       $base_path = $this->themeHandler->getTheme($provider)->getPath();
87     }
88     else {
89       $base_path = '';
90     }
91
92     $path = $definition->getPath();
93     $path = !empty($path) ? $base_path . '/' . $path : $base_path;
94     $definition->setPath($path);
95
96     // Add the base path to the icon path.
97     if ($icon_path = $definition->getIconPath()) {
98       $definition->setIconPath($path . '/' . $icon_path);
99     }
100
101     // Add a dependency on the provider of the library.
102     if ($library = $definition->getLibrary()) {
103       $config_dependencies = $definition->getConfigDependencies();
104       list($library_provider) = explode('/', $library, 2);
105       if ($this->moduleHandler->moduleExists($library_provider)) {
106         $config_dependencies['module'][] = $library_provider;
107       }
108       elseif ($this->themeHandler->themeExists($library_provider)) {
109         $config_dependencies['theme'][] = $library_provider;
110       }
111       $definition->setConfigDependencies($config_dependencies);
112     }
113
114     // If 'template' is set, then we'll derive 'template_path' and 'theme_hook'.
115     $template = $definition->getTemplate();
116     if (!empty($template)) {
117       $template_parts = explode('/', $template);
118
119       $template = array_pop($template_parts);
120       $template_path = $path;
121       if (count($template_parts) > 0) {
122         $template_path .= '/' . implode('/', $template_parts);
123       }
124       $definition->setTemplate($template);
125       $definition->setThemeHook(strtr($template, '-', '_'));
126       $definition->setTemplatePath($template_path);
127     }
128
129     if (!$definition->getDefaultRegion()) {
130       $definition->setDefaultRegion(key($definition->getRegions()));
131     }
132   }
133
134   /**
135    * {@inheritdoc}
136    */
137   public function getThemeImplementations() {
138     $hooks = [];
139     $hooks['layout'] = [
140       'render element' => 'content',
141     ];
142     /** @var \Drupal\Core\Layout\LayoutDefinition[] $definitions */
143     $definitions = $this->getDefinitions();
144     foreach ($definitions as $definition) {
145       if ($template = $definition->getTemplate()) {
146         $hooks[$definition->getThemeHook()] = [
147           'render element' => 'content',
148           'base hook' => 'layout',
149           'template' => $template,
150           'path' => $definition->getTemplatePath(),
151         ];
152       }
153     }
154     return $hooks;
155   }
156
157   /**
158    * {@inheritdoc}
159    */
160   public function getCategories() {
161     // Fetch all categories from definitions and remove duplicates.
162     $categories = array_unique(array_values(array_map(function (LayoutDefinition $definition) {
163       return $definition->getCategory();
164     }, $this->getDefinitions())));
165     natcasesort($categories);
166     return $categories;
167   }
168
169   /**
170    * {@inheritdoc}
171    *
172    * @return \Drupal\Core\Layout\LayoutDefinition[]
173    */
174   public function getSortedDefinitions(array $definitions = NULL, $label_key = 'label') {
175     // Sort the plugins first by category, then by label.
176     $definitions = isset($definitions) ? $definitions : $this->getDefinitions();
177     // Suppress errors because PHPUnit will indirectly modify the contents,
178     // triggering https://bugs.php.net/bug.php?id=50688.
179     @uasort($definitions, function (LayoutDefinition $a, LayoutDefinition $b) {
180       if ($a->getCategory() != $b->getCategory()) {
181         return strnatcasecmp($a->getCategory(), $b->getCategory());
182       }
183       return strnatcasecmp($a->getLabel(), $b->getLabel());
184     });
185     return $definitions;
186   }
187
188   /**
189    * {@inheritdoc}
190    *
191    * @return \Drupal\Core\Layout\LayoutDefinition[][]
192    */
193   public function getGroupedDefinitions(array $definitions = NULL, $label_key = 'label') {
194     $definitions = $this->getSortedDefinitions(isset($definitions) ? $definitions : $this->getDefinitions(), $label_key);
195     $grouped_definitions = [];
196     foreach ($definitions as $id => $definition) {
197       $grouped_definitions[(string) $definition->getCategory()][$id] = $definition;
198     }
199     return $grouped_definitions;
200   }
201
202   /**
203    * {@inheritdoc}
204    */
205   public function getLayoutOptions() {
206     $layout_options = [];
207     foreach ($this->getGroupedDefinitions() as $category => $layout_definitions) {
208       foreach ($layout_definitions as $name => $layout_definition) {
209         $layout_options[$category][$name] = $layout_definition->getLabel();
210       }
211     }
212     return $layout_options;
213   }
214
215 }