Code Coverage
 
Lines
Branches
Paths
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 134
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
EntityViewDisplayFormTrait
0.00% covered (danger)
0.00%
0 / 134
n/a
0 / 0
n/a
0 / 0
0.00% covered (danger)
0.00%
0 / 8
870
0.00% covered (danger)
0.00%
0 / 1
 submitForm
0.00% covered (danger)
0.00%
0 / 16
n/a
0 / 0
n/a
0 / 0
0.00% covered (danger)
0.00%
0 / 1
20
 entityViewDisplayForm
0.00% covered (danger)
0.00%
0 / 26
n/a
0 / 0
n/a
0 / 0
0.00% covered (danger)
0.00%
0 / 1
20
 buildOverridesForm
0.00% covered (danger)
0.00%
0 / 55
n/a
0 / 0
n/a
0 / 0
0.00% covered (danger)
0.00%
0 / 1
12
 getAlreadyMappedFields
0.00% covered (danger)
0.00%
0 / 11
n/a
0 / 0
n/a
0 / 0
0.00% covered (danger)
0.00%
0 / 1
30
 getSourceFieldAsOptions
0.00% covered (danger)
0.00%
0 / 14
n/a
0 / 0
n/a
0 / 0
0.00% covered (danger)
0.00%
0 / 1
42
 buildExtraFieldRow
0.00% covered (danger)
0.00%
0 / 3
n/a
0 / 0
n/a
0 / 0
0.00% covered (danger)
0.00%
0 / 1
6
 buildFieldRow
0.00% covered (danger)
0.00%
0 / 3
n/a
0 / 0
n/a
0 / 0
0.00% covered (danger)
0.00%
0 / 1
6
 copyFormValuesToEntity
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
1<?php
2
3declare(strict_types=1);
4
5namespace Drupal\display_builder_entity_view\Form;
6
7use Drupal\Core\Entity\Display\EntityViewDisplayInterface;
8use Drupal\Core\Entity\EntityInterface;
9use Drupal\Core\Field\FieldDefinitionInterface;
10use Drupal\Core\Form\FormStateInterface;
11use Drupal\Core\Url;
12use Drupal\display_builder\ConfigFormBuilderInterface;
13use Drupal\display_builder_entity_view\Entity\DisplayBuilderEntityDisplayInterface;
14use Drupal\display_builder_entity_view\Entity\DisplayBuilderOverridableInterface;
15
16/**
17 * Common methods for entity view display form.
18 */
19trait EntityViewDisplayFormTrait {
20
21  /**
22   * Form submission handler.
23   *
24   * @param array $form
25   *   An associative array containing the structure of the form.
26   * @param \Drupal\Core\Form\FormStateInterface $form_state
27   *   The current state of the form.
28   */
29  public function submitForm(array &$form, FormStateInterface $form_state): void {
30    parent::submitForm($form, $form_state);
31
32    // @todo we should have always a fallback.
33    $display_builder_config = $form_state->getValue([ConfigFormBuilderInterface::PROFILE_PROPERTY]) ?? 'default';
34
35    // Empty means disabled.
36    if (empty($display_builder_config)) {
37      $this->entity->unsetThirdPartySetting('display_builder', ConfigFormBuilderInterface::PROFILE_PROPERTY);
38    }
39    else {
40      $this->entity->setThirdPartySetting('display_builder', ConfigFormBuilderInterface::PROFILE_PROPERTY, $display_builder_config);
41    }
42
43    $display_builder_override = $form_state->getValue([ConfigFormBuilderInterface::OVERRIDE_FIELD_PROPERTY]) ?? '';
44
45    // Empty means disabled.
46    if (empty($display_builder_override)) {
47      $this->entity->unsetThirdPartySetting('display_builder', ConfigFormBuilderInterface::OVERRIDE_FIELD_PROPERTY);
48    }
49    else {
50      $this->entity->setThirdPartySetting('display_builder', ConfigFormBuilderInterface::OVERRIDE_FIELD_PROPERTY, $display_builder_override);
51    }
52
53    $display_builder_override_profile = $form_state->getValue([ConfigFormBuilderInterface::OVERRIDE_PROFILE_PROPERTY]) ?? '';
54
55    // Empty means disabled.
56    if (empty($display_builder_override_profile)) {
57      $this->entity->unsetThirdPartySetting('display_builder', ConfigFormBuilderInterface::OVERRIDE_PROFILE_PROPERTY);
58    }
59    else {
60      $this->entity->setThirdPartySetting('display_builder', ConfigFormBuilderInterface::OVERRIDE_PROFILE_PROPERTY, $display_builder_override_profile);
61    }
62
63    $this->entity->save();
64    $this->localTaskManager->clearCachedDefinitions();
65    $this->routeBuilder->rebuild();
66  }
67
68  /**
69   * Provides form elements to enable Display Builder.
70   *
71   * @param array $form
72   *   The form structure.
73   *
74   * @return array
75   *   The modified form.
76   */
77  protected function entityViewDisplayForm(array $form): array {
78    $is_display_builder_enabled = $this->entity->isDisplayBuilderEnabled();
79
80    if ($is_display_builder_enabled) {
81      // Hide the table of fields.
82      $form['fields']['#access'] = FALSE;
83      $form['#fields'] = [];
84      $form['#extra'] = [];
85    }
86
87    $form['manage_display_builder'] = [
88      '#type' => 'link',
89      '#title' => $this->t('Display builder'),
90      '#weight' => -11,
91      '#attributes' => ['class' => ['button']],
92      '#url' => $this->entity->getBuilderUrl(),
93      '#access' => $is_display_builder_enabled,
94    ];
95
96    if (isset($form['modes'])) {
97      $form['modes']['#weight'] = 0;
98    }
99
100    $form['display_builder_wrapper'] = [
101      '#type' => 'details',
102      '#open' => TRUE,
103      '#title' => $this->t('Display builder'),
104      '#weight' => 1,
105    ];
106
107    $form['display_builder_wrapper'][ConfigFormBuilderInterface::PROFILE_PROPERTY] = $this->configFormBuilder->build($this->entity, FALSE);
108
109    /** @var \Drupal\display_builder_entity_view\Entity\DisplayBuilderEntityDisplayInterface $entity */
110    $entity = $this->getEntity();
111
112    if ($entity instanceof DisplayBuilderOverridableInterface) {
113      $form['display_builder_wrapper'][ConfigFormBuilderInterface::PROFILE_PROPERTY]['override_form'] = $this->buildOverridesForm($entity);
114    }
115
116    return $form;
117  }
118
119  /**
120   * Build the form for entity display overrides per content.
121   *
122   * @param \Drupal\display_builder_entity_view\Entity\DisplayBuilderEntityDisplayInterface $entity
123   *   The entity.
124   *
125   * @return array
126   *   The renderable form array.
127   */
128  protected function buildOverridesForm(DisplayBuilderEntityDisplayInterface|DisplayBuilderOverridableInterface $entity): array {
129    $entity_type_id = $entity->getTargetEntityTypeId();
130    $options = $this->getSourceFieldAsOptions();
131    $target_entity_type_id = $this->entityTypeManager->getDefinition($entity_type_id)->getBundleEntityType();
132    $description = [
133      '#title' => $this->t('Add a UI Patterns Source field'),
134      '#type' => 'link',
135      '#attributes' => [
136        'data-dialog-type' => 'modal',
137        'data-dialog-options' => \json_encode([
138          'title' => 'Add field: Source (UI Patterns)',
139          'width' => '800',
140        ]),
141        'class' => ['use-ajax'],
142      ],
143      '#url' => Url::fromRoute("field_ui.field_storage_config_add_sub_{$entity_type_id}", [
144        $target_entity_type_id => $entity->getTargetBundle(),
145        'display_as_group' => 'Group',
146        'selected_field_type' => 'ui_patterns_source',
147      ]),
148      '#suffix' => '.',
149    ];
150
151    if (empty($options)) {
152      $description = [
153        [
154          '#plain_text' => $this->t('No eligible field found.') . ' ',
155        ],
156        $description,
157      ];
158    }
159    /** @var \Drupal\display_builder_entity_view\Entity\DisplayBuilderOverridableInterface $overridable */
160    $overridable = $entity;
161    $form = [];
162    $form[ConfigFormBuilderInterface::OVERRIDE_FIELD_PROPERTY] = [
163      '#type' => 'select',
164      '#title' => $this->t('Select a field to override this display per content'),
165      '#options' => $options,
166      '#empty_option' => $this->t('- Disabled -'),
167      '#default_value' => $overridable->getDisplayBuilderOverrideField(),
168      '#description' => $description,
169    ];
170
171    $form[ConfigFormBuilderInterface::OVERRIDE_PROFILE_PROPERTY] = [
172      '#type' => 'select',
173      '#title' => $this->t('Override profile'),
174      '#description' => $this->t('The profile used for content overrides.'),
175      '#options' => $this->configFormBuilder->getAllowedProfiles(),
176      '#default_value' => $overridable->getDisplayBuilderOverrideProfile()?->id(),
177      '#states' => [
178        'invisible' => [
179          ':input[name="' . ConfigFormBuilderInterface::OVERRIDE_FIELD_PROPERTY . '"]' => ['filled' => FALSE],
180        ],
181      ],
182    ];
183
184    if (!$this->configFormBuilder->isAllowed($entity)) {
185      $form[ConfigFormBuilderInterface::OVERRIDE_FIELD_PROPERTY]['#disabled'] = TRUE;
186      unset($form[ConfigFormBuilderInterface::OVERRIDE_FIELD_PROPERTY]['#description']);
187      $form[ConfigFormBuilderInterface::OVERRIDE_PROFILE_PROPERTY]['#disabled'] = TRUE;
188    }
189
190    return $form;
191  }
192
193  /**
194   * Returns an array of UI Patterns Source fields which are already mapped.
195   *
196   * @param \Drupal\Core\Entity\Display\EntityViewDisplayInterface $current_display
197   *   The current display.
198   *
199   * @return array
200   *   An array of field names that are already mapped to the current display.
201   */
202  protected function getAlreadyMappedFields(EntityViewDisplayInterface $current_display): array {
203    /** @var \Drupal\display_builder_entity_view\Entity\DisplayBuilderEntityDisplayInterface[] $displays */
204    $displays = $this->entityTypeManager->getStorage('entity_view_display')->loadByProperties([
205      'targetEntityType' => $current_display->getTargetEntityTypeId(),
206      'bundle' => $current_display->getTargetBundle(),
207    ]);
208    $field_names = [];
209
210    foreach ($displays as $display) {
211      if ($display instanceof DisplayBuilderOverridableInterface) {
212        if ($display->isDisplayBuilderOverridable()
213          && $current_display->id() !== $display->id()) {
214          $field_names[] = $display->getDisplayBuilderOverrideField();
215        }
216      }
217    }
218
219    return $field_names;
220  }
221
222  /**
223   * Returns UI Patterns source fields as options.
224   *
225   * @return array
226   *   An associative array of field names and labels.
227   */
228  protected function getSourceFieldAsOptions(): array {
229    /** @var \Drupal\display_builder_entity_view\Entity\DisplayBuilderEntityDisplayInterface $display */
230    $display = $this->getEntity();
231    $field_definitions = $this->entityFieldManager->getFieldDefinitions(
232      $display->getTargetEntityTypeId(),
233      $display->getTargetBundle(),
234    );
235    $fields = [];
236
237    if ($display instanceof DisplayBuilderOverridableInterface
238      && $display instanceof EntityViewDisplayInterface
239    ) {
240      $already_mapped = $this->getAlreadyMappedFields($display);
241
242      foreach ($field_definitions as $field_name => $field_definition) {
243        if ($field_definition->getType() === 'ui_patterns_source'
244          && !\in_array($field_name, $already_mapped, TRUE)
245        ) {
246          $fields[$field_name] = $field_definition->getLabel();
247        }
248      }
249    }
250
251    return $fields;
252  }
253
254  /**
255   * Builds the table row structure for a single extra field.
256   *
257   * @param string $field_id
258   *   The field ID.
259   * @param array $extra_field
260   *   The pseudo-field element.
261   *
262   * @return array
263   *   A table row array.
264   */
265  protected function buildExtraFieldRow($field_id, $extra_field): array {
266    if ($this->entity->isDisplayBuilderEnabled()) {
267      return [];
268    }
269
270    return parent::buildExtraFieldRow($field_id, $extra_field);
271  }
272
273  /**
274   * Builds the table row structure for a single field.
275   *
276   * @param \Drupal\Core\Field\FieldDefinitionInterface $field_definition
277   *   The field definition.
278   * @param array $form
279   *   An associative array containing the structure of the form.
280   * @param \Drupal\Core\Form\FormStateInterface $form_state
281   *   The current state of the form.
282   *
283   * @return array
284   *   A table row array.
285   */
286  protected function buildFieldRow(FieldDefinitionInterface $field_definition, array $form, FormStateInterface $form_state): array {
287    if ($this->entity->isDisplayBuilderEnabled()) {
288      return [];
289    }
290
291    return parent::buildFieldRow($field_definition, $form, $form_state);
292  }
293
294  /**
295   * Copies top-level form values to entity properties.
296   *
297   * This should not change existing entity properties that are not being edited
298   * by this form.
299   *
300   * @param \Drupal\Core\Entity\EntityInterface $entity
301   *   The entity the current form should operate upon.
302   * @param array $form
303   *   A nested array of form elements comprising the form.
304   * @param \Drupal\Core\Form\FormStateInterface $form_state
305   *   The current state of the form.
306   *
307   * @see \Drupal\Core\Form\ConfigFormBase::copyFormValuesToConfig()
308   */
309  protected function copyFormValuesToEntity(EntityInterface $entity, array $form, FormStateInterface $form_state): void {
310    /** @var \Drupal\display_builder_entity_view\Entity\DisplayBuilderEntityDisplayInterface $entity */
311    // Do not process field values if Display Builder is or will be enabled.
312    $set_enabled = (bool) $form_state->getValue(['display_builder', 'enabled'], FALSE);
313    $already_enabled = $entity->isDisplayBuilderEnabled();
314
315    if ($already_enabled || $set_enabled) {
316      $form['#fields'] = [];
317      $form['#extra'] = [];
318    }
319
320    parent::copyFormValuesToEntity($entity, $form, $form_state);
321  }
322
323}