3 namespace Drupal\layout_plugin\Plugin\Layout;
5 use Drupal\Core\Cache\CacheBackendInterface;
6 use Drupal\Core\Extension\ModuleHandlerInterface;
7 use Drupal\Core\Extension\ThemeHandlerInterface;
8 use Drupal\Core\Plugin\CategorizingPluginManagerTrait;
9 use Drupal\Core\Plugin\DefaultPluginManager;
10 use Drupal\Core\Plugin\Discovery\YamlDiscoveryDecorator;
13 * Plugin type manager for all layouts.
15 class LayoutPluginManager extends DefaultPluginManager implements LayoutPluginManagerInterface {
17 use CategorizingPluginManagerTrait;
22 * @var \Drupal\Core\Extension\ThemeHandlerInterface
24 protected $themeHandler;
27 * Constructs a LayoutPluginManager object.
29 * @param \Traversable $namespaces
30 * An object that implements \Traversable which contains the root paths
31 * keyed by the corresponding namespace to look for plugin implementations.
32 * @param \Drupal\Core\Cache\CacheBackendInterface $cache_backend
33 * Cache backend instance to use.
34 * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
35 * The module handler to invoke the alter hook with.
36 * @param \Drupal\Core\Extension\ThemeHandlerInterface $theme_handler
37 * The theme handle to invoke the alter hook with.
39 public function __construct(\Traversable $namespaces, CacheBackendInterface $cache_backend, ModuleHandlerInterface $module_handler, ThemeHandlerInterface $theme_handler) {
40 $plugin_interface = 'Drupal\layout_plugin\Plugin\Layout\LayoutInterface';
41 $plugin_definition_annotation_name = 'Drupal\layout_plugin\Annotation\Layout';
42 parent::__construct("Plugin/Layout", $namespaces, $module_handler, $plugin_interface, $plugin_definition_annotation_name);
43 $discovery = $this->getDiscovery();
44 $this->discovery = new YamlDiscoveryDecorator($discovery, 'layouts', $module_handler->getModuleDirectories() + $theme_handler->getThemeDirectories());
45 $this->themeHandler = $theme_handler;
47 $this->defaults += array(
49 // Used for plugins defined in layouts.yml that do not specify a class
51 'class' => 'Drupal\layout_plugin\Plugin\Layout\LayoutDefault',
54 $this->setCacheBackend($cache_backend, 'layout');
55 $this->alterInfo('layout');
61 protected function providerExists($provider) {
62 return $this->moduleHandler->moduleExists($provider) || $this->themeHandler->themeExists($provider);
68 public function processDefinition(&$definition, $plugin_id) {
69 parent::processDefinition($definition, $plugin_id);
71 // Add the module or theme path to the 'path'.
72 if ($this->moduleHandler->moduleExists($definition['provider'])) {
73 $definition['provider_type'] = 'module';
74 $base_path = $this->moduleHandler->getModule($definition['provider'])->getPath();
76 elseif ($this->themeHandler->themeExists($definition['provider'])) {
77 $definition['provider_type'] = 'theme';
78 $base_path = $this->themeHandler->getTheme($definition['provider'])->getPath();
83 $definition['path'] = !empty($definition['path']) ? $base_path . '/' . $definition['path'] : $base_path;
85 // Add a dependency on the provider of the library.
86 if (!empty($definition['library'])) {
87 list ($library_provider, ) = explode('/', $definition['library']);
88 if ($this->moduleHandler->moduleExists($library_provider)) {
89 $definition['dependencies'] = ['module' => [$library_provider]];
91 elseif ($this->themeHandler->themeExists($library_provider)) {
92 $definition['dependencies'] = ['theme' => [$library_provider]];
96 // Add the path to the icon filename.
97 if (!empty($definition['icon'])) {
98 $definition['icon'] = $definition['path'] . '/' . $definition['icon'];
101 // If 'template' is set, then we'll derive 'template_path' and 'theme'.
102 if (!empty($definition['template'])) {
103 $template_parts = explode('/', $definition['template']);
105 $definition['template'] = array_pop($template_parts);
106 $definition['theme'] = strtr($definition['template'], '-', '_');
107 $definition['template_path'] = $definition['path'];
108 if (count($template_parts) > 0) {
109 $definition['template_path'] .= '/' . implode('/', $template_parts);
113 // If 'css' is set, then we'll derive 'library'.
114 if (!empty($definition['css'])) {
115 $definition['css'] = $definition['path'] . '/' . $definition['css'];
116 $definition['library'] = 'layout_plugin/' . $plugin_id;
119 // Generate the 'region_names' key from the 'regions' key.
120 $definition['region_names'] = array();
121 if (!empty($definition['regions']) && is_array($definition['regions'])) {
122 foreach ($definition['regions'] as $region_id => $region_definition) {
123 $definition['region_names'][$region_id] = $region_definition['label'];
131 public function getLayoutOptions(array $params = []) {
132 $group_by_category = !empty($params['group_by_category']);
133 $plugins = $group_by_category ? $this->getGroupedDefinitions() : ['default' => $this->getDefinitions()];
134 $categories = $group_by_category ? $this->getCategories() : ['default'];
136 // Go through each category, sort it, and get just the labels out.
138 foreach ($categories as $category) {
139 // Convert from a translation to a real string.
140 $category = (string) $category;
142 // Sort the category.
143 $plugins[$category] = $this->getSortedDefinitions($plugins[$category]);
145 // Put only the label in the options array.
146 foreach ($plugins[$category] as $id => $plugin) {
147 $options[$category][$id] = $plugin['label'];
151 return $group_by_category ? $options : $options['default'];
157 public function getThemeImplementations() {
158 $plugins = $this->getDefinitions();
160 $theme_registry = [];
161 foreach ($plugins as $id => $definition) {
162 if (!empty($definition['template']) && !empty($definition['theme'])) {
163 $theme_registry[$definition['theme']] = [
164 'render element' => 'content',
165 'template' => $definition['template'],
166 'path' => $definition['template_path'],
171 return $theme_registry;
177 public function alterThemeImplementations(array &$theme_registry) {
178 $plugins = $this->getDefinitions();
180 // Find all the theme hooks which are for automatically registered templates
181 // (we ignore manually set theme hooks because we don't know how they were
183 $layout_theme_hooks = [];
184 foreach ($plugins as $id => $definition) {
185 if (!empty($definition['template']) && !empty($definition['theme']) && isset($theme_registry[$definition['theme']])) {
186 $layout_theme_hooks[] = $definition['theme'];
190 // Go through the theme registry looking for our theme hooks and any
191 // suggestions based on them.
192 foreach ($theme_registry as $theme_hook => &$info) {
193 if (in_array($theme_hook, $layout_theme_hooks) || (!empty($info['base hook']) && in_array($info['base hook'], $layout_theme_hooks))) {
194 // If 'template_preprocess' is included, we want to put our preprocess
195 // after to not mess up the expectation that 'template_process' always
197 if (($index = array_search('template_preprocess', $info['preprocess functions'])) !== FALSE) {
201 // Otherwise, put our preprocess function first.
205 array_splice($info['preprocess functions'], $index, 0, '_layout_plugin_preprocess_layout');
211 * Gets the version of the given provider.
213 * Wraps system_get_info() so that we can mock it in our tests.
215 * @param string $provider_type
216 * The provider type (ex. module or theme).
217 * @param string $provider
218 * The name of the provider.
221 * The version string for the provider or 'VERSION' if it can't be found.
223 protected function getProviderVersion($provider_type, $provider) {
224 $info = system_get_info($provider_type, $provider);
225 return !empty($info['version']) ? $info['version'] : 'VERSION';
231 public function getLibraryInfo() {
232 $plugins = $this->getDefinitions();
235 foreach ($plugins as $id => $definition) {
236 if (!empty($definition['css']) && !empty($definition['library'])) {
237 list ($library_module, $library_name) = explode('/', $definition['library']);
239 // Make sure the library is from layout_plugin.
240 if ($library_module != 'layout_plugin') {
244 $library_info[$library_name] = [
245 'version' => $this->getProviderVersion($definition['provider_type'], $definition['provider']),
248 '/' . $definition['css'] => [],
255 return $library_info;