annotate core/lib/Drupal/Core/Layout/LayoutPluginManager.php @ 19:fa3358dc1485 tip

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