Code Coverage
 
Lines
Branches
Paths
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 92
n/a
0 / 0
n/a
0 / 0
0.00% covered (danger)
0.00%
0 / 8
CRAP
0.00% covered (danger)
0.00%
0 / 1
PageLayoutForm
0.00% covered (danger)
0.00%
0 / 92
n/a
0 / 0
n/a
0 / 0
0.00% covered (danger)
0.00%
0 / 8
506
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 1
n/a
0 / 0
n/a
0 / 0
0.00% covered (danger)
0.00%
0 / 1
2
 form
0.00% covered (danger)
0.00%
0 / 28
n/a
0 / 0
n/a
0 / 0
0.00% covered (danger)
0.00%
0 / 1
2
 save
0.00% covered (danger)
0.00%
0 / 10
n/a
0 / 0
n/a
0 / 0
0.00% covered (danger)
0.00%
0 / 1
12
 submitForm
0.00% covered (danger)
0.00%
0 / 2
n/a
0 / 0
n/a
0 / 0
0.00% covered (danger)
0.00%
0 / 1
2
 actionsElement
0.00% covered (danger)
0.00%
0 / 6
n/a
0 / 0
n/a
0 / 0
0.00% covered (danger)
0.00%
0 / 1
12
 buildConditionsForm
0.00% covered (danger)
0.00%
0 / 27
n/a
0 / 0
n/a
0 / 0
0.00% covered (danger)
0.00%
0 / 1
72
 alterConditionsForm
0.00% covered (danger)
0.00%
0 / 12
n/a
0 / 0
n/a
0 / 0
0.00% covered (danger)
0.00%
0 / 1
12
 submitConditions
0.00% covered (danger)
0.00%
0 / 6
n/a
0 / 0
n/a
0 / 0
0.00% covered (danger)
0.00%
0 / 1
6
1<?php
2
3declare(strict_types=1);
4
5namespace Drupal\display_builder_page_layout\Form;
6
7use Drupal\Core\DependencyInjection\AutowireTrait;
8use Drupal\Core\Entity\EntityForm;
9use Drupal\Core\Executable\ExecutableManagerInterface;
10use Drupal\Core\Form\FormStateInterface;
11use Drupal\Core\Form\SubformState;
12use Drupal\Core\Language\LanguageManagerInterface;
13use Drupal\Core\Plugin\Context\ContextRepositoryInterface;
14use Drupal\display_builder\DisplayBuildablePluginManager;
15use Drupal\display_builder_page_layout\Entity\PageLayout;
16use Symfony\Component\DependencyInjection\Attribute\Autowire;
17
18/**
19 * Page layout form.
20 */
21final class PageLayoutForm extends EntityForm {
22
23  use AutowireTrait;
24
25  public function __construct(
26    private readonly ContextRepositoryInterface $contextRepository,
27    #[Autowire(service: 'plugin.manager.condition')]
28    private readonly ExecutableManagerInterface $conditionManager,
29    private readonly LanguageManagerInterface $languageManager,
30    #[Autowire(service: 'plugin.manager.display_buildable')]
31    private readonly DisplayBuildablePluginManager $displayBuildableManager,
32  ) {}
33
34  /**
35   * {@inheritdoc}
36   */
37  public function form(array $form, FormStateInterface $form_state): array {
38    $form = parent::form($form, $form_state);
39
40    /** @var \Drupal\display_builder_page_layout\PageLayoutInterface $entity */
41    $entity = $this->entity;
42
43    // Store the gathered contexts in the form state for other objects to use
44    // during form building.
45    $form_state->setTemporaryValue('gathered_contexts', $this->contextRepository->getAvailableContexts());
46
47    // Because of $form['conditions'].
48    $form['#tree'] = TRUE;
49
50    $form['label'] = [
51      '#type' => 'textfield',
52      '#title' => $this->t('Label'),
53      '#maxlength' => 255,
54      '#default_value' => $entity->label(),
55      '#required' => TRUE,
56    ];
57
58    $form['id'] = [
59      '#type' => 'machine_name',
60      '#default_value' => $entity->id(),
61      '#machine_name' => [
62        'exists' => [PageLayout::class, 'load'],
63      ],
64      '#disabled' => !$entity->isNew(),
65    ];
66
67    /** @var \Drupal\display_builder\DisplayBuildableInterface $buildable */
68    $buildable = $this->displayBuildableManager->createInstance('page_layout', ['entity' => $entity]);
69    $form = \array_merge($form, $buildable->buildInstanceForm());
70
71    $form['conditions'] = $this->buildConditionsForm([], $form_state);
72    $form['status'] = [
73      '#type' => 'checkbox',
74      '#title' => $this->t('Enabled'),
75      '#default_value' => $entity->status(),
76    ];
77
78    return $form;
79  }
80
81  /**
82   * {@inheritdoc}
83   */
84  public function save(array $form, FormStateInterface $form_state): int {
85    $result = parent::save($form, $form_state);
86    $message_args = ['%label' => $this->entity->label()];
87    $this->messenger()->addStatus(
88      match ($result) {
89        SAVED_NEW => $this->t('Created new page layout %label.', $message_args),
90        default => $this->t('Updated page layout %label.', $message_args),
91      }
92    );
93    $form_state->setRedirectUrl($this->entity->toUrl('collection'));
94
95    return $result;
96  }
97
98  /**
99   * {@inheritdoc}
100   */
101  public function submitForm(array &$form, FormStateInterface $form_state): void {
102    parent::submitForm($form, $form_state);
103    $this->submitConditions($form, $form_state);
104  }
105
106  /**
107   * {@inheritdoc}
108   */
109  protected function actionsElement(array $form, FormStateInterface $form_state): array {
110    $form = parent::actionsElement($form, $form_state);
111    /** @var \Drupal\display_builder_page_layout\PageLayoutInterface $page_layout */
112    $page_layout = $this->entity;
113
114    /** @var \Drupal\display_builder\DisplayBuildableInterface $buildable */
115    $buildable = $this->displayBuildableManager->createInstance('page_layout', ['entity' => $page_layout]);
116
117    if ($page_layout->isNew() && !$buildable->isAllowed()) {
118      $form['submit']['#disabled'] = TRUE;
119    }
120
121    return $form;
122  }
123
124  /**
125   * Helper function for building the conditions UI form.
126   *
127   * @param array $form
128   *   An associative array containing the structure of the form.
129   * @param \Drupal\Core\Form\FormStateInterface $form_state
130   *   The current state of the form.
131   *
132   * @return array
133   *   The form array with the conditions UI added in.
134   */
135  private function buildConditionsForm(array $form, FormStateInterface $form_state) {
136    $form['visibility_tabs'] = [
137      '#type' => 'vertical_tabs',
138      '#title' => $this->t('Conditions'),
139      '#parents' => ['visibility_tabs'],
140    ];
141
142    /** @var \Drupal\display_builder_page_layout\PageLayoutInterface $entity */
143    $entity = $this->entity;
144    // @todo \PluginNotFoundException:
145    $conditions = $entity->getConditions()->getConfiguration();
146
147    // Important to filter with contexts to have ContextAware working.
148    $definitions = $this->conditionManager->getFilteredDefinitions('page_layout', $form_state->getTemporaryValue('gathered_contexts'), ['page_layout' => $entity]);
149
150    foreach ($definitions as $condition_id => $definition) {
151      // Don't display the current theme condition.
152      if ($condition_id === 'current_theme') {
153        continue;
154      }
155
156      // Don't display the language condition until we have multiple languages.
157      if ($condition_id === 'language' && !$this->languageManager->isMultilingual()) {
158        continue;
159      }
160
161      if (\str_starts_with($condition_id, 'entity_bundle:')) {
162        $entity_type_id = \str_replace('entity_bundle:', '', $condition_id);
163        $entity_type = $this->entityTypeManager->getDefinition($entity_type_id);
164        $url = $entity_type->getLinkTemplate('canonical');
165
166        if (!$url || \str_starts_with((string) $url, '/admin')) {
167          continue;
168        }
169      }
170
171      /** @var \Drupal\Core\Condition\ConditionInterface $condition */
172      $condition = $this->conditionManager->createInstance($condition_id, $conditions[$condition_id] ?? []);
173      $form_state->set(['conditions', $condition_id], $condition);
174      $condition_form = $condition->buildConfigurationForm([], $form_state);
175      $condition_form['#type'] = 'details';
176      $condition_form['#title'] = $definition['label'];
177      $condition_form['#group'] = 'visibility_tabs';
178      $form[$condition_id] = $condition_form;
179    }
180
181    return $this->alterConditionsForm($form);
182  }
183
184  /**
185   * Alter conditions form.
186   *
187   * @param array $form
188   *   An associative array containing the structure of the form.
189   */
190  private function alterConditionsForm(array $form): array {
191    if (isset($form['user_role'])) {
192      $form['user_role']['#title'] = $this->t('User roles');
193    }
194
195    if (isset($form['request_path'])) {
196      $form['request_path']['#title'] = $this->t('Pages');
197      $form['request_path']['negate']['#type'] = 'radios';
198      $form['request_path']['negate']['#default_value'] = (int) $form['request_path']['negate']['#default_value'];
199      $form['request_path']['negate']['#title_display'] = 'invisible';
200      $form['request_path']['negate']['#options'] = [
201        $this->t('Activate on the listed pages'),
202        $this->t('Skip for the listed pages'),
203      ];
204    }
205
206    return $form;
207  }
208
209  /**
210   * Helper function to independently submit the conditions UI.
211   *
212   * @param array $form
213   *   A nested array form elements comprising the form.
214   * @param \Drupal\Core\Form\FormStateInterface $form_state
215   *   The current state of the form.
216   */
217  private function submitConditions(array $form, FormStateInterface $form_state): void {
218    foreach (\array_keys($form_state->getValue('conditions')) as $condition_id) {
219      // Allow the condition to submit the form.
220      $condition = $form_state->get(['conditions', $condition_id]);
221      $condition->submitConfigurationForm($form['conditions'][$condition_id], SubformState::createForSubform($form['conditions'][$condition_id], $form, $form_state));
222
223      $condition_configuration = $condition->getConfiguration();
224      // Update the visibility conditions on the block.
225      /** @var \Drupal\display_builder_page_layout\PageLayoutInterface $entity */
226      $entity = $this->entity;
227      $entity->getConditions()->addInstanceId((string) $condition_id, $condition_configuration);
228    }
229  }
230
231}

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.