annotate core/lib/Drupal/Core/Layout/LayoutPluginManager.php @ 9:1fc0ff908d1f

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