Code Coverage
 
Lines
Branches
Paths
Functions and Methods
Classes and Traits
Total
61.47% covered (warning)
61.47%
67 / 109
59.68% covered (warning)
59.68%
37 / 62
40.48% covered (danger)
40.48%
17 / 42
36.36% covered (danger)
36.36%
8 / 22
CRAP
0.00% covered (danger)
0.00%
0 / 1
DisplayExtender
61.47% covered (warning)
61.47%
67 / 109
59.68% covered (warning)
59.68%
37 / 62
40.48% covered (danger)
40.48%
17 / 42
36.36% covered (danger)
36.36%
8 / 22
414.02
0.00% covered (danger)
0.00%
0 / 1
 create
100.00% covered (success)
100.00%
6 / 6
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getPrefix
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 buildOptionsForm
75.00% covered (warning)
75.00%
3 / 4
66.67% covered (warning)
66.67%
2 / 3
50.00% covered (danger)
50.00%
1 / 2
0.00% covered (danger)
0.00%
0 / 1
2.50
 submitOptionsForm
53.85% covered (warning)
53.85%
7 / 13
62.50% covered (warning)
62.50%
5 / 8
20.00% covered (danger)
20.00%
1 / 5
0.00% covered (danger)
0.00%
0 / 1
17.80
 optionsSummary
87.50% covered (warning)
87.50%
7 / 8
66.67% covered (warning)
66.67%
2 / 3
50.00% covered (danger)
50.00%
1 / 2
0.00% covered (danger)
0.00%
0 / 1
2.50
 preExecute
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
6
 getContextRequirement
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getBuilderUrl
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 checkInstanceId
85.71% covered (warning)
85.71%
6 / 7
66.67% covered (warning)
66.67%
2 / 3
50.00% covered (danger)
50.00%
1 / 2
0.00% covered (danger)
0.00%
0 / 1
2.50
 getUrlFromInstanceId
75.00% covered (warning)
75.00%
3 / 4
66.67% covered (warning)
66.67%
2 / 3
50.00% covered (danger)
50.00%
1 / 2
0.00% covered (danger)
0.00%
0 / 1
2.50
 getDisplayUrlFromInstanceId
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
6
 getProfile
87.50% covered (warning)
87.50%
7 / 8
80.00% covered (warning)
80.00%
4 / 5
66.67% covered (warning)
66.67%
2 / 3
0.00% covered (danger)
0.00%
0 / 1
3.33
 getInstanceId
66.67% covered (warning)
66.67%
2 / 3
66.67% covered (warning)
66.67%
2 / 3
50.00% covered (danger)
50.00%
1 / 2
0.00% covered (danger)
0.00%
0 / 1
2.50
 checkAccess
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
6
 initInstanceIfMissing
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
3 / 3
50.00% covered (danger)
50.00%
1 / 2
100.00% covered (success)
100.00%
1 / 1
2.50
 getInitialSources
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
3 / 3
50.00% covered (danger)
50.00%
1 / 2
100.00% covered (success)
100.00%
1 / 1
2.50
 getInitialContext
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getSources
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 saveSources
0.00% covered (danger)
0.00%
0 / 7
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 buildThemeRegistryEntry
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 isApplicable
66.67% covered (warning)
66.67%
6 / 9
57.14% covered (warning)
57.14%
4 / 7
25.00% covered (danger)
25.00%
1 / 4
0.00% covered (danger)
0.00%
0 / 1
10.75
 getInstance
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
3 / 3
50.00% covered (danger)
50.00%
1 / 2
100.00% covered (success)
100.00%
1 / 1
2.50
1<?php
2
3declare(strict_types=1);
4
5namespace Drupal\display_builder_views\Plugin\views\display_extender;
6
7use Drupal\Core\Access\AccessResult;
8use Drupal\Core\Access\AccessResultInterface;
9use Drupal\Core\Extension\ModuleExtensionList;
10use Drupal\Core\Form\FormStateInterface;
11use Drupal\Core\Plugin\Context\EntityContext;
12use Drupal\Core\Session\AccountInterface;
13use Drupal\Core\StringTranslation\TranslatableMarkup;
14use Drupal\Core\Theme\Registry;
15use Drupal\Core\Url;
16use Drupal\display_builder\ConfigFormBuilderInterface;
17use Drupal\display_builder\DisplayBuildableInterface;
18use Drupal\display_builder\DisplayBuilderHelpers;
19use Drupal\display_builder\InstanceInterface;
20use Drupal\display_builder\ProfileInterface;
21use Drupal\ui_patterns\Plugin\Context\RequirementsContext;
22use Drupal\views\Attribute\ViewsDisplayExtender;
23use Drupal\views\Plugin\views\display_extender\DisplayExtenderPluginBase;
24use Symfony\Component\DependencyInjection\ContainerInterface;
25
26/**
27 * Styles display extender plugin.
28 *
29 * @ingroup views_display_extender_plugins
30 */
31#[ViewsDisplayExtender(
32  id: 'display_builder',
33  title: new TranslatableMarkup('Display Builder'),
34  help: new TranslatableMarkup('Use display builder as output for this view.'),
35  no_ui: FALSE,
36)]
37final class DisplayExtender extends DisplayExtenderPluginBase implements DisplayBuildableInterface {
38
39  /**
40   * The config form builder for Display Builder.
41   *
42   * @var \Drupal\display_builder\ConfigFormBuilderInterface
43   */
44  protected $configFormBuilder;
45
46  /**
47   * The entity type interface.
48   *
49   * @var \Drupal\Core\Entity\EntityTypeManagerInterface
50   */
51  protected $entityTypeManager;
52
53  /**
54   * The theme registry.
55   */
56  protected Registry $themeRegistry;
57
58  /**
59   * The list of modules.
60   */
61  protected ModuleExtensionList $modules;
62
63  /**
64   * The loaded display builder instance.
65   */
66  protected ?InstanceInterface $instance;
67
68  /**
69   * {@inheritdoc}
70   */
71  public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition): self {
72    $instance = parent::create($container, $configuration, $plugin_id, $plugin_definition);
73    $instance->configFormBuilder = $container->get('display_builder.config_form_builder');
74    $instance->entityTypeManager = $container->get('entity_type.manager');
75    $instance->themeRegistry = $container->get('theme.registry');
76    $instance->modules = $container->get('extension.list.module');
77
78    return $instance;
79  }
80
81  /**
82   * {@inheritdoc}
83   */
84  public static function getPrefix(): string {
85    return 'views__';
86  }
87
88  /**
89   * {@inheritdoc}
90   */
91  public function buildOptionsForm(&$form, FormStateInterface $form_state): void {
92    if ($form_state->get('section') !== 'display_builder') {
93      return;
94    }
95
96    $form['#title'] .= $this->t('Display Builder');
97    $form[ConfigFormBuilderInterface::PROFILE_PROPERTY] = $this->configFormBuilder->build($this, FALSE);
98  }
99
100  /**
101   * {@inheritdoc}
102   */
103  public function submitOptionsForm(&$form, FormStateInterface $form_state): void {
104    if ($form_state->get('section') !== 'display_builder') {
105      return;
106    }
107
108    // @todo we should have always a fallback.
109    $profile_id = $form_state->getValue(ConfigFormBuilderInterface::PROFILE_PROPERTY, 'default');
110    $this->options[ConfigFormBuilderInterface::PROFILE_PROPERTY] = $profile_id;
111
112    if (empty($profile_id)) {
113      // If no Display Builder selected, we delete the related instance.
114      // @todo Do we move that to the View's EntityInterface::delete() method?
115      // @todo Also, when the changed are canceled from UI leaving the View
116      // without Display Builder.
117      $storage = $this->entityTypeManager->getStorage('display_builder_instance');
118      $storage->delete([$this->getInstance()]);
119
120      return;
121    }
122
123    $this->initInstanceIfMissing();
124
125    // Save the profile in the instance if changed.
126    $instance = $this->getInstance();
127
128    if ($instance && $instance->getProfile()->id() !== $profile_id) {
129      $instance->setProfile($profile_id);
130      $instance->save();
131    }
132  }
133
134  /**
135   * {@inheritdoc}
136   */
137  public function optionsSummary(&$categories, &$options): void {
138    if (!$this->isApplicable()) {
139      return;
140    }
141
142    $options['display_builder'] = [
143      'category' => 'other',
144      'title' => $this->t('Display Builder'),
145      'desc' => $this->t('Use display builder as output for this view.'),
146      'value' => $this->getProfile()?->label() ?? $this->t('Disabled'),
147    ];
148  }
149
150  /**
151   * {@inheritdoc}
152   */
153  public function preExecute(): void {
154    if (!$this->getProfile()) {
155      return;
156    }
157    // We alter the registry here instead of implementing
158    // hook_theme_registry_alter in order keep the alteration specific to each
159    // view.
160    $view = $this->view;
161    // Theme hook suggestion of the current view display.
162    $suggestion = \implode('__', ['views_view', $view->id(), $view->getDisplay()->getPluginId()]);
163    $entry = $this->buildThemeRegistryEntry();
164    $this->themeRegistry->getRuntime()->set($suggestion, $entry);
165  }
166
167  /**
168   * {@inheritdoc}
169   */
170  public static function getContextRequirement(): string {
171    // @see \Drupal\ui_patterns_views\Plugin\UiPatterns\Source\ViewRowsSource.
172    return 'views:style';
173  }
174
175  /**
176   * {@inheritdoc}
177   */
178  public function getBuilderUrl(): Url {
179    $params = [
180      'view' => $this->view->id(),
181      'display' => $this->view->current_display,
182    ];
183
184    return Url::fromRoute('display_builder_views.views.manage', $params);
185  }
186
187  /**
188   * {@inheritdoc}
189   */
190  public static function checkInstanceId(string $instance_id): ?array {
191    if (!\str_starts_with($instance_id, DisplayExtender::getPrefix())) {
192      return NULL;
193    }
194    [, $view, $display] = \explode('__', $instance_id);
195
196    return [
197      'view' => $view,
198      'display' => $display,
199    ];
200  }
201
202  /**
203   * {@inheritdoc}
204   */
205  public static function getUrlFromInstanceId(string $instance_id): Url {
206    $params = self::checkInstanceId($instance_id);
207
208    if (!$params) {
209      // Fallback to the list of instances.
210      return Url::fromRoute('entity.display_builder_instance.collection');
211    }
212
213    return Url::fromRoute('display_builder_views.views.manage', $params);
214  }
215
216  /**
217   * {@inheritdoc}
218   */
219  public static function getDisplayUrlFromInstanceId(string $instance_id): Url {
220    $params = self::checkInstanceId($instance_id);
221
222    if (!$params) {
223      // Fallback to the list of instances.
224      return Url::fromRoute('entity.display_builder_instance.collection');
225    }
226
227    return Url::fromRoute('entity.view.edit_form', $params);
228  }
229
230  /**
231   * {@inheritdoc}
232   */
233  public function getProfile(): ?ProfileInterface {
234    if (!isset($this->options[ConfigFormBuilderInterface::PROFILE_PROPERTY])) {
235      return NULL;
236    }
237    $display_builder_id = $this->options[ConfigFormBuilderInterface::PROFILE_PROPERTY];
238
239    if (empty($display_builder_id)) {
240      return NULL;
241    }
242    $storage = $this->entityTypeManager->getStorage('display_builder_profile');
243
244    /** @var \Drupal\display_builder\ProfileInterface $display_builder */
245    $display_builder = $storage->load($display_builder_id);
246
247    return $display_builder;
248  }
249
250  /**
251   * {@inheritdoc}
252   */
253  public function getInstanceId(): ?string {
254    if (!$this->view) {
255      return NULL;
256    }
257
258    return \sprintf('%s%s__%s', self::getPrefix(), $this->view->id(), $this->view->current_display);
259  }
260
261  /**
262   * {@inheritdoc}
263   */
264  public static function checkAccess(string $instance_id, AccountInterface $account): AccessResultInterface {
265    return $account->hasPermission('administer views') ? AccessResult::allowed() : AccessResult::forbidden();
266  }
267
268  /**
269   * {@inheritdoc}
270   */
271  public function initInstanceIfMissing(): void {
272    /** @var \Drupal\display_builder\InstanceStorage $storage */
273    $storage = $this->entityTypeManager->getStorage('display_builder_instance');
274
275    /** @var \Drupal\display_builder\InstanceInterface $instance */
276    $instance = $storage->load($this->getInstanceId());
277
278    if (!$instance) {
279      $instance = $storage->createFromImplementation($this);
280      $instance->save();
281    }
282  }
283
284  /**
285   * {@inheritdoc}
286   */
287  public function getInitialSources(): array {
288    // Get the sources stored in config.
289    $sources = $this->getSources();
290
291    if (empty($sources)) {
292      // Fallback to a fixture mimicking the standard view layout.
293      $sources = DisplayBuilderHelpers::getFixtureDataFromExtension('display_builder_views', 'default_view');
294    }
295
296    return $sources;
297  }
298
299  /**
300   * {@inheritdoc}
301   */
302  public function getInitialContext(): array {
303    $contexts = [];
304    // Mark for usage with views.
305    $contexts = RequirementsContext::addToContext([self::getContextRequirement()], $contexts);
306    // Add view entity that we need in our sources or even UI Patterns Views
307    // sources.
308    $contexts['ui_patterns_views:view_entity'] = EntityContext::fromEntity($this->view->storage);
309
310    return $contexts;
311  }
312
313  /**
314   * {@inheritdoc}
315   */
316  public function getSources(): array {
317    return $this->options[ConfigFormBuilderInterface::SOURCES_PROPERTY] ?? [];
318  }
319
320  /**
321   * {@inheritdoc}
322   */
323  public function saveSources(): void {
324    $sources = $this->getInstance()->getCurrentState();
325    $displays = $this->view->storage->get('display');
326    $display_id = $this->view->current_display;
327    // It is risky to alter a View like that. We need to be careful to not
328    // break the storage integrity, but we didn't find a better way.
329    $displays[$display_id]['display_options']['display_extenders']['display_builder']['sources'] = $sources;
330    $this->view->storage->set('display', $displays);
331    $this->view->storage->save();
332    // @todo Test if we still need to invalidate the cache manually here.
333    $this->view->storage->invalidateCaches();
334  }
335
336  /**
337   * Build theme registry entry.
338   *
339   * @return array
340   *   A theme registry entry.
341   */
342  protected function buildThemeRegistryEntry(): array {
343    $theme_registry = $this->themeRegistry->get();
344    // Identical to views_view with a specific path.
345    $entry = $theme_registry['views_view'];
346    $entry['path'] = $this->modules->getPath('display_builder_views') . '/templates';
347
348    return $entry;
349  }
350
351  /**
352   * If display builder can be applied to this display.
353   *
354   * @return bool
355   *   Applicable or not.
356   */
357  private function isApplicable(): bool {
358    $display = $this->view->getDisplay();
359    $display_definition = $display->getPluginDefinition();
360
361    if (!isset($display_definition['class'])) {
362      return FALSE;
363    }
364
365    // Do not include with feed and entity reference, as they have no output to
366    // apply a display builder to.
367    if ($display_definition['class'] === 'Drupal\views\Plugin\views\display\Feed') {
368      return FALSE;
369    }
370
371    if ($display_definition['class'] === 'Drupal\views\Plugin\views\display\EntityReference') {
372      return FALSE;
373    }
374
375    // @todo safer to not allow third party display?
376    // phpcs:disable
377    // if (str_contains($display_definition['class'], 'Drupal\views\Plugin\views\display')) {
378    //   return FALSE;
379    // }
380    // phpcs:enable
381
382    return TRUE;
383  }
384
385  /**
386   * Gets the Display Builder instance.
387   *
388   * @return \Drupal\display_builder\InstanceInterface|null
389   *   A display builder instance entity.
390   */
391  private function getInstance(): ?InstanceInterface {
392    if (!isset($this->instance)) {
393      /** @var \Drupal\display_builder\InstanceInterface|null $instance */
394      $instance = $this->entityTypeManager->getStorage('display_builder_instance')->load($this->getInstanceId());
395      $this->instance = $instance;
396    }
397
398    return $this->instance;
399  }
400
401}

Paths

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.

DisplayExtender->buildOptionsForm
91  public function buildOptionsForm(&$form, FormStateInterface $form_state): void {
92    if ($form_state->get('section') !== 'display_builder') {
 
93      return;
91  public function buildOptionsForm(&$form, FormStateInterface $form_state): void {
92    if ($form_state->get('section') !== 'display_builder') {
 
96    $form['#title'] .= $this->t('Display Builder');
97    $form[ConfigFormBuilderInterface::PROFILE_PROPERTY] = $this->configFormBuilder->build($this, FALSE);
98  }
DisplayExtender->buildThemeRegistryEntry
343    $theme_registry = $this->themeRegistry->get();
344    // Identical to views_view with a specific path.
345    $entry = $theme_registry['views_view'];
346    $entry['path'] = $this->modules->getPath('display_builder_views') . '/templates';
347
348    return $entry;
DisplayExtender->checkAccess
264  public static function checkAccess(string $instance_id, AccountInterface $account): AccessResultInterface {
265    return $account->hasPermission('administer views') ? AccessResult::allowed() : AccessResult::forbidden();
 
265    return $account->hasPermission('administer views') ? AccessResult::allowed() : AccessResult::forbidden();
 
265    return $account->hasPermission('administer views') ? AccessResult::allowed() : AccessResult::forbidden();
264  public static function checkAccess(string $instance_id, AccountInterface $account): AccessResultInterface {
265    return $account->hasPermission('administer views') ? AccessResult::allowed() : AccessResult::forbidden();
 
265    return $account->hasPermission('administer views') ? AccessResult::allowed() : AccessResult::forbidden();
 
265    return $account->hasPermission('administer views') ? AccessResult::allowed() : AccessResult::forbidden();
DisplayExtender->checkInstanceId
190  public static function checkInstanceId(string $instance_id): ?array {
191    if (!\str_starts_with($instance_id, DisplayExtender::getPrefix())) {
 
192      return NULL;
190  public static function checkInstanceId(string $instance_id): ?array {
191    if (!\str_starts_with($instance_id, DisplayExtender::getPrefix())) {
 
194    [, $view, $display] = \explode('__', $instance_id);
195
196    return [
197      'view' => $view,
198      'display' => $display,
DisplayExtender->create
71  public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition): self {
72    $instance = parent::create($container, $configuration, $plugin_id, $plugin_definition);
73    $instance->configFormBuilder = $container->get('display_builder.config_form_builder');
74    $instance->entityTypeManager = $container->get('entity_type.manager');
75    $instance->themeRegistry = $container->get('theme.registry');
76    $instance->modules = $container->get('extension.list.module');
77
78    return $instance;
DisplayExtender->getBuilderUrl
180      'view' => $this->view->id(),
181      'display' => $this->view->current_display,
182    ];
183
184    return Url::fromRoute('display_builder_views.views.manage', $params);
DisplayExtender->getContextRequirement
172    return 'views:style';
DisplayExtender->getDisplayUrlFromInstanceId
219  public static function getDisplayUrlFromInstanceId(string $instance_id): Url {
220    $params = self::checkInstanceId($instance_id);
221
222    if (!$params) {
 
224      return Url::fromRoute('entity.display_builder_instance.collection');
219  public static function getDisplayUrlFromInstanceId(string $instance_id): Url {
220    $params = self::checkInstanceId($instance_id);
221
222    if (!$params) {
 
227    return Url::fromRoute('entity.view.edit_form', $params);
DisplayExtender->getInitialContext
303    $contexts = [];
304    // Mark for usage with views.
305    $contexts = RequirementsContext::addToContext([self::getContextRequirement()], $contexts);
306    // Add view entity that we need in our sources or even UI Patterns Views
307    // sources.
308    $contexts['ui_patterns_views:view_entity'] = EntityContext::fromEntity($this->view->storage);
309
310    return $contexts;
DisplayExtender->getInitialSources
289    $sources = $this->getSources();
290
291    if (empty($sources)) {
 
293      $sources = DisplayBuilderHelpers::getFixtureDataFromExtension('display_builder_views', 'default_view');
294    }
295
296    return $sources;
 
296    return $sources;
289    $sources = $this->getSources();
290
291    if (empty($sources)) {
 
296    return $sources;
DisplayExtender->getInstance
392    if (!isset($this->instance)) {
 
394      $instance = $this->entityTypeManager->getStorage('display_builder_instance')->load($this->getInstanceId());
395      $this->instance = $instance;
396    }
397
398    return $this->instance;
 
398    return $this->instance;
392    if (!isset($this->instance)) {
 
398    return $this->instance;
DisplayExtender->getInstanceId
254    if (!$this->view) {
 
255      return NULL;
254    if (!$this->view) {
 
258    return \sprintf('%s%s__%s', self::getPrefix(), $this->view->id(), $this->view->current_display);
DisplayExtender->getPrefix
85    return 'views__';
DisplayExtender->getProfile
234    if (!isset($this->options[ConfigFormBuilderInterface::PROFILE_PROPERTY])) {
 
235      return NULL;
234    if (!isset($this->options[ConfigFormBuilderInterface::PROFILE_PROPERTY])) {
 
237    $display_builder_id = $this->options[ConfigFormBuilderInterface::PROFILE_PROPERTY];
238
239    if (empty($display_builder_id)) {
 
240      return NULL;
234    if (!isset($this->options[ConfigFormBuilderInterface::PROFILE_PROPERTY])) {
 
237    $display_builder_id = $this->options[ConfigFormBuilderInterface::PROFILE_PROPERTY];
238
239    if (empty($display_builder_id)) {
 
242    $storage = $this->entityTypeManager->getStorage('display_builder_profile');
243
244    /** @var \Drupal\display_builder\ProfileInterface $display_builder */
245    $display_builder = $storage->load($display_builder_id);
246
247    return $display_builder;
DisplayExtender->getSources
317    return $this->options[ConfigFormBuilderInterface::SOURCES_PROPERTY] ?? [];
DisplayExtender->getUrlFromInstanceId
205  public static function getUrlFromInstanceId(string $instance_id): Url {
206    $params = self::checkInstanceId($instance_id);
207
208    if (!$params) {
 
210      return Url::fromRoute('entity.display_builder_instance.collection');
205  public static function getUrlFromInstanceId(string $instance_id): Url {
206    $params = self::checkInstanceId($instance_id);
207
208    if (!$params) {
 
213    return Url::fromRoute('display_builder_views.views.manage', $params);
DisplayExtender->initInstanceIfMissing
273    $storage = $this->entityTypeManager->getStorage('display_builder_instance');
274
275    /** @var \Drupal\display_builder\InstanceInterface $instance */
276    $instance = $storage->load($this->getInstanceId());
277
278    if (!$instance) {
 
279      $instance = $storage->createFromImplementation($this);
280      $instance->save();
281    }
282  }
 
282  }
273    $storage = $this->entityTypeManager->getStorage('display_builder_instance');
274
275    /** @var \Drupal\display_builder\InstanceInterface $instance */
276    $instance = $storage->load($this->getInstanceId());
277
278    if (!$instance) {
 
282  }
DisplayExtender->isApplicable
358    $display = $this->view->getDisplay();
359    $display_definition = $display->getPluginDefinition();
360
361    if (!isset($display_definition['class'])) {
 
362      return FALSE;
358    $display = $this->view->getDisplay();
359    $display_definition = $display->getPluginDefinition();
360
361    if (!isset($display_definition['class'])) {
 
367    if ($display_definition['class'] === 'Drupal\views\Plugin\views\display\Feed') {
 
368      return FALSE;
358    $display = $this->view->getDisplay();
359    $display_definition = $display->getPluginDefinition();
360
361    if (!isset($display_definition['class'])) {
 
367    if ($display_definition['class'] === 'Drupal\views\Plugin\views\display\Feed') {
 
371    if ($display_definition['class'] === 'Drupal\views\Plugin\views\display\EntityReference') {
 
372      return FALSE;
358    $display = $this->view->getDisplay();
359    $display_definition = $display->getPluginDefinition();
360
361    if (!isset($display_definition['class'])) {
 
367    if ($display_definition['class'] === 'Drupal\views\Plugin\views\display\Feed') {
 
371    if ($display_definition['class'] === 'Drupal\views\Plugin\views\display\EntityReference') {
 
382    return TRUE;
DisplayExtender->optionsSummary
137  public function optionsSummary(&$categories, &$options): void {
138    if (!$this->isApplicable()) {
 
139      return;
137  public function optionsSummary(&$categories, &$options): void {
138    if (!$this->isApplicable()) {
 
143      'category' => 'other',
144      'title' => $this->t('Display Builder'),
145      'desc' => $this->t('Use display builder as output for this view.'),
146      'value' => $this->getProfile()?->label() ?? $this->t('Disabled'),
147    ];
148  }
DisplayExtender->preExecute
154    if (!$this->getProfile()) {
 
155      return;
154    if (!$this->getProfile()) {
 
160    $view = $this->view;
161    // Theme hook suggestion of the current view display.
162    $suggestion = \implode('__', ['views_view', $view->id(), $view->getDisplay()->getPluginId()]);
163    $entry = $this->buildThemeRegistryEntry();
164    $this->themeRegistry->getRuntime()->set($suggestion, $entry);
165  }
DisplayExtender->saveSources
324    $sources = $this->getInstance()->getCurrentState();
325    $displays = $this->view->storage->get('display');
326    $display_id = $this->view->current_display;
327    // It is risky to alter a View like that. We need to be careful to not
328    // break the storage integrity, but we didn't find a better way.
329    $displays[$display_id]['display_options']['display_extenders']['display_builder']['sources'] = $sources;
330    $this->view->storage->set('display', $displays);
331    $this->view->storage->save();
332    // @todo Test if we still need to invalidate the cache manually here.
333    $this->view->storage->invalidateCaches();
334  }
DisplayExtender->submitOptionsForm
103  public function submitOptionsForm(&$form, FormStateInterface $form_state): void {
104    if ($form_state->get('section') !== 'display_builder') {
 
105      return;
103  public function submitOptionsForm(&$form, FormStateInterface $form_state): void {
104    if ($form_state->get('section') !== 'display_builder') {
 
109    $profile_id = $form_state->getValue(ConfigFormBuilderInterface::PROFILE_PROPERTY, 'default');
110    $this->options[ConfigFormBuilderInterface::PROFILE_PROPERTY] = $profile_id;
111
112    if (empty($profile_id)) {
 
117      $storage = $this->entityTypeManager->getStorage('display_builder_instance');
118      $storage->delete([$this->getInstance()]);
119
120      return;
103  public function submitOptionsForm(&$form, FormStateInterface $form_state): void {
104    if ($form_state->get('section') !== 'display_builder') {
 
109    $profile_id = $form_state->getValue(ConfigFormBuilderInterface::PROFILE_PROPERTY, 'default');
110    $this->options[ConfigFormBuilderInterface::PROFILE_PROPERTY] = $profile_id;
111
112    if (empty($profile_id)) {
 
123    $this->initInstanceIfMissing();
124
125    // Save the profile in the instance if changed.
126    $instance = $this->getInstance();
127
128    if ($instance && $instance->getProfile()->id() !== $profile_id) {
 
128    if ($instance && $instance->getProfile()->id() !== $profile_id) {
 
129      $instance->setProfile($profile_id);
130      $instance->save();
131    }
132  }
 
132  }
103  public function submitOptionsForm(&$form, FormStateInterface $form_state): void {
104    if ($form_state->get('section') !== 'display_builder') {
 
109    $profile_id = $form_state->getValue(ConfigFormBuilderInterface::PROFILE_PROPERTY, 'default');
110    $this->options[ConfigFormBuilderInterface::PROFILE_PROPERTY] = $profile_id;
111
112    if (empty($profile_id)) {
 
123    $this->initInstanceIfMissing();
124
125    // Save the profile in the instance if changed.
126    $instance = $this->getInstance();
127
128    if ($instance && $instance->getProfile()->id() !== $profile_id) {
 
128    if ($instance && $instance->getProfile()->id() !== $profile_id) {
 
132  }
103  public function submitOptionsForm(&$form, FormStateInterface $form_state): void {
104    if ($form_state->get('section') !== 'display_builder') {
 
109    $profile_id = $form_state->getValue(ConfigFormBuilderInterface::PROFILE_PROPERTY, 'default');
110    $this->options[ConfigFormBuilderInterface::PROFILE_PROPERTY] = $profile_id;
111
112    if (empty($profile_id)) {
 
123    $this->initInstanceIfMissing();
124
125    // Save the profile in the instance if changed.
126    $instance = $this->getInstance();
127
128    if ($instance && $instance->getProfile()->id() !== $profile_id) {
 
132  }