Code Coverage |
||||||||||||||||
Lines |
Branches |
Paths |
Functions and Methods |
Classes and Traits |
||||||||||||
| Total | |
0.00% |
0 / 183 |
n/a |
0 / 0 |
n/a |
0 / 0 |
|
0.00% |
0 / 11 |
CRAP | |
0.00% |
0 / 1 |
||
| EntityViewDisplayFormTrait | |
0.00% |
0 / 183 |
n/a |
0 / 0 |
n/a |
0 / 0 |
|
0.00% |
0 / 11 |
1806 | |
0.00% |
0 / 1 |
||
| submitForm | |
0.00% |
0 / 27 |
n/a |
0 / 0 |
n/a |
0 / 0 |
|
0.00% |
0 / 1 |
30 | |||||
| entityViewDisplayForm | |
0.00% |
0 / 27 |
n/a |
0 / 0 |
n/a |
0 / 0 |
|
0.00% |
0 / 1 |
20 | |||||
| buildOverridesForm | |
0.00% |
0 / 49 |
n/a |
0 / 0 |
n/a |
0 / 0 |
|
0.00% |
0 / 1 |
20 | |||||
| getAlreadyMappedFields | |
0.00% |
0 / 11 |
n/a |
0 / 0 |
n/a |
0 / 0 |
|
0.00% |
0 / 1 |
30 | |||||
| getSourceFieldAsOptions | |
0.00% |
0 / 17 |
n/a |
0 / 0 |
n/a |
0 / 0 |
|
0.00% |
0 / 1 |
56 | |||||
| buildExtraFieldRow | |
0.00% |
0 / 3 |
n/a |
0 / 0 |
n/a |
0 / 0 |
|
0.00% |
0 / 1 |
6 | |||||
| buildFieldRow | |
0.00% |
0 / 3 |
n/a |
0 / 0 |
n/a |
0 / 0 |
|
0.00% |
0 / 1 |
6 | |||||
| copyFormValuesToEntity | |
0.00% |
0 / 6 |
n/a |
0 / 0 |
n/a |
0 / 0 |
|
0.00% |
0 / 1 |
12 | |||||
| displayBuildable | |
0.00% |
0 / 2 |
n/a |
0 / 0 |
n/a |
0 / 0 |
|
0.00% |
0 / 1 |
2 | |||||
| createOverrideField | |
0.00% |
0 / 31 |
n/a |
0 / 0 |
n/a |
0 / 0 |
|
0.00% |
0 / 1 |
42 | |||||
| setOverrideFieldLocked | |
0.00% |
0 / 7 |
n/a |
0 / 0 |
n/a |
0 / 0 |
|
0.00% |
0 / 1 |
12 | |||||
| 1 | <?php |
| 2 | |
| 3 | declare(strict_types=1); |
| 4 | |
| 5 | namespace Drupal\display_builder_entity_view\Form; |
| 6 | |
| 7 | use Drupal\Core\Entity\Display\EntityViewDisplayInterface; |
| 8 | use Drupal\Core\Entity\EntityInterface; |
| 9 | use Drupal\Core\Field\FieldDefinitionInterface; |
| 10 | use Drupal\Core\Form\FormStateInterface; |
| 11 | use Drupal\Core\StringTranslation\StringTranslationTrait; |
| 12 | use Drupal\Core\StringTranslation\TranslatableMarkup; |
| 13 | use Drupal\display_builder\DisplayBuildableInterface; |
| 14 | use Drupal\display_builder_entity_view\Entity\DisplayBuilderEntityDisplayInterface; |
| 15 | use Drupal\display_builder_entity_view\Entity\DisplayBuilderOverridableInterface; |
| 16 | use Drupal\field\Entity\FieldConfig; |
| 17 | use Drupal\field\Entity\FieldStorageConfig; |
| 18 | |
| 19 | /** |
| 20 | * Common methods for entity view display form. |
| 21 | */ |
| 22 | trait EntityViewDisplayFormTrait { |
| 23 | |
| 24 | use StringTranslationTrait; |
| 25 | |
| 26 | /** |
| 27 | * Form submission handler. |
| 28 | * |
| 29 | * @param array $form |
| 30 | * An associative array containing the structure of the form. |
| 31 | * @param \Drupal\Core\Form\FormStateInterface $form_state |
| 32 | * The current state of the form. |
| 33 | */ |
| 34 | public function submitForm(array &$form, FormStateInterface $form_state): void { |
| 35 | parent::submitForm($form, $form_state); |
| 36 | |
| 37 | $profile = $form_state->getValue(DisplayBuildableInterface::PROFILE_PROPERTY, ''); |
| 38 | $override_status = (bool) $form_state->getValue('override_status', FALSE); |
| 39 | |
| 40 | // Empty means disable main and override. |
| 41 | if (empty($profile)) { |
| 42 | $this->setOverrideFieldLocked(FALSE); |
| 43 | $this->entity->unsetThirdPartySetting('display_builder', DisplayBuildableInterface::PROFILE_PROPERTY); |
| 44 | $this->entity->unsetThirdPartySetting('display_builder', DisplayBuildableInterface::OVERRIDE_FIELD_PROPERTY); |
| 45 | $this->entity->unsetThirdPartySetting('display_builder', DisplayBuildableInterface::OVERRIDE_PROFILE_PROPERTY); |
| 46 | } |
| 47 | else { |
| 48 | $this->entity->setThirdPartySetting('display_builder', DisplayBuildableInterface::PROFILE_PROPERTY, $profile); |
| 49 | } |
| 50 | |
| 51 | if ($override_status) { |
| 52 | $profile_override = $form_state->getValue(DisplayBuildableInterface::OVERRIDE_PROFILE_PROPERTY, NULL); |
| 53 | |
| 54 | if ($profile_override) { |
| 55 | $this->entity->setThirdPartySetting('display_builder', DisplayBuildableInterface::OVERRIDE_PROFILE_PROPERTY, $profile_override); |
| 56 | } |
| 57 | |
| 58 | $override_field = $form_state->getValue(DisplayBuildableInterface::OVERRIDE_FIELD_PROPERTY, NULL); |
| 59 | |
| 60 | if (!$override_field) { |
| 61 | $field_name = \sprintf('display_%s', $this->entity->getMode()); |
| 62 | $field_name = $this->createOverrideField($field_name); |
| 63 | } |
| 64 | else { |
| 65 | $field_name = $override_field; |
| 66 | } |
| 67 | // In case of field change, need to unlock the previous field. |
| 68 | $this->setOverrideFieldLocked(FALSE); |
| 69 | $this->entity->setThirdPartySetting('display_builder', DisplayBuildableInterface::OVERRIDE_FIELD_PROPERTY, $field_name); |
| 70 | $this->setOverrideFieldLocked(TRUE); |
| 71 | } |
| 72 | else { |
| 73 | $this->setOverrideFieldLocked(FALSE); |
| 74 | $this->entity->unsetThirdPartySetting('display_builder', DisplayBuildableInterface::OVERRIDE_FIELD_PROPERTY); |
| 75 | $this->entity->unsetThirdPartySetting('display_builder', DisplayBuildableInterface::OVERRIDE_PROFILE_PROPERTY); |
| 76 | } |
| 77 | |
| 78 | $this->entity->save(); |
| 79 | $this->localTaskManager->clearCachedDefinitions(); |
| 80 | $this->routeBuilder->rebuild(); |
| 81 | } |
| 82 | |
| 83 | /** |
| 84 | * Provides form elements to enable Display Builder. |
| 85 | * |
| 86 | * @param array $form |
| 87 | * The form structure. |
| 88 | * |
| 89 | * @return array |
| 90 | * The modified form. |
| 91 | */ |
| 92 | protected function entityViewDisplayForm(array $form): array { |
| 93 | $is_display_builder_enabled = $this->entity->isDisplayBuilderEnabled(); |
| 94 | |
| 95 | if ($is_display_builder_enabled) { |
| 96 | // Hide the table of fields. |
| 97 | $form['fields']['#access'] = FALSE; |
| 98 | $form['#fields'] = []; |
| 99 | $form['#extra'] = []; |
| 100 | } |
| 101 | |
| 102 | $form['manage_display_builder'] = [ |
| 103 | '#type' => 'link', |
| 104 | '#title' => $this->t('Display builder'), |
| 105 | '#weight' => -11, |
| 106 | '#attributes' => ['class' => ['button']], |
| 107 | '#url' => $this->displayBuildable()->getBuilderUrl(), |
| 108 | '#access' => $is_display_builder_enabled, |
| 109 | ]; |
| 110 | |
| 111 | if (isset($form['modes'])) { |
| 112 | $form['modes']['#weight'] = 0; |
| 113 | } |
| 114 | |
| 115 | $form['display_builder_wrapper'] = [ |
| 116 | '#type' => 'details', |
| 117 | '#open' => TRUE, |
| 118 | '#title' => $this->t('Display builder'), |
| 119 | '#weight' => 1, |
| 120 | ]; |
| 121 | |
| 122 | $title = new TranslatableMarkup('Enable with profile'); |
| 123 | $form['display_builder_wrapper'][DisplayBuildableInterface::PROFILE_PROPERTY] = $this->displayBuildable()->buildInstanceForm(FALSE, $title, FALSE); |
| 124 | |
| 125 | /** @var \Drupal\display_builder_entity_view\Entity\DisplayBuilderEntityDisplayInterface $entity */ |
| 126 | $entity = $this->getEntity(); |
| 127 | |
| 128 | if ($entity instanceof DisplayBuilderOverridableInterface) { |
| 129 | $form['display_builder_wrapper'][DisplayBuildableInterface::PROFILE_PROPERTY]['override_form'] = $this->buildOverridesForm($entity); |
| 130 | } |
| 131 | |
| 132 | return $form; |
| 133 | } |
| 134 | |
| 135 | /** |
| 136 | * Build the form for entity display overrides per content. |
| 137 | * |
| 138 | * @param \Drupal\display_builder_entity_view\Entity\DisplayBuilderEntityDisplayInterface $entity |
| 139 | * The entity. |
| 140 | * |
| 141 | * @return array |
| 142 | * The renderable form array. |
| 143 | */ |
| 144 | protected function buildOverridesForm(DisplayBuilderEntityDisplayInterface|DisplayBuilderOverridableInterface $entity): array { |
| 145 | /** @var \Drupal\display_builder_entity_view\Entity\DisplayBuilderOverridableInterface $overridable */ |
| 146 | $overridable = $entity; |
| 147 | |
| 148 | $options = $this->getSourceFieldAsOptions(); |
| 149 | $overrideFieldIsSet = $overridable->getDisplayBuilderOverrideField(); |
| 150 | |
| 151 | $form = [ |
| 152 | 'override' => [ |
| 153 | '#type' => 'container', |
| 154 | '#title' => $this->t('Content Override'), |
| 155 | '#states' => [ |
| 156 | 'visible' => [ |
| 157 | ':input[name="profile"]' => ['!value' => ''], |
| 158 | ], |
| 159 | ], |
| 160 | ], |
| 161 | ]; |
| 162 | |
| 163 | $form['override']['override_status'] = [ |
| 164 | '#type' => 'checkbox', |
| 165 | '#title' => $this->t('Enable content overrides'), |
| 166 | '#description' => $this->t('Each content will have the option to define a custom display.'), |
| 167 | '#default_value' => $overrideFieldIsSet ? TRUE : FALSE, |
| 168 | ]; |
| 169 | |
| 170 | $form['override']['settings'] = [ |
| 171 | '#type' => 'container', |
| 172 | '#title' => $this->t('Content Override'), |
| 173 | '#states' => [ |
| 174 | 'visible' => [ |
| 175 | ':input[name="override_status"]' => ['checked' => TRUE], |
| 176 | ], |
| 177 | ], |
| 178 | ]; |
| 179 | |
| 180 | $form['override']['settings'][DisplayBuildableInterface::OVERRIDE_PROFILE_PROPERTY] = [ |
| 181 | '#type' => 'select', |
| 182 | '#title' => $this->t('Profile for overrides'), |
| 183 | '#description' => $this->t('The profile used for content overrides. Can be changed at any time.'), |
| 184 | '#options' => $this->displayBuildable()->getAllowedProfiles(), |
| 185 | '#default_value' => $overridable->getDisplayBuilderOverrideProfile()?->id() ?? 'default', |
| 186 | ]; |
| 187 | |
| 188 | if (!empty($options)) { |
| 189 | $form['override']['settings'][DisplayBuildableInterface::OVERRIDE_FIELD_PROPERTY] = [ |
| 190 | '#type' => 'select', |
| 191 | '#title' => $this->t('Display Override Field'), |
| 192 | '#description' => $this->t('The field where per-content display overrides are stored.'), |
| 193 | '#options' => $options, |
| 194 | '#default_value' => $overrideFieldIsSet, |
| 195 | ]; |
| 196 | } |
| 197 | |
| 198 | if (!$this->displayBuildable()->isAllowed()) { |
| 199 | $form['override']['override_status']['#disabled'] = TRUE; |
| 200 | $form['override']['settings'][DisplayBuildableInterface::OVERRIDE_PROFILE_PROPERTY]['#disabled'] = TRUE; |
| 201 | $form['override']['settings'][DisplayBuildableInterface::OVERRIDE_FIELD_PROPERTY]['#disabled'] = TRUE; |
| 202 | } |
| 203 | |
| 204 | return $form; |
| 205 | } |
| 206 | |
| 207 | /** |
| 208 | * Returns an array of UI Patterns Source fields which are already mapped. |
| 209 | * |
| 210 | * @param \Drupal\Core\Entity\Display\EntityViewDisplayInterface $current_display |
| 211 | * The current display. |
| 212 | * |
| 213 | * @return array |
| 214 | * An array of field names that are already mapped to the current display. |
| 215 | */ |
| 216 | protected function getAlreadyMappedFields(EntityViewDisplayInterface $current_display): array { |
| 217 | /** @var \Drupal\display_builder_entity_view\Entity\DisplayBuilderEntityDisplayInterface[] $displays */ |
| 218 | $displays = $this->entityTypeManager->getStorage('entity_view_display')->loadByProperties([ |
| 219 | 'targetEntityType' => $current_display->getTargetEntityTypeId(), |
| 220 | 'bundle' => $current_display->getTargetBundle(), |
| 221 | ]); |
| 222 | $field_names = []; |
| 223 | |
| 224 | foreach ($displays as $display) { |
| 225 | if ($display instanceof DisplayBuilderOverridableInterface) { |
| 226 | if ($display->isDisplayBuilderOverridable() |
| 227 | && $current_display->id() !== $display->id()) { |
| 228 | $field_names[] = $display->getDisplayBuilderOverrideField(); |
| 229 | } |
| 230 | } |
| 231 | } |
| 232 | |
| 233 | return $field_names; |
| 234 | } |
| 235 | |
| 236 | /** |
| 237 | * Returns UI Patterns source fields as options. |
| 238 | * |
| 239 | * @return array |
| 240 | * An associative array of field names and labels. |
| 241 | */ |
| 242 | protected function getSourceFieldAsOptions(): array { |
| 243 | /** @var \Drupal\display_builder_entity_view\Entity\DisplayBuilderEntityDisplayInterface $display */ |
| 244 | $display = $this->getEntity(); |
| 245 | $fields = []; |
| 246 | // Load field instance definitions. |
| 247 | $field_storage_definitions = $this->entityFieldManager->getFieldDefinitions( |
| 248 | $display->getTargetEntityTypeId(), |
| 249 | $display->getTargetBundle(), |
| 250 | ); |
| 251 | |
| 252 | if ($display instanceof DisplayBuilderOverridableInterface |
| 253 | && $display instanceof EntityViewDisplayInterface |
| 254 | ) { |
| 255 | $already_mapped = $this->getAlreadyMappedFields($display); |
| 256 | |
| 257 | foreach ($field_storage_definitions as $field_name => $field_definition) { |
| 258 | if ($field_definition->getType() !== 'ui_patterns_source') { |
| 259 | continue; |
| 260 | } |
| 261 | |
| 262 | if (\in_array($field_name, $already_mapped, TRUE)) { |
| 263 | continue; |
| 264 | } |
| 265 | $field = FieldConfig::loadByName($this->entity->getTargetEntityTypeId(), $this->entity->getTargetBundle(), $field_name); |
| 266 | $fields[$field_name] = $field ? $field->label() : $field_name; |
| 267 | } |
| 268 | } |
| 269 | |
| 270 | return $fields; |
| 271 | } |
| 272 | |
| 273 | /** |
| 274 | * Builds the table row structure for a single extra field. |
| 275 | * |
| 276 | * @param string $field_id |
| 277 | * The field ID. |
| 278 | * @param array $extra_field |
| 279 | * The pseudo-field element. |
| 280 | * |
| 281 | * @return array |
| 282 | * A table row array. |
| 283 | */ |
| 284 | protected function buildExtraFieldRow($field_id, $extra_field): array { |
| 285 | if ($this->entity->isDisplayBuilderEnabled()) { |
| 286 | return []; |
| 287 | } |
| 288 | |
| 289 | return parent::buildExtraFieldRow($field_id, $extra_field); |
| 290 | } |
| 291 | |
| 292 | /** |
| 293 | * Builds the table row structure for a single field. |
| 294 | * |
| 295 | * @param \Drupal\Core\Field\FieldDefinitionInterface $field_definition |
| 296 | * The field definition. |
| 297 | * @param array $form |
| 298 | * An associative array containing the structure of the form. |
| 299 | * @param \Drupal\Core\Form\FormStateInterface $form_state |
| 300 | * The current state of the form. |
| 301 | * |
| 302 | * @return array |
| 303 | * A table row array. |
| 304 | */ |
| 305 | protected function buildFieldRow(FieldDefinitionInterface $field_definition, array $form, FormStateInterface $form_state): array { |
| 306 | if ($this->entity->isDisplayBuilderEnabled()) { |
| 307 | return []; |
| 308 | } |
| 309 | |
| 310 | return parent::buildFieldRow($field_definition, $form, $form_state); |
| 311 | } |
| 312 | |
| 313 | /** |
| 314 | * Copies top-level form values to entity properties. |
| 315 | * |
| 316 | * This should not change existing entity properties that are not being edited |
| 317 | * by this form. |
| 318 | * |
| 319 | * @param \Drupal\Core\Entity\EntityInterface $entity |
| 320 | * The entity the current form should operate upon. |
| 321 | * @param array $form |
| 322 | * A nested array of form elements comprising the form. |
| 323 | * @param \Drupal\Core\Form\FormStateInterface $form_state |
| 324 | * The current state of the form. |
| 325 | * |
| 326 | * @see \Drupal\Core\Form\ConfigFormBase::copyFormValuesToConfig() |
| 327 | */ |
| 328 | protected function copyFormValuesToEntity(EntityInterface $entity, array $form, FormStateInterface $form_state): void { |
| 329 | /** @var \Drupal\display_builder_entity_view\Entity\DisplayBuilderEntityDisplayInterface $entity */ |
| 330 | // Do not process field values if Display Builder is or will be enabled. |
| 331 | $set_enabled = (bool) $form_state->getValue(['display_builder', 'enabled'], FALSE); |
| 332 | $already_enabled = $entity->isDisplayBuilderEnabled(); |
| 333 | |
| 334 | if ($already_enabled || $set_enabled) { |
| 335 | $form['#fields'] = []; |
| 336 | $form['#extra'] = []; |
| 337 | } |
| 338 | |
| 339 | parent::copyFormValuesToEntity($entity, $form, $form_state); |
| 340 | } |
| 341 | |
| 342 | /** |
| 343 | * Gets the display buildable manager. |
| 344 | * |
| 345 | * @return \Drupal\display_builder\DisplayBuildableInterface |
| 346 | * The manager for display buildable. |
| 347 | */ |
| 348 | protected function displayBuildable(): DisplayBuildableInterface { |
| 349 | /** @var \Drupal\display_builder\DisplayBuildableInterface $buildable */ |
| 350 | $buildable = $this->displayBuildableManager->createInstance('entity_view', ['entity' => $this->getEntity()]); |
| 351 | |
| 352 | return $buildable; |
| 353 | } |
| 354 | |
| 355 | /** |
| 356 | * Create a field based on a name. |
| 357 | * |
| 358 | * @param string $field_name |
| 359 | * The field name, field prefix will be added. |
| 360 | */ |
| 361 | private function createOverrideField(string $field_name): string { |
| 362 | try { |
| 363 | $field_prefix = $this->configFactory()->get('field_ui.settings')->get('field_prefix'); |
| 364 | } |
| 365 | catch (\Throwable $th) { |
| 366 | $field_prefix = 'field_'; |
| 367 | } |
| 368 | |
| 369 | $field_name = $field_prefix . $field_name; |
| 370 | |
| 371 | if (\strlen($field_name) > FieldStorageConfig::NAME_MAX_LENGTH) { |
| 372 | $field_name = \substr($field_name, 0, FieldStorageConfig::NAME_MAX_LENGTH); |
| 373 | } |
| 374 | $field_storage = FieldStorageConfig::loadByName($this->entity->getTargetEntityTypeId(), $field_name); |
| 375 | |
| 376 | if (!$field_storage) { |
| 377 | $field_storage = FieldStorageConfig::create([ |
| 378 | 'field_name' => $field_name, |
| 379 | 'entity_type' => $this->entity->getTargetEntityTypeId(), |
| 380 | 'type' => 'ui_patterns_source', |
| 381 | ]); |
| 382 | $field_storage->setTranslatable(TRUE); |
| 383 | $field_storage->setCardinality(-1); |
| 384 | $field_storage->save(); |
| 385 | } |
| 386 | |
| 387 | // Add the field prefix to the field name and cut to max size if needed. |
| 388 | $field_definition = FieldConfig::loadByName($this->entity->getTargetEntityTypeId(), $this->entity->getTargetBundle(), $field_name); |
| 389 | |
| 390 | if (!$field_definition) { |
| 391 | $view_mode_id = $this->entity->getMode(); |
| 392 | $view_mode_id = ($view_mode_id === 'default') ? 'full' : $view_mode_id; |
| 393 | $view_mode_id = \sprintf('%s.%s', $this->entity->getTargetEntityTypeId(), $view_mode_id); |
| 394 | $view_mode = $this->entityTypeManager->getStorage('entity_view_mode')->load($view_mode_id); |
| 395 | $field_definition = FieldConfig::create([ |
| 396 | 'field_storage' => $field_storage, |
| 397 | 'bundle' => $this->entity->getTargetBundle(), |
| 398 | 'field_name' => $field_name, |
| 399 | 'label' => $this->t('@display display override', ['@display' => $view_mode->label()]), |
| 400 | ]); |
| 401 | $field_definition->setTranslatable(TRUE); |
| 402 | $field_definition->save(); |
| 403 | } |
| 404 | |
| 405 | return $field_name; |
| 406 | } |
| 407 | |
| 408 | /** |
| 409 | * Set the lock status of the override field if it exists. |
| 410 | * |
| 411 | * @param bool $locked |
| 412 | * Whether to lock or unlock the field. |
| 413 | */ |
| 414 | private function setOverrideFieldLocked(bool $locked): void { |
| 415 | $field_name = $this->entity->getDisplayBuilderOverrideField(); |
| 416 | |
| 417 | if (!$field_name) { |
| 418 | return; |
| 419 | } |
| 420 | $field_storage = FieldStorageConfig::loadByName($this->entity->getTargetEntityTypeId(), $field_name); |
| 421 | |
| 422 | if ($field_storage) { |
| 423 | $field_storage->setLocked($locked); |
| 424 | $field_storage->save(); |
| 425 | } |
| 426 | } |
| 427 | |
| 428 | } |
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.