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