Code Coverage
 
Lines
Branches
Paths
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 52
0.00% covered (danger)
0.00%
0 / 36
0.00% covered (danger)
0.00%
0 / 32
0.00% covered (danger)
0.00%
0 / 8
CRAP
0.00% covered (danger)
0.00%
0 / 1
VisibilityConditionsPanel
0.00% covered (danger)
0.00%
0 / 46
0.00% covered (danger)
0.00%
0 / 36
0.00% covered (danger)
0.00%
0 / 32
0.00% covered (danger)
0.00%
0 / 8
380
0.00% covered (danger)
0.00%
0 / 1
 create
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
 buildForm
0.00% covered (danger)
0.00%
0 / 18
0.00% covered (danger)
0.00%
0 / 13
0.00% covered (danger)
0.00%
0 / 8
0.00% covered (danger)
0.00%
0 / 1
42
 validateForm
0.00% covered (danger)
0.00%
0 / 10
0.00% covered (danger)
0.00%
0 / 7
0.00% covered (danger)
0.00%
0 / 9
0.00% covered (danger)
0.00%
0 / 1
12
 alterElement
0.00% covered (danger)
0.00%
0 / 10
0.00% covered (danger)
0.00%
0 / 11
0.00% covered (danger)
0.00%
0 / 10
0.00% covered (danger)
0.00%
0 / 1
30
 onAttachToRoot
0.00% covered (danger)
0.00%
0 / 1
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
 onAttachToSlot
0.00% covered (danger)
0.00%
0 / 1
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
 onActive
0.00% covered (danger)
0.00%
0 / 1
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
 onDelete
0.00% covered (danger)
0.00%
0 / 1
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
1<?php
2
3declare(strict_types=1);
4
5namespace Drupal\display_builder\Plugin\display_builder\Island;
6
7use Drupal\Core\Condition\ConditionPluginCollection;
8use Drupal\Core\Executable\ExecutableManagerInterface;
9use Drupal\Core\Form\FormStateInterface;
10use Drupal\Core\Form\SubformState;
11use Drupal\Core\Plugin\Context\ContextRepositoryInterface;
12use Drupal\Core\StringTranslation\TranslatableMarkup;
13use Drupal\display_builder\Attribute\Island;
14use Drupal\display_builder\InstanceInterface;
15use Drupal\display_builder\Island\IslandPluginBase;
16use Drupal\display_builder\Island\IslandType;
17use Drupal\display_builder\Island\IslandWithFormInterface;
18use Drupal\display_builder\Island\IslandWithFormTrait;
19use Drupal\display_builder\Island\RenderableAltererInterface;
20use Symfony\Component\DependencyInjection\ContainerInterface;
21
22/**
23 * Visibility conditions island plugin implementation.
24 */
25#[Island(
26  id: 'visibility_conditions',
27  label: new TranslatableMarkup('Visibility'),
28  description: new TranslatableMarkup('Set visibility conditions for the active component or block.'),
29  type: IslandType::Contextual,
30  icon: 'eye',
31)]
32class VisibilityConditionsPanel extends IslandPluginBase implements IslandWithFormInterface, RenderableAltererInterface {
33
34  use IslandWithFormTrait;
35
36  /**
37   * The condition manager.
38   */
39  protected ExecutableManagerInterface $conditionManager;
40
41  /**
42   * The context repository.
43   */
44  protected ContextRepositoryInterface $contextRepository;
45
46  /**
47   * {@inheritdoc}
48   */
49  public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition): static {
50    $instance = parent::create($container, $configuration, $plugin_id, $plugin_definition);
51    $instance->conditionManager = $container->get('plugin.manager.condition');
52    $instance->contextRepository = $container->get('context.repository');
53
54    return $instance;
55  }
56
57  /**
58   * {@inheritdoc}
59   */
60  public function buildForm(array &$form, FormStateInterface $form_state): void {
61    $form['#tree'] = TRUE;
62
63    $data = $form_state->getBuildInfo()['args'][0];
64    $instance = $data['instance'] ?? [];
65    $conditions = $this->conditionManager->getDefinitions();
66    unset($conditions['response_status']);
67
68    // Gather all available contexts.
69    $gathered_contexts = $form_state->getTemporaryValue('gathered_contexts') ?? [];
70    $form_state->setTemporaryValue('gathered_contexts', $gathered_contexts + $this->contextRepository->getAvailableContexts());
71
72    foreach ($conditions as $condition_id => $definition) {
73      if ($condition_id === 'current_theme') {
74        continue;
75      }
76
77      if (\str_starts_with($condition_id, 'entity_bundle:')) {
78        continue;
79      }
80
81      /** @var \Drupal\Core\Condition\ConditionInterface $condition */
82      $condition = $this->conditionManager->createInstance($condition_id, $instance[$condition_id] ?? []);
83      $form_state->set(['conditions', $condition_id], $condition);
84      $condition_form = $condition->buildConfigurationForm([], $form_state);
85      $condition_form['#type'] = 'details';
86      $condition_form['#title'] = (\is_array($definition) && isset($definition['label'])) ? $definition['label'] : '';
87      $form[$condition_id] = $condition_form;
88    }
89  }
90
91  /**
92   * {@inheritdoc}
93   */
94  public function validateForm(array &$form, FormStateInterface $form_state): void {
95    // Remove configuration if it matches the defaults.
96    $visibility = new ConditionPluginCollection($this->conditionManager);
97    $conditions = $form_state->get('conditions');
98
99    foreach ($conditions as $condition_id => $condition) {
100      $condition->submitConfigurationForm($form[$condition_id], SubformState::createForSubform($form[$condition_id], $form, $form_state));
101      $visibility->set($condition_id, $condition);
102      $form_state->unsetValue($condition_id);
103    }
104
105    foreach ($visibility->getConfiguration() as $condition_id => $configuration) {
106      $form_state->setValue($condition_id, $configuration);
107    }
108
109    // Those two lines are necessary to prevent the form from being rebuilt.
110    // if rebuilt, the form state values will have both the computed ones
111    // and the raw ones (wrapper key and values).
112    $form_state->setRebuild(FALSE);
113    $form_state->setExecuted();
114  }
115
116  /**
117   * {@inheritdoc}
118   */
119  public function alterElement(array $element, array $data = []): array {
120    $available_contexts = $this->contextRepository->getAvailableContexts();
121
122    foreach (\array_keys($data) as $condition_id) {
123      /** @var \Drupal\Component\Plugin\ContextAwarePluginInterface $condition */
124      $condition = $this->conditionManager->createInstance($condition_id, $data[$condition_id] ?? []);
125
126      // Apply context mapping.
127      $context_mapping = $condition->getContextMapping();
128
129      foreach ($context_mapping as $key => $value) {
130        if (isset($available_contexts[$value])) {
131          $condition->setContextValue($key, $available_contexts[$value]->getContextValue());
132        }
133      }
134
135      /** @var \Drupal\Core\Executable\ExecutableInterface $condition */
136      if (!$this->conditionManager->execute($condition)) {
137        return [];
138      }
139    }
140
141    return $element;
142  }
143
144  /**
145   * {@inheritdoc}
146   *
147   * No-op: a freshly attached node always has a brand-new node_id, so its
148   * own contextual panel can never already be open in the second drawer for
149   * this or any other reload to target.
150   */
151  public function onAttachToRoot(InstanceInterface $instance, string $node_id): array {
152    return [];
153  }
154
155  /**
156   * {@inheritdoc}
157   *
158   * @see self::onAttachToRoot()
159   */
160  public function onAttachToSlot(InstanceInterface $instance, string $node_id, string $parent_id): array {
161    return [];
162  }
163
164  /**
165   * {@inheritdoc}
166   */
167  public function onActive(InstanceInterface $instance, array $data): array {
168    return $this->reloadWithLocalData($instance, $data);
169  }
170
171  /**
172   * {@inheritdoc}
173   */
174  public function onDelete(InstanceInterface $instance, ?string $parent_id): array {
175    return $this->reloadWithLocalData($instance, []);
176  }
177
178}