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