Code Coverage |
||||||||||||||||
Lines |
Branches |
Paths |
Functions and Methods |
Classes and Traits |
||||||||||||
| Total | |
0.00% |
0 / 66 |
n/a |
0 / 0 |
n/a |
0 / 0 |
|
0.00% |
0 / 6 |
CRAP | |
0.00% |
0 / 1 |
||
| DisplayBuilderPageVariant | |
0.00% |
0 / 63 |
n/a |
0 / 0 |
n/a |
0 / 0 |
|
0.00% |
0 / 6 |
306 | |
0.00% |
0 / 1 |
||
| __construct | |
0.00% |
0 / 5 |
n/a |
0 / 0 |
n/a |
0 / 0 |
|
0.00% |
0 / 1 |
2 | |||||
| create | |
0.00% |
0 / 9 |
n/a |
0 / 0 |
n/a |
0 / 0 |
|
0.00% |
0 / 1 |
2 | |||||
| build | |
0.00% |
0 / 34 |
n/a |
0 / 0 |
n/a |
0 / 0 |
|
0.00% |
0 / 1 |
42 | |||||
| setMainContent | |
0.00% |
0 / 2 |
n/a |
0 / 0 |
n/a |
0 / 0 |
|
0.00% |
0 / 1 |
2 | |||||
| setTitle | |
0.00% |
0 / 2 |
n/a |
0 / 0 |
n/a |
0 / 0 |
|
0.00% |
0 / 1 |
2 | |||||
| replaceTitleAndContent | |
0.00% |
0 / 11 |
n/a |
0 / 0 |
n/a |
0 / 0 |
|
0.00% |
0 / 1 |
56 | |||||
| 1 | <?php |
| 2 | |
| 3 | declare(strict_types=1); |
| 4 | |
| 5 | namespace Drupal\display_builder_page_layout\Plugin\DisplayVariant; |
| 6 | |
| 7 | use Drupal\Component\Render\MarkupInterface; |
| 8 | use Drupal\Core\Cache\CacheableMetadata; |
| 9 | use Drupal\Core\Display\Attribute\PageDisplayVariant; |
| 10 | use Drupal\Core\Display\PageVariantInterface; |
| 11 | use Drupal\Core\Display\VariantBase; |
| 12 | use Drupal\Core\Entity\EntityTypeManagerInterface; |
| 13 | use Drupal\Core\Extension\ExtensionList; |
| 14 | use Drupal\Core\Plugin\ContainerFactoryPluginInterface; |
| 15 | use Drupal\Core\StringTranslation\TranslatableMarkup; |
| 16 | use Drupal\Core\Theme\Registry; |
| 17 | use Drupal\display_builder\DisplayBuilderHelpers; |
| 18 | use Drupal\ui_patterns\Element\ComponentElementBuilder; |
| 19 | use Symfony\Component\DependencyInjection\ContainerInterface; |
| 20 | |
| 21 | /** |
| 22 | * A variant for pages managed by Display Builder Page Layout. |
| 23 | */ |
| 24 | #[PageDisplayVariant( |
| 25 | id: 'display_builder', |
| 26 | admin_label: new TranslatableMarkup('Display Builder page') |
| 27 | )] |
| 28 | class DisplayBuilderPageVariant extends VariantBase implements ContainerFactoryPluginInterface, PageVariantInterface { |
| 29 | |
| 30 | private const SOURCE_CONTENT_ID = 'main_page_content'; |
| 31 | |
| 32 | private const SOURCE_TITLE_ID = 'page_title'; |
| 33 | |
| 34 | /** |
| 35 | * The render array representing the main content. |
| 36 | */ |
| 37 | protected array $mainContent; |
| 38 | |
| 39 | /** |
| 40 | * The page title. |
| 41 | * |
| 42 | * Can be a string (plain title), Markup or a render array (formatted title). |
| 43 | */ |
| 44 | protected array|MarkupInterface|string $title; |
| 45 | |
| 46 | /** |
| 47 | * Component element builder. |
| 48 | */ |
| 49 | protected ComponentElementBuilder $componentElementBuilder; |
| 50 | |
| 51 | /** |
| 52 | * The entity type manager. |
| 53 | */ |
| 54 | protected EntityTypeManagerInterface $entityTypeManager; |
| 55 | |
| 56 | /** |
| 57 | * The theme registry. |
| 58 | */ |
| 59 | protected Registry $themeRegistry; |
| 60 | |
| 61 | /** |
| 62 | * The list of modules. |
| 63 | */ |
| 64 | protected ExtensionList $modules; |
| 65 | |
| 66 | public function __construct( |
| 67 | array $configuration, |
| 68 | $plugin_id, |
| 69 | $plugin_definition, |
| 70 | ComponentElementBuilder $component_element_builder, |
| 71 | EntityTypeManagerInterface $entity_type_manager, |
| 72 | Registry $theme_registry, |
| 73 | ExtensionList $modules, |
| 74 | ) { |
| 75 | parent::__construct($configuration, $plugin_id, $plugin_definition); |
| 76 | $this->componentElementBuilder = $component_element_builder; |
| 77 | $this->entityTypeManager = $entity_type_manager; |
| 78 | $this->themeRegistry = $theme_registry; |
| 79 | $this->modules = $modules; |
| 80 | } |
| 81 | |
| 82 | /** |
| 83 | * {@inheritdoc} |
| 84 | */ |
| 85 | public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition): static { |
| 86 | return new static( |
| 87 | $configuration, |
| 88 | $plugin_id, |
| 89 | $plugin_definition, |
| 90 | $container->get('ui_patterns.component_element_builder'), |
| 91 | $container->get('entity_type.manager'), |
| 92 | $container->get('theme.registry'), |
| 93 | $container->get('extension.list.module'), |
| 94 | ); |
| 95 | } |
| 96 | |
| 97 | /** |
| 98 | * {@inheritdoc} |
| 99 | */ |
| 100 | public function build(): array { |
| 101 | /** @var \Drupal\display_builder_page_layout\AccessControlHandler $access_control */ |
| 102 | $access_control = $this->entityTypeManager->getAccessControlHandler('page_layout'); |
| 103 | $page_layout = $access_control->loadCurrentPageLayout(); |
| 104 | |
| 105 | if (!$page_layout) { |
| 106 | // This is not supposed to happen. PageVariantSubscriber must not load |
| 107 | // this plugin, and fallback to Block Layout if there is no suitable |
| 108 | // Page Layout entities. |
| 109 | // @todo raise an error |
| 110 | return []; |
| 111 | } |
| 112 | |
| 113 | // We alter the registry here instead of implementing |
| 114 | // hook_theme_registry_alter in order keep the alteration specific to each |
| 115 | // page. |
| 116 | $theme_registry = $this->themeRegistry->get(); |
| 117 | $template_uri = $this->modules->getPath('display_builder_page_layout') . '/templates'; |
| 118 | $runtime = $this->themeRegistry->getRuntime(); |
| 119 | $theme_registry['page']['path'] = $template_uri; |
| 120 | $runtime->set('page', $theme_registry['page']); |
| 121 | $theme_registry['region']['path'] = $template_uri; |
| 122 | $runtime->set('region', $theme_registry['region']); |
| 123 | |
| 124 | // Also skip the related template suggestions. |
| 125 | foreach (\array_keys($theme_registry) as $renderable_id) { |
| 126 | if (\str_starts_with($renderable_id, 'page__') || \str_starts_with($renderable_id, 'region__')) { |
| 127 | $runtime->delete($renderable_id); |
| 128 | } |
| 129 | } |
| 130 | |
| 131 | $sources = $page_layout->getSources(); |
| 132 | $instance_id = $page_layout->getInstanceId(); |
| 133 | $this->replaceTitleAndContent($sources, $this->title, $this->mainContent); |
| 134 | |
| 135 | /** @var \Drupal\display_builder\InstanceInterface $instance */ |
| 136 | $instance = $this->entityTypeManager->getStorage('display_builder_instance')->load($instance_id); |
| 137 | $contexts = $instance->getContexts() ?? []; |
| 138 | $data = []; |
| 139 | |
| 140 | foreach ($sources as $source) { |
| 141 | $build = $this->componentElementBuilder->buildSource($data, 'content', [], $source, $contexts); |
| 142 | $data[] = $build['#slots']['content'][0] ?? []; |
| 143 | } |
| 144 | |
| 145 | $cache = new CacheableMetadata(); |
| 146 | $cache->addCacheableDependency($this); |
| 147 | // See also: PageLayout::postSave(). |
| 148 | // See also: PageVariantSubscriber::onSelectPageDisplayVariant() |
| 149 | $cache->addCacheTags($page_layout->getCacheTags()); |
| 150 | |
| 151 | $build = [ |
| 152 | 'display_builder_content' => [ |
| 153 | 'data' => $data, |
| 154 | '#weight' => -800, |
| 155 | ], |
| 156 | ]; |
| 157 | $cache->applyTo($build); |
| 158 | |
| 159 | return $build; |
| 160 | } |
| 161 | |
| 162 | /** |
| 163 | * {@inheritdoc} |
| 164 | */ |
| 165 | public function setMainContent(array $main_content): self { |
| 166 | $this->mainContent = $main_content; |
| 167 | |
| 168 | return $this; |
| 169 | } |
| 170 | |
| 171 | /** |
| 172 | * {@inheritdoc} |
| 173 | */ |
| 174 | public function setTitle($title): self { |
| 175 | $this->title = $title; |
| 176 | |
| 177 | return $this; |
| 178 | } |
| 179 | |
| 180 | /** |
| 181 | * Replace title and content blocks. |
| 182 | * |
| 183 | * @param array $data |
| 184 | * The Display Builder data to alter. |
| 185 | * @param mixed $title |
| 186 | * The title to set. |
| 187 | * @param array|null $content |
| 188 | * The content to set. |
| 189 | * |
| 190 | * @todo replace multiple in one pass? |
| 191 | */ |
| 192 | private function replaceTitleAndContent(array &$data, mixed $title, ?array $content): void { |
| 193 | if ($content !== NULL) { |
| 194 | DisplayBuilderHelpers::findArrayReplaceSource($data, ['source_id' => self::SOURCE_CONTENT_ID], $content); |
| 195 | } |
| 196 | |
| 197 | // Try to handle specific title cases. |
| 198 | if (\is_string($title)) { |
| 199 | $title = $title; |
| 200 | } |
| 201 | elseif ($title instanceof MarkupInterface) { |
| 202 | $title = (string) $title; |
| 203 | } |
| 204 | elseif (isset($title['#markup'])) { |
| 205 | $title = $title['#markup']; |
| 206 | } |
| 207 | |
| 208 | if ($title !== NULL && \is_string($title)) { |
| 209 | // @todo avoid arbitrary classes. |
| 210 | $title = ['#markup' => '<h1 class="title page-title">' . $title . '</h1>']; |
| 211 | DisplayBuilderHelpers::findArrayReplaceSource($data, ['source_id' => self::SOURCE_TITLE_ID], $title); |
| 212 | } |
| 213 | } |
| 214 | |
| 215 | } |
Below are the source code lines that represent each code path as identified by Xdebug. Please note a path is not
necessarily coterminous with a line, a line may contain multiple paths and therefore show up more than once.
Please also be aware that some paths may include implicit rather than explicit branches, e.g. an if statement
always has an else as part of its logical flow even if you didn't write one.