Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 130
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 / 130
0.00% covered (danger)
0.00%
0 / 8
812
0.00% covered (danger)
0.00%
0 / 1
 submitForm
0.00% covered (danger)
0.00%
0 / 16
0.00% covered (danger)
0.00%
0 / 1
20
 entityViewDisplayForm
0.00% covered (danger)
0.00%
0 / 26
0.00% covered (danger)
0.00%
0 / 1
20
 buildOverridesForm
0.00% covered (danger)
0.00%
0 / 51
0.00% covered (danger)
0.00%
0 / 1
6
 getAlreadyMappedFields
0.00% covered (danger)
0.00%
0 / 11
0.00% covered (danger)
0.00%
0 / 1
30
 getSourceFieldAsOptions
0.00% covered (danger)
0.00%
0 / 14
0.00% covered (danger)
0.00%
0 / 1
42
 buildExtraFieldRow
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
6
 buildFieldRow
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
6
 copyFormValuesToEntity
0.00% covered (danger)
0.00%
0 / 6
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. It can be changed anytime.'),
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    return $form;
185  }
186
187  /**
188   * Returns an array of UI Patterns Source fields which are already mapped.
189   *
190   * @param \Drupal\Core\Entity\Display\EntityViewDisplayInterface $current_display
191   *   The current display.
192   *
193   * @return array
194   *   An array of field names that are already mapped to the current display.
195   */
196  protected function getAlreadyMappedFields(EntityViewDisplayInterface $current_display): array {
197    /** @var \Drupal\display_builder_entity_view\Entity\DisplayBuilderEntityDisplayInterface[] $displays */
198    $displays = $this->entityTypeManager->getStorage('entity_view_display')->loadByProperties([
199      'targetEntityType' => $current_display->getTargetEntityTypeId(),
200      'bundle' => $current_display->getTargetBundle(),
201    ]);
202    $field_names = [];
203
204    foreach ($displays as $display) {
205      if ($display instanceof DisplayBuilderOverridableInterface) {
206        if ($display->isDisplayBuilderOverridable()
207          && $current_display->id() !== $display->id()) {
208          $field_names[] = $display->getDisplayBuilderOverrideField();
209        }
210      }
211    }
212
213    return $field_names;
214  }
215
216  /**
217   * Returns UI Patterns source fields as options.
218   *
219   * @return array
220   *   An associative array of field names and labels.
221   */
222  protected function getSourceFieldAsOptions(): array {
223    /** @var \Drupal\display_builder_entity_view\Entity\DisplayBuilderEntityDisplayInterface $display */
224    $display = $this->getEntity();
225    $field_definitions = $this->entityFieldManager->getFieldDefinitions(
226      $display->getTargetEntityTypeId(),
227      $display->getTargetBundle(),
228    );
229    $fields = [];
230
231    if ($display instanceof DisplayBuilderOverridableInterface
232      && $display instanceof EntityViewDisplayInterface
233    ) {
234      $already_mapped = $this->getAlreadyMappedFields($display);
235
236      foreach ($field_definitions as $field_name => $field_definition) {
237        if ($field_definition->getType() === 'ui_patterns_source'
238          && !\in_array($field_name, $already_mapped, TRUE)
239        ) {
240          $fields[$field_name] = $field_definition->getLabel();
241        }
242      }
243    }
244
245    return $fields;
246  }
247
248  /**
249   * Builds the table row structure for a single extra field.
250   *
251   * @param string $field_id
252   *   The field ID.
253   * @param array $extra_field
254   *   The pseudo-field element.
255   *
256   * @return array
257   *   A table row array.
258   */
259  protected function buildExtraFieldRow($field_id, $extra_field): array {
260    if ($this->entity->isDisplayBuilderEnabled()) {
261      return [];
262    }
263
264    return parent::buildExtraFieldRow($field_id, $extra_field);
265  }
266
267  /**
268   * Builds the table row structure for a single field.
269   *
270   * @param \Drupal\Core\Field\FieldDefinitionInterface $field_definition
271   *   The field definition.
272   * @param array $form
273   *   An associative array containing the structure of the form.
274   * @param \Drupal\Core\Form\FormStateInterface $form_state
275   *   The current state of the form.
276   *
277   * @return array
278   *   A table row array.
279   */
280  protected function buildFieldRow(FieldDefinitionInterface $field_definition, array $form, FormStateInterface $form_state): array {
281    if ($this->entity->isDisplayBuilderEnabled()) {
282      return [];
283    }
284
285    return parent::buildFieldRow($field_definition, $form, $form_state);
286  }
287
288  /**
289   * Copies top-level form values to entity properties.
290   *
291   * This should not change existing entity properties that are not being edited
292   * by this form.
293   *
294   * @param \Drupal\Core\Entity\EntityInterface $entity
295   *   The entity the current form should operate upon.
296   * @param array $form
297   *   A nested array of form elements comprising the form.
298   * @param \Drupal\Core\Form\FormStateInterface $form_state
299   *   The current state of the form.
300   *
301   * @see \Drupal\Core\Form\ConfigFormBase::copyFormValuesToConfig()
302   */
303  protected function copyFormValuesToEntity(EntityInterface $entity, array $form, FormStateInterface $form_state): void {
304    /** @var \Drupal\display_builder_entity_view\Entity\DisplayBuilderEntityDisplayInterface $entity */
305    // Do not process field values if Display Builder is or will be enabled.
306    $set_enabled = (bool) $form_state->getValue(['display_builder', 'enabled'], FALSE);
307    $already_enabled = $entity->isDisplayBuilderEnabled();
308
309    if ($already_enabled || $set_enabled) {
310      $form['#fields'] = [];
311      $form['#extra'] = [];
312    }
313
314    parent::copyFormValuesToEntity($entity, $form, $form_state);
315  }
316
317}