Chris@0: themeHandler = $theme_handler; Chris@0: Chris@17: $type = $this->getType(); Chris@17: $this->setCacheBackend($cache_backend, $type); Chris@17: $this->alterInfo($type); Chris@17: } Chris@17: Chris@17: /** Chris@17: * {@inheritdoc} Chris@17: */ Chris@17: protected function getType() { Chris@17: return 'layout'; Chris@0: } Chris@0: Chris@0: /** Chris@0: * {@inheritdoc} Chris@0: */ Chris@0: protected function providerExists($provider) { Chris@0: return $this->moduleHandler->moduleExists($provider) || $this->themeHandler->themeExists($provider); Chris@0: } Chris@0: Chris@0: /** Chris@0: * {@inheritdoc} Chris@0: */ Chris@0: protected function getDiscovery() { Chris@0: if (!$this->discovery) { Chris@0: $discovery = new AnnotatedClassDiscovery($this->subdir, $this->namespaces, $this->pluginDefinitionAnnotationName, $this->additionalAnnotationNamespaces); Chris@0: $discovery = new YamlDiscoveryDecorator($discovery, 'layouts', $this->moduleHandler->getModuleDirectories() + $this->themeHandler->getThemeDirectories()); Chris@17: $discovery Chris@17: ->addTranslatableProperty('label') Chris@17: ->addTranslatableProperty('description') Chris@17: ->addTranslatableProperty('category'); Chris@0: $discovery = new AnnotationBridgeDecorator($discovery, $this->pluginDefinitionAnnotationName); Chris@0: $discovery = new ContainerDerivativeDiscoveryDecorator($discovery); Chris@0: $this->discovery = $discovery; Chris@0: } Chris@0: return $this->discovery; Chris@0: } Chris@0: Chris@0: /** Chris@0: * {@inheritdoc} Chris@0: */ Chris@0: public function processDefinition(&$definition, $plugin_id) { Chris@0: parent::processDefinition($definition, $plugin_id); Chris@0: Chris@0: if (!$definition instanceof LayoutDefinition) { Chris@0: throw new InvalidPluginDefinitionException($plugin_id, sprintf('The "%s" layout definition must extend %s', $plugin_id, LayoutDefinition::class)); Chris@0: } Chris@0: Chris@0: // Add the module or theme path to the 'path'. Chris@0: $provider = $definition->getProvider(); Chris@0: if ($this->moduleHandler->moduleExists($provider)) { Chris@0: $base_path = $this->moduleHandler->getModule($provider)->getPath(); Chris@0: } Chris@0: elseif ($this->themeHandler->themeExists($provider)) { Chris@0: $base_path = $this->themeHandler->getTheme($provider)->getPath(); Chris@0: } Chris@0: else { Chris@0: $base_path = ''; Chris@0: } Chris@0: Chris@0: $path = $definition->getPath(); Chris@0: $path = !empty($path) ? $base_path . '/' . $path : $base_path; Chris@0: $definition->setPath($path); Chris@0: Chris@0: // Add the base path to the icon path. Chris@0: if ($icon_path = $definition->getIconPath()) { Chris@0: $definition->setIconPath($path . '/' . $icon_path); Chris@0: } Chris@0: Chris@0: // Add a dependency on the provider of the library. Chris@0: if ($library = $definition->getLibrary()) { Chris@0: $config_dependencies = $definition->getConfigDependencies(); Chris@0: list($library_provider) = explode('/', $library, 2); Chris@0: if ($this->moduleHandler->moduleExists($library_provider)) { Chris@0: $config_dependencies['module'][] = $library_provider; Chris@0: } Chris@0: elseif ($this->themeHandler->themeExists($library_provider)) { Chris@0: $config_dependencies['theme'][] = $library_provider; Chris@0: } Chris@0: $definition->setConfigDependencies($config_dependencies); Chris@0: } Chris@0: Chris@0: // If 'template' is set, then we'll derive 'template_path' and 'theme_hook'. Chris@0: $template = $definition->getTemplate(); Chris@0: if (!empty($template)) { Chris@0: $template_parts = explode('/', $template); Chris@0: Chris@0: $template = array_pop($template_parts); Chris@0: $template_path = $path; Chris@0: if (count($template_parts) > 0) { Chris@0: $template_path .= '/' . implode('/', $template_parts); Chris@0: } Chris@0: $definition->setTemplate($template); Chris@0: $definition->setThemeHook(strtr($template, '-', '_')); Chris@0: $definition->setTemplatePath($template_path); Chris@0: } Chris@0: Chris@0: if (!$definition->getDefaultRegion()) { Chris@0: $definition->setDefaultRegion(key($definition->getRegions())); Chris@0: } Chris@17: // Makes sure region names are translatable. Chris@17: $regions = array_map(function ($region) { Chris@17: if (!$region['label'] instanceof TranslatableMarkup) { Chris@17: // Region labels from YAML discovery needs translation. Chris@17: $region['label'] = new TranslatableMarkup($region['label'], [], ['context' => 'layout_region']); Chris@17: } Chris@17: return $region; Chris@17: }, $definition->getRegions()); Chris@17: $definition->setRegions($regions); Chris@0: } Chris@0: Chris@0: /** Chris@0: * {@inheritdoc} Chris@0: */ Chris@0: public function getThemeImplementations() { Chris@0: $hooks = []; Chris@0: $hooks['layout'] = [ Chris@0: 'render element' => 'content', Chris@0: ]; Chris@0: /** @var \Drupal\Core\Layout\LayoutDefinition[] $definitions */ Chris@0: $definitions = $this->getDefinitions(); Chris@0: foreach ($definitions as $definition) { Chris@0: if ($template = $definition->getTemplate()) { Chris@0: $hooks[$definition->getThemeHook()] = [ Chris@0: 'render element' => 'content', Chris@0: 'base hook' => 'layout', Chris@0: 'template' => $template, Chris@0: 'path' => $definition->getTemplatePath(), Chris@0: ]; Chris@0: } Chris@0: } Chris@0: return $hooks; Chris@0: } Chris@0: Chris@0: /** Chris@0: * {@inheritdoc} Chris@0: */ Chris@0: public function getCategories() { Chris@0: // Fetch all categories from definitions and remove duplicates. Chris@0: $categories = array_unique(array_values(array_map(function (LayoutDefinition $definition) { Chris@0: return $definition->getCategory(); Chris@0: }, $this->getDefinitions()))); Chris@0: natcasesort($categories); Chris@0: return $categories; Chris@0: } Chris@0: Chris@0: /** Chris@0: * {@inheritdoc} Chris@0: * Chris@0: * @return \Drupal\Core\Layout\LayoutDefinition[] Chris@0: */ Chris@0: public function getSortedDefinitions(array $definitions = NULL, $label_key = 'label') { Chris@0: // Sort the plugins first by category, then by label. Chris@0: $definitions = isset($definitions) ? $definitions : $this->getDefinitions(); Chris@0: // Suppress errors because PHPUnit will indirectly modify the contents, Chris@0: // triggering https://bugs.php.net/bug.php?id=50688. Chris@0: @uasort($definitions, function (LayoutDefinition $a, LayoutDefinition $b) { Chris@0: if ($a->getCategory() != $b->getCategory()) { Chris@0: return strnatcasecmp($a->getCategory(), $b->getCategory()); Chris@0: } Chris@0: return strnatcasecmp($a->getLabel(), $b->getLabel()); Chris@0: }); Chris@0: return $definitions; Chris@0: } Chris@0: Chris@0: /** Chris@0: * {@inheritdoc} Chris@0: * Chris@0: * @return \Drupal\Core\Layout\LayoutDefinition[][] Chris@0: */ Chris@0: public function getGroupedDefinitions(array $definitions = NULL, $label_key = 'label') { Chris@0: $definitions = $this->getSortedDefinitions(isset($definitions) ? $definitions : $this->getDefinitions(), $label_key); Chris@0: $grouped_definitions = []; Chris@0: foreach ($definitions as $id => $definition) { Chris@0: $grouped_definitions[(string) $definition->getCategory()][$id] = $definition; Chris@0: } Chris@0: return $grouped_definitions; Chris@0: } Chris@0: Chris@0: /** Chris@0: * {@inheritdoc} Chris@0: */ Chris@0: public function getLayoutOptions() { Chris@0: $layout_options = []; Chris@0: foreach ($this->getGroupedDefinitions() as $category => $layout_definitions) { Chris@0: foreach ($layout_definitions as $name => $layout_definition) { Chris@0: $layout_options[$category][$name] = $layout_definition->getLabel(); Chris@0: } Chris@0: } Chris@0: return $layout_options; Chris@0: } Chris@0: Chris@0: }