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