Code Coverage |
||||||||||||||||
Lines |
Branches |
Paths |
Functions and Methods |
Classes and Traits |
||||||||||||
| Total | |
62.90% |
39 / 62 |
|
60.61% |
20 / 33 |
|
31.82% |
7 / 22 |
|
22.22% |
2 / 9 |
CRAP | |
0.00% |
0 / 1 |
| DisplayExtender | |
62.90% |
39 / 62 |
|
60.61% |
20 / 33 |
|
31.82% |
7 / 22 |
|
22.22% |
2 / 9 |
160.78 | |
0.00% |
0 / 1 |
| create | |
100.00% |
5 / 5 |
|
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
| buildOptionsForm | |
75.00% |
3 / 4 |
|
66.67% |
2 / 3 |
|
50.00% |
1 / 2 |
|
0.00% |
0 / 1 |
2.50 | |||
| submitOptionsForm | |
57.14% |
8 / 14 |
|
66.67% |
6 / 9 |
|
16.67% |
1 / 6 |
|
0.00% |
0 / 1 |
19.47 | |||
| optionsSummary | |
88.89% |
8 / 9 |
|
66.67% |
2 / 3 |
|
50.00% |
1 / 2 |
|
0.00% |
0 / 1 |
2.50 | |||
| preExecute | |
0.00% |
0 / 7 |
|
0.00% |
0 / 3 |
|
0.00% |
0 / 2 |
|
0.00% |
0 / 1 |
6 | |||
| buildThemeRegistryEntry | |
0.00% |
0 / 4 |
|
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
| getInstance | |
85.71% |
6 / 7 |
|
80.00% |
4 / 5 |
|
33.33% |
1 / 3 |
|
0.00% |
0 / 1 |
5.67 | |||
| isApplicable | |
66.67% |
6 / 9 |
|
57.14% |
4 / 7 |
|
25.00% |
1 / 4 |
|
0.00% |
0 / 1 |
10.75 | |||
| displayBuildable | |
100.00% |
3 / 3 |
|
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
| 1 | <?php |
| 2 | |
| 3 | declare(strict_types=1); |
| 4 | |
| 5 | namespace Drupal\display_builder_views\Plugin\views\display_extender; |
| 6 | |
| 7 | use Drupal\Core\Extension\ModuleExtensionList; |
| 8 | use Drupal\Core\Form\FormStateInterface; |
| 9 | use Drupal\Core\StringTranslation\TranslatableMarkup; |
| 10 | use Drupal\Core\Theme\Registry; |
| 11 | use Drupal\display_builder\DisplayBuildableInterface; |
| 12 | use Drupal\display_builder\InstanceInterface; |
| 13 | use Drupal\views\Attribute\ViewsDisplayExtender; |
| 14 | use Drupal\views\Plugin\views\display_extender\DisplayExtenderPluginBase; |
| 15 | use Symfony\Component\DependencyInjection\ContainerInterface; |
| 16 | |
| 17 | /** |
| 18 | * Styles display extender plugin. |
| 19 | * |
| 20 | * @ingroup views_display_extender_plugins |
| 21 | */ |
| 22 | #[ViewsDisplayExtender( |
| 23 | id: 'display_builder', |
| 24 | title: new TranslatableMarkup('Display Builder'), |
| 25 | help: new TranslatableMarkup('Use display builder as output for this view.'), |
| 26 | no_ui: FALSE, |
| 27 | )] |
| 28 | final class DisplayExtender extends DisplayExtenderPluginBase { |
| 29 | |
| 30 | /** |
| 31 | * The entity type interface. |
| 32 | * |
| 33 | * @var \Drupal\Core\Entity\EntityTypeManagerInterface |
| 34 | */ |
| 35 | protected $entityTypeManager; |
| 36 | |
| 37 | /** |
| 38 | * The theme registry. |
| 39 | */ |
| 40 | protected Registry $themeRegistry; |
| 41 | |
| 42 | /** |
| 43 | * The list of modules. |
| 44 | */ |
| 45 | protected ModuleExtensionList $modules; |
| 46 | |
| 47 | /** |
| 48 | * The loaded display builder instance. |
| 49 | */ |
| 50 | protected ?InstanceInterface $instance; |
| 51 | |
| 52 | /** |
| 53 | * {@inheritdoc} |
| 54 | */ |
| 55 | public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition): static { |
| 56 | $instance = parent::create($container, $configuration, $plugin_id, $plugin_definition); |
| 57 | $instance->entityTypeManager = $container->get('entity_type.manager'); |
| 58 | $instance->themeRegistry = $container->get('theme.registry'); |
| 59 | $instance->modules = $container->get('extension.list.module'); |
| 60 | |
| 61 | return $instance; |
| 62 | } |
| 63 | |
| 64 | /** |
| 65 | * {@inheritdoc} |
| 66 | */ |
| 67 | public function buildOptionsForm(&$form, FormStateInterface $form_state): void { |
| 68 | if ($form_state->get('section') !== 'display_builder') { |
| 69 | return; |
| 70 | } |
| 71 | |
| 72 | $form['#title'] .= $this->t('Display Builder'); |
| 73 | $form[DisplayBuildableInterface::PROFILE_PROPERTY] = $this->displayBuildable()->buildInstanceForm(FALSE); |
| 74 | } |
| 75 | |
| 76 | /** |
| 77 | * {@inheritdoc} |
| 78 | */ |
| 79 | public function submitOptionsForm(&$form, FormStateInterface $form_state): void { |
| 80 | if ($form_state->get('section') !== 'display_builder') { |
| 81 | return; |
| 82 | } |
| 83 | |
| 84 | // @todo we should have always a fallback. |
| 85 | $profile_id = $form_state->getValue(DisplayBuildableInterface::PROFILE_PROPERTY, 'default'); |
| 86 | $this->options[DisplayBuildableInterface::PROFILE_PROPERTY] = $profile_id; |
| 87 | $buildable = $this->displayBuildable(); |
| 88 | |
| 89 | if (empty($profile_id)) { |
| 90 | // If no Display Builder selected, we delete the related instance. |
| 91 | // @todo Do we move that to the View's EntityInterface::delete() method? |
| 92 | // @todo Also, when the changed are canceled from UI leaving the View |
| 93 | // without Display Builder. |
| 94 | $storage = $this->entityTypeManager->getStorage('display_builder_instance'); |
| 95 | $storage->delete([$this->getInstance()]); |
| 96 | |
| 97 | return; |
| 98 | } |
| 99 | |
| 100 | $buildable->initInstanceIfMissing(); |
| 101 | |
| 102 | // Save the profile in the instance if changed. |
| 103 | $instance = $this->getInstance(); |
| 104 | |
| 105 | if ($instance && $buildable->getProfile()->id() !== $profile_id) { |
| 106 | $instance->setProfile($profile_id); |
| 107 | $instance->save(); |
| 108 | } |
| 109 | } |
| 110 | |
| 111 | /** |
| 112 | * {@inheritdoc} |
| 113 | */ |
| 114 | public function optionsSummary(&$categories, &$options): void { |
| 115 | $buildable = $this->displayBuildable(); |
| 116 | |
| 117 | if (!$this->isApplicable()) { |
| 118 | return; |
| 119 | } |
| 120 | |
| 121 | $options['display_builder'] = [ |
| 122 | 'category' => 'other', |
| 123 | 'title' => $this->t('Display Builder'), |
| 124 | 'desc' => $this->t('Use display builder as output for this view.'), |
| 125 | 'value' => $buildable->getProfile()?->label() ?? $this->t('Disabled'), |
| 126 | ]; |
| 127 | } |
| 128 | |
| 129 | /** |
| 130 | * {@inheritdoc} |
| 131 | */ |
| 132 | public function preExecute(): void { |
| 133 | $buildable = $this->displayBuildable(); |
| 134 | |
| 135 | if (!$buildable->getProfile()) { |
| 136 | return; |
| 137 | } |
| 138 | // We alter the registry here instead of implementing |
| 139 | // hook_theme_registry_alter in order keep the alteration specific to each |
| 140 | // view. |
| 141 | $view = $this->view; |
| 142 | // Theme hook suggestion of the current view display. |
| 143 | $suggestion = \implode('__', ['views_view', $view->id(), $view->getDisplay()->getPluginId()]); |
| 144 | $entry = $this->buildThemeRegistryEntry(); |
| 145 | $this->themeRegistry->getRuntime()->set($suggestion, $entry); |
| 146 | } |
| 147 | |
| 148 | /** |
| 149 | * Build theme registry entry. |
| 150 | * |
| 151 | * @return array |
| 152 | * A theme registry entry. |
| 153 | */ |
| 154 | protected function buildThemeRegistryEntry(): array { |
| 155 | $theme_registry = $this->themeRegistry->get(); |
| 156 | // Identical to views_view with a specific path. |
| 157 | $entry = $theme_registry['views_view']; |
| 158 | $entry['path'] = $this->modules->getPath('display_builder_views') . '/templates'; |
| 159 | |
| 160 | return $entry; |
| 161 | } |
| 162 | |
| 163 | /** |
| 164 | * Gets the Display Builder instance. |
| 165 | * |
| 166 | * @return \Drupal\display_builder\InstanceInterface|null |
| 167 | * A display builder instance. |
| 168 | */ |
| 169 | protected function getInstance(): ?InstanceInterface { |
| 170 | if (!$this->displayBuildable()->getInstanceId()) { |
| 171 | return NULL; |
| 172 | } |
| 173 | |
| 174 | if (!isset($this->instance)) { |
| 175 | $instance_id = $this->displayBuildable()->getInstanceId(); |
| 176 | /** @var \Drupal\display_builder\InstanceInterface|null $instance */ |
| 177 | $instance = $this->entityTypeManager->getStorage('display_builder_instance')->load($instance_id); |
| 178 | $this->instance = $instance; |
| 179 | } |
| 180 | |
| 181 | return $this->instance; |
| 182 | } |
| 183 | |
| 184 | /** |
| 185 | * If display builder can be applied to this display. |
| 186 | * |
| 187 | * @return bool |
| 188 | * Applicable or not. |
| 189 | */ |
| 190 | private function isApplicable(): bool { |
| 191 | $display = $this->view->getDisplay(); |
| 192 | $display_definition = $display->getPluginDefinition(); |
| 193 | |
| 194 | if (!isset($display_definition['class'])) { |
| 195 | return FALSE; |
| 196 | } |
| 197 | |
| 198 | // Do not include with feed and entity reference, as they have no output to |
| 199 | // apply a display builder to. |
| 200 | if ($display_definition['class'] === 'Drupal\views\Plugin\views\display\Feed') { |
| 201 | return FALSE; |
| 202 | } |
| 203 | |
| 204 | if ($display_definition['class'] === 'Drupal\views\Plugin\views\display\EntityReference') { |
| 205 | return FALSE; |
| 206 | } |
| 207 | |
| 208 | // @todo safer to not allow third party display? |
| 209 | // phpcs:disable |
| 210 | // if (str_contains($display_definition['class'], 'Drupal\views\Plugin\views\display')) { |
| 211 | // return FALSE; |
| 212 | // } |
| 213 | // phpcs:enable |
| 214 | |
| 215 | return TRUE; |
| 216 | } |
| 217 | |
| 218 | /** |
| 219 | * Gets the display buildable manager. |
| 220 | * |
| 221 | * @return \Drupal\display_builder\DisplayBuildableInterface |
| 222 | * The manager for display buildable. |
| 223 | */ |
| 224 | private function displayBuildable(): DisplayBuildableInterface { |
| 225 | /** @var \Drupal\display_builder\DisplayBuildablePluginManager $manager */ |
| 226 | $manager = \Drupal::service('plugin.manager.display_buildable'); |
| 227 | /** @var \Drupal\display_builder\DisplayBuildableInterface $buildable */ |
| 228 | $buildable = $manager->createInstance('view_display', ['extender' => $this]); |
| 229 | |
| 230 | return $buildable; |
| 231 | } |
| 232 | |
| 233 | } |
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.
| 67 | public function buildOptionsForm(&$form, FormStateInterface $form_state): void { |
| 68 | if ($form_state->get('section') !== 'display_builder') { |
| 69 | return; |
| 67 | public function buildOptionsForm(&$form, FormStateInterface $form_state): void { |
| 68 | if ($form_state->get('section') !== 'display_builder') { |
| 72 | $form['#title'] .= $this->t('Display Builder'); |
| 73 | $form[DisplayBuildableInterface::PROFILE_PROPERTY] = $this->displayBuildable()->buildInstanceForm(FALSE); |
| 74 | } |
| 155 | $theme_registry = $this->themeRegistry->get(); |
| 156 | // Identical to views_view with a specific path. |
| 157 | $entry = $theme_registry['views_view']; |
| 158 | $entry['path'] = $this->modules->getPath('display_builder_views') . '/templates'; |
| 159 | |
| 160 | return $entry; |
| 161 | } |
| 55 | public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition): static { |
| 56 | $instance = parent::create($container, $configuration, $plugin_id, $plugin_definition); |
| 57 | $instance->entityTypeManager = $container->get('entity_type.manager'); |
| 58 | $instance->themeRegistry = $container->get('theme.registry'); |
| 59 | $instance->modules = $container->get('extension.list.module'); |
| 60 | |
| 61 | return $instance; |
| 62 | } |
| 226 | $manager = \Drupal::service('plugin.manager.display_buildable'); |
| 227 | /** @var \Drupal\display_builder\DisplayBuildableInterface $buildable */ |
| 228 | $buildable = $manager->createInstance('view_display', ['extender' => $this]); |
| 229 | |
| 230 | return $buildable; |
| 231 | } |
| 170 | if (!$this->displayBuildable()->getInstanceId()) { |
| 171 | return NULL; |
| 170 | if (!$this->displayBuildable()->getInstanceId()) { |
| 174 | if (!isset($this->instance)) { |
| 175 | $instance_id = $this->displayBuildable()->getInstanceId(); |
| 176 | /** @var \Drupal\display_builder\InstanceInterface|null $instance */ |
| 177 | $instance = $this->entityTypeManager->getStorage('display_builder_instance')->load($instance_id); |
| 178 | $this->instance = $instance; |
| 179 | } |
| 180 | |
| 181 | return $this->instance; |
| 181 | return $this->instance; |
| 182 | } |
| 170 | if (!$this->displayBuildable()->getInstanceId()) { |
| 174 | if (!isset($this->instance)) { |
| 181 | return $this->instance; |
| 182 | } |
| 191 | $display = $this->view->getDisplay(); |
| 192 | $display_definition = $display->getPluginDefinition(); |
| 193 | |
| 194 | if (!isset($display_definition['class'])) { |
| 195 | return FALSE; |
| 191 | $display = $this->view->getDisplay(); |
| 192 | $display_definition = $display->getPluginDefinition(); |
| 193 | |
| 194 | if (!isset($display_definition['class'])) { |
| 200 | if ($display_definition['class'] === 'Drupal\views\Plugin\views\display\Feed') { |
| 201 | return FALSE; |
| 191 | $display = $this->view->getDisplay(); |
| 192 | $display_definition = $display->getPluginDefinition(); |
| 193 | |
| 194 | if (!isset($display_definition['class'])) { |
| 200 | if ($display_definition['class'] === 'Drupal\views\Plugin\views\display\Feed') { |
| 204 | if ($display_definition['class'] === 'Drupal\views\Plugin\views\display\EntityReference') { |
| 205 | return FALSE; |
| 191 | $display = $this->view->getDisplay(); |
| 192 | $display_definition = $display->getPluginDefinition(); |
| 193 | |
| 194 | if (!isset($display_definition['class'])) { |
| 200 | if ($display_definition['class'] === 'Drupal\views\Plugin\views\display\Feed') { |
| 204 | if ($display_definition['class'] === 'Drupal\views\Plugin\views\display\EntityReference') { |
| 215 | return TRUE; |
| 216 | } |
| 114 | public function optionsSummary(&$categories, &$options): void { |
| 115 | $buildable = $this->displayBuildable(); |
| 116 | |
| 117 | if (!$this->isApplicable()) { |
| 118 | return; |
| 114 | public function optionsSummary(&$categories, &$options): void { |
| 115 | $buildable = $this->displayBuildable(); |
| 116 | |
| 117 | if (!$this->isApplicable()) { |
| 122 | 'category' => 'other', |
| 123 | 'title' => $this->t('Display Builder'), |
| 124 | 'desc' => $this->t('Use display builder as output for this view.'), |
| 125 | 'value' => $buildable->getProfile()?->label() ?? $this->t('Disabled'), |
| 126 | ]; |
| 127 | } |
| 133 | $buildable = $this->displayBuildable(); |
| 134 | |
| 135 | if (!$buildable->getProfile()) { |
| 136 | return; |
| 133 | $buildable = $this->displayBuildable(); |
| 134 | |
| 135 | if (!$buildable->getProfile()) { |
| 141 | $view = $this->view; |
| 142 | // Theme hook suggestion of the current view display. |
| 143 | $suggestion = \implode('__', ['views_view', $view->id(), $view->getDisplay()->getPluginId()]); |
| 144 | $entry = $this->buildThemeRegistryEntry(); |
| 145 | $this->themeRegistry->getRuntime()->set($suggestion, $entry); |
| 146 | } |
| 79 | public function submitOptionsForm(&$form, FormStateInterface $form_state): void { |
| 80 | if ($form_state->get('section') !== 'display_builder') { |
| 81 | return; |
| 79 | public function submitOptionsForm(&$form, FormStateInterface $form_state): void { |
| 80 | if ($form_state->get('section') !== 'display_builder') { |
| 85 | $profile_id = $form_state->getValue(DisplayBuildableInterface::PROFILE_PROPERTY, 'default'); |
| 86 | $this->options[DisplayBuildableInterface::PROFILE_PROPERTY] = $profile_id; |
| 87 | $buildable = $this->displayBuildable(); |
| 88 | |
| 89 | if (empty($profile_id)) { |
| 94 | $storage = $this->entityTypeManager->getStorage('display_builder_instance'); |
| 95 | $storage->delete([$this->getInstance()]); |
| 96 | |
| 97 | return; |
| 79 | public function submitOptionsForm(&$form, FormStateInterface $form_state): void { |
| 80 | if ($form_state->get('section') !== 'display_builder') { |
| 85 | $profile_id = $form_state->getValue(DisplayBuildableInterface::PROFILE_PROPERTY, 'default'); |
| 86 | $this->options[DisplayBuildableInterface::PROFILE_PROPERTY] = $profile_id; |
| 87 | $buildable = $this->displayBuildable(); |
| 88 | |
| 89 | if (empty($profile_id)) { |
| 100 | $buildable->initInstanceIfMissing(); |
| 101 | |
| 102 | // Save the profile in the instance if changed. |
| 103 | $instance = $this->getInstance(); |
| 104 | |
| 105 | if ($instance && $buildable->getProfile()->id() !== $profile_id) { |
| 105 | if ($instance && $buildable->getProfile()->id() !== $profile_id) { |
| 105 | if ($instance && $buildable->getProfile()->id() !== $profile_id) { |
| 106 | $instance->setProfile($profile_id); |
| 107 | $instance->save(); |
| 108 | } |
| 109 | } |
| 109 | } |
| 79 | public function submitOptionsForm(&$form, FormStateInterface $form_state): void { |
| 80 | if ($form_state->get('section') !== 'display_builder') { |
| 85 | $profile_id = $form_state->getValue(DisplayBuildableInterface::PROFILE_PROPERTY, 'default'); |
| 86 | $this->options[DisplayBuildableInterface::PROFILE_PROPERTY] = $profile_id; |
| 87 | $buildable = $this->displayBuildable(); |
| 88 | |
| 89 | if (empty($profile_id)) { |
| 100 | $buildable->initInstanceIfMissing(); |
| 101 | |
| 102 | // Save the profile in the instance if changed. |
| 103 | $instance = $this->getInstance(); |
| 104 | |
| 105 | if ($instance && $buildable->getProfile()->id() !== $profile_id) { |
| 105 | if ($instance && $buildable->getProfile()->id() !== $profile_id) { |
| 105 | if ($instance && $buildable->getProfile()->id() !== $profile_id) { |
| 109 | } |
| 79 | public function submitOptionsForm(&$form, FormStateInterface $form_state): void { |
| 80 | if ($form_state->get('section') !== 'display_builder') { |
| 85 | $profile_id = $form_state->getValue(DisplayBuildableInterface::PROFILE_PROPERTY, 'default'); |
| 86 | $this->options[DisplayBuildableInterface::PROFILE_PROPERTY] = $profile_id; |
| 87 | $buildable = $this->displayBuildable(); |
| 88 | |
| 89 | if (empty($profile_id)) { |
| 100 | $buildable->initInstanceIfMissing(); |
| 101 | |
| 102 | // Save the profile in the instance if changed. |
| 103 | $instance = $this->getInstance(); |
| 104 | |
| 105 | if ($instance && $buildable->getProfile()->id() !== $profile_id) { |
| 105 | if ($instance && $buildable->getProfile()->id() !== $profile_id) { |
| 106 | $instance->setProfile($profile_id); |
| 107 | $instance->save(); |
| 108 | } |
| 109 | } |
| 109 | } |
| 79 | public function submitOptionsForm(&$form, FormStateInterface $form_state): void { |
| 80 | if ($form_state->get('section') !== 'display_builder') { |
| 85 | $profile_id = $form_state->getValue(DisplayBuildableInterface::PROFILE_PROPERTY, 'default'); |
| 86 | $this->options[DisplayBuildableInterface::PROFILE_PROPERTY] = $profile_id; |
| 87 | $buildable = $this->displayBuildable(); |
| 88 | |
| 89 | if (empty($profile_id)) { |
| 100 | $buildable->initInstanceIfMissing(); |
| 101 | |
| 102 | // Save the profile in the instance if changed. |
| 103 | $instance = $this->getInstance(); |
| 104 | |
| 105 | if ($instance && $buildable->getProfile()->id() !== $profile_id) { |
| 105 | if ($instance && $buildable->getProfile()->id() !== $profile_id) { |
| 109 | } |