Code Coverage |
||||||||||||||||
Lines |
Branches |
Paths |
Functions and Methods |
Classes and Traits |
||||||||||||
| Total | |
0.00% |
0 / 154 |
|
0.00% |
0 / 78 |
|
0.00% |
0 / 70 |
|
0.00% |
0 / 22 |
CRAP | |
0.00% |
0 / 1 |
| EntityViewOverride | |
0.00% |
0 / 150 |
|
0.00% |
0 / 78 |
|
0.00% |
0 / 70 |
|
0.00% |
0 / 22 |
2352 | |
0.00% |
0 / 1 |
| __construct | |
0.00% |
0 / 9 |
|
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
| getContextRequirement | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
| getBuilderUrl | |
0.00% |
0 / 8 |
|
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
| checkInstanceId | |
0.00% |
0 / 8 |
|
0.00% |
0 / 3 |
|
0.00% |
0 / 2 |
|
0.00% |
0 / 1 |
6 | |||
| getUrlFromInstanceId | |
0.00% |
0 / 13 |
|
0.00% |
0 / 5 |
|
0.00% |
0 / 3 |
|
0.00% |
0 / 1 |
12 | |||
| getDisplayUrlFromInstanceId | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
| getProfile | |
0.00% |
0 / 2 |
|
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
| getSources | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
| saveSources | |
0.00% |
0 / 7 |
|
0.00% |
0 / 3 |
|
0.00% |
0 / 2 |
|
0.00% |
0 / 1 |
6 | |||
| checkAccess | |
0.00% |
0 / 5 |
|
0.00% |
0 / 3 |
|
0.00% |
0 / 2 |
|
0.00% |
0 / 1 |
6 | |||
| getInstanceId | |
0.00% |
0 / 10 |
|
0.00% |
0 / 3 |
|
0.00% |
0 / 2 |
|
0.00% |
0 / 1 |
6 | |||
| setRevision | |
0.00% |
0 / 8 |
|
0.00% |
0 / 7 |
|
0.00% |
0 / 6 |
|
0.00% |
0 / 1 |
20 | |||
| collectInstances | |
0.00% |
0 / 17 |
|
0.00% |
0 / 10 |
|
0.00% |
0 / 8 |
|
0.00% |
0 / 1 |
20 | |||
| getInitializationMessage | |
0.00% |
0 / 5 |
|
0.00% |
0 / 5 |
|
0.00% |
0 / 3 |
|
0.00% |
0 / 1 |
12 | |||
| getInitialSources | |
0.00% |
0 / 16 |
|
0.00% |
0 / 10 |
|
0.00% |
0 / 9 |
|
0.00% |
0 / 1 |
42 | |||
| getInitialContext | |
0.00% |
0 / 10 |
|
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
| collectInstancesByField | |
0.00% |
0 / 15 |
|
0.00% |
0 / 6 |
|
0.00% |
0 / 4 |
|
0.00% |
0 / 1 |
12 | |||
| time | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
| entityTypeManager | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
| displayBuildableManager | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
| dataConverter | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
| getEntityViewDisplay | |
0.00% |
0 / 10 |
|
0.00% |
0 / 12 |
|
0.00% |
0 / 18 |
|
0.00% |
0 / 1 |
42 | |||
| 1 | <?php |
| 2 | |
| 3 | declare(strict_types=1); |
| 4 | |
| 5 | namespace Drupal\display_builder_entity_view\Plugin\display_builder\Buildable; |
| 6 | |
| 7 | use Drupal\Component\Datetime\TimeInterface; |
| 8 | use Drupal\Core\Access\AccessResult; |
| 9 | use Drupal\Core\Access\AccessResultInterface; |
| 10 | use Drupal\Core\Entity\ContentEntityInterface; |
| 11 | use Drupal\Core\Entity\EntityStorageInterface; |
| 12 | use Drupal\Core\Entity\EntityTypeManagerInterface; |
| 13 | use Drupal\Core\Entity\Query\QueryInterface; |
| 14 | use Drupal\Core\Entity\RevisionableEntityBundleInterface; |
| 15 | use Drupal\Core\Entity\RevisionLogInterface; |
| 16 | use Drupal\Core\Field\FieldItemListInterface; |
| 17 | use Drupal\Core\Plugin\Context\Context; |
| 18 | use Drupal\Core\Plugin\Context\ContextDefinition; |
| 19 | use Drupal\Core\Plugin\Context\EntityContext; |
| 20 | use Drupal\Core\Session\AccountInterface; |
| 21 | use Drupal\Core\StringTranslation\TranslatableMarkup; |
| 22 | use Drupal\Core\Url; |
| 23 | use Drupal\display_builder\Attribute\DisplayBuildable; |
| 24 | use Drupal\display_builder\DisplayBuildableInterface; |
| 25 | use Drupal\display_builder\DisplayBuildablePluginBase; |
| 26 | use Drupal\display_builder\DisplayBuildablePluginManager; |
| 27 | use Drupal\display_builder\ProfileInterface; |
| 28 | use Drupal\display_builder_entity_view\BuilderDataConverter; |
| 29 | use Drupal\display_builder_entity_view\Entity\DisplayBuilderEntityDisplayInterface; |
| 30 | use Drupal\display_builder_entity_view\Entity\DisplayBuilderOverridableInterface; |
| 31 | use Drupal\ui_patterns\Plugin\Context\RequirementsContext; |
| 32 | |
| 33 | /** |
| 34 | * Plugin implementation of the display_buildable. |
| 35 | */ |
| 36 | #[DisplayBuildable( |
| 37 | id: 'entity_view_override', |
| 38 | label: new TranslatableMarkup('Entity view override'), |
| 39 | instance_prefix: 'entity_override__', |
| 40 | )] |
| 41 | final class EntityViewOverride extends DisplayBuildablePluginBase { |
| 42 | |
| 43 | /** |
| 44 | * The time service. |
| 45 | */ |
| 46 | protected ?TimeInterface $time; |
| 47 | |
| 48 | /** |
| 49 | * The data converter from Manage Display and Layout Builder. |
| 50 | */ |
| 51 | protected BuilderDataConverter $dataConverter; |
| 52 | |
| 53 | /** |
| 54 | * The field items where the override is stored. |
| 55 | */ |
| 56 | private FieldItemListInterface $field; |
| 57 | |
| 58 | /** |
| 59 | * The display buildable plugin manager. |
| 60 | */ |
| 61 | private DisplayBuildablePluginManager $displayBuildableManager; |
| 62 | |
| 63 | /** |
| 64 | * The overridden display. |
| 65 | */ |
| 66 | private DisplayBuilderEntityDisplayInterface $display; |
| 67 | |
| 68 | /** |
| 69 | * {@inheritdoc} |
| 70 | */ |
| 71 | public function __construct(array $configuration, $plugin_id, $plugin_definition) { |
| 72 | parent::__construct($configuration, $plugin_id, $plugin_definition); |
| 73 | $this->field = $configuration['field']; |
| 74 | \assert(\is_string($this->field->getName())); |
| 75 | $entity = $this->field->getEntity(); |
| 76 | $this->display = self::getEntityViewDisplay( |
| 77 | $entity->getEntityTypeId(), |
| 78 | $entity->bundle(), |
| 79 | $this->field->getName(), |
| 80 | ); |
| 81 | } |
| 82 | |
| 83 | /** |
| 84 | * {@inheritdoc} |
| 85 | */ |
| 86 | public static function getContextRequirement(): string { |
| 87 | return 'content'; |
| 88 | } |
| 89 | |
| 90 | /** |
| 91 | * {@inheritdoc} |
| 92 | */ |
| 93 | public function getBuilderUrl(): Url { |
| 94 | \assert(\is_string($this->field->getName())); |
| 95 | $entity = $this->field->getEntity(); |
| 96 | $entity_type_id = $this->display->getTargetEntityTypeId(); |
| 97 | $parameters = [ |
| 98 | $entity_type_id => $entity->id(), |
| 99 | 'view_mode_name' => $this->display->getMode(), |
| 100 | ]; |
| 101 | |
| 102 | return Url::fromRoute(\sprintf('entity.%s.display_builder.%s', $entity_type_id, $this->display->getMode()), $parameters); |
| 103 | } |
| 104 | |
| 105 | /** |
| 106 | * {@inheritdoc} |
| 107 | */ |
| 108 | public static function checkInstanceId(string $instance_id): ?array { |
| 109 | if (!\str_starts_with($instance_id, self::getPrefix())) { |
| 110 | return NULL; |
| 111 | } |
| 112 | [, $entity_type_id, $entity_id, $field_name] = \explode('__', $instance_id); |
| 113 | |
| 114 | return [ |
| 115 | 'entity_type_id' => $entity_type_id, |
| 116 | 'entity_id' => $entity_id, |
| 117 | 'field_name' => $field_name, |
| 118 | ]; |
| 119 | } |
| 120 | |
| 121 | /** |
| 122 | * {@inheritdoc} |
| 123 | */ |
| 124 | public static function getUrlFromInstanceId(string $instance_id): Url { |
| 125 | [, $entity_type_id, $entity_id, $field_name] = \explode('__', $instance_id); |
| 126 | |
| 127 | $entity = \Drupal::entityTypeManager()->getStorage($entity_type_id)->load($entity_id); |
| 128 | |
| 129 | if (!$entity) { |
| 130 | return Url::fromRoute('entity.display_builder_instance.collection'); |
| 131 | } |
| 132 | |
| 133 | $display = self::getEntityViewDisplay($entity_type_id, $entity->bundle(), $field_name); |
| 134 | |
| 135 | if (!$display) { |
| 136 | return Url::fromRoute('entity.display_builder_instance.collection'); |
| 137 | } |
| 138 | $params = [ |
| 139 | $entity_type_id => $entity_id, |
| 140 | 'view_mode_name' => $display->getMode(), |
| 141 | ]; |
| 142 | |
| 143 | $route_name = \sprintf('entity.%s.display_builder.%s', $entity_type_id, $display->getMode()); |
| 144 | |
| 145 | return Url::fromRoute($route_name, $params); |
| 146 | } |
| 147 | |
| 148 | /** |
| 149 | * {@inheritdoc} |
| 150 | */ |
| 151 | public static function getDisplayUrlFromInstanceId(string $instance_id): Url { |
| 152 | return Url::fromRoute('<front>'); |
| 153 | } |
| 154 | |
| 155 | /** |
| 156 | * {@inheritdoc} |
| 157 | */ |
| 158 | public function getProfile(): ?ProfileInterface { |
| 159 | \assert($this->display instanceof DisplayBuilderOverridableInterface); |
| 160 | |
| 161 | return $this->display->getDisplayBuilderOverrideProfile(); |
| 162 | } |
| 163 | |
| 164 | /** |
| 165 | * {@inheritdoc} |
| 166 | */ |
| 167 | public function getSources(): array { |
| 168 | return $this->field->getValue(); |
| 169 | } |
| 170 | |
| 171 | /** |
| 172 | * {@inheritdoc} |
| 173 | */ |
| 174 | public function saveSources(): void { |
| 175 | $data = $this->getInstance()->getCurrentState(); |
| 176 | $entity = $this->field->getEntity(); |
| 177 | |
| 178 | if ($entity instanceof ContentEntityInterface) { |
| 179 | $this->setRevision($entity); |
| 180 | } |
| 181 | $entity->save(); |
| 182 | $this->field->setValue($data); |
| 183 | $this->field->getEntity()->save(); |
| 184 | } |
| 185 | |
| 186 | /** |
| 187 | * {@inheritdoc} |
| 188 | */ |
| 189 | public static function checkAccess(string $instance_id, AccountInterface $account): AccessResultInterface { |
| 190 | [, $entity_type_id, $entity_id] = \explode('__', $instance_id); |
| 191 | $entity = \Drupal::entityTypeManager()->getStorage($entity_type_id)->load($entity_id); |
| 192 | |
| 193 | if (!$entity) { |
| 194 | return AccessResult::neutral(); |
| 195 | } |
| 196 | |
| 197 | return $entity->access('update', $account, TRUE); |
| 198 | } |
| 199 | |
| 200 | /** |
| 201 | * {@inheritdoc} |
| 202 | */ |
| 203 | public function getInstanceId(): ?string { |
| 204 | // Usually an entity is new if no ID exists for it yet. |
| 205 | if ($this->field->getEntity()->isNew()) { |
| 206 | return NULL; |
| 207 | } |
| 208 | |
| 209 | $entity = $this->field->getEntity(); |
| 210 | |
| 211 | return \sprintf( |
| 212 | '%s%s__%s__%s', |
| 213 | self::getPrefix(), |
| 214 | $entity->getEntityTypeId(), |
| 215 | $entity->id(), |
| 216 | $this->field->getName() |
| 217 | ); |
| 218 | } |
| 219 | |
| 220 | /** |
| 221 | * Set revision if appropriate. |
| 222 | * |
| 223 | * @param \Drupal\Core\Entity\ContentEntityInterface $entity |
| 224 | * The entity to set revision if appropriate. |
| 225 | */ |
| 226 | public function setRevision(ContentEntityInterface $entity): void { |
| 227 | $bundle = $entity->getBundleEntity(); |
| 228 | |
| 229 | if ($bundle instanceof RevisionableEntityBundleInterface |
| 230 | && !$bundle->shouldCreateNewRevision() |
| 231 | ) { |
| 232 | return; |
| 233 | } |
| 234 | |
| 235 | $entity->setNewRevision(); |
| 236 | |
| 237 | if ($entity instanceof RevisionLogInterface) { |
| 238 | $entity->setRevisionLogMessage($this->t('Updated using Display Builder.')->render()); |
| 239 | $entity->setRevisionCreationTime($this->time()->getCurrentTime()); |
| 240 | } |
| 241 | } |
| 242 | |
| 243 | /** |
| 244 | * {@inheritdoc} |
| 245 | */ |
| 246 | public static function collectInstances(?EntityTypeManagerInterface $entityTypeManager = NULL): array { |
| 247 | $instances = []; |
| 248 | $entityTypeManager = \Drupal::service('entity_type.manager'); |
| 249 | $storage = $entityTypeManager->getStorage('entity_view_display'); |
| 250 | $instance_storage = $entityTypeManager->getStorage('display_builder_instance'); |
| 251 | $entity_query = $entity_storage = []; |
| 252 | |
| 253 | foreach ($storage->loadMultiple() as $display) { |
| 254 | /** @var \Drupal\Core\Entity\Display\EntityViewDisplayInterface $display */ |
| 255 | $display_builder = $display->getThirdPartySettings('display_builder'); |
| 256 | |
| 257 | if (!isset($display_builder[DisplayBuildableInterface::OVERRIDE_FIELD_PROPERTY], $display_builder[DisplayBuildableInterface::OVERRIDE_PROFILE_PROPERTY])) { |
| 258 | continue; |
| 259 | } |
| 260 | |
| 261 | $entity_type = $display->getTargetEntityTypeId(); |
| 262 | $field_name = $display_builder[DisplayBuildableInterface::OVERRIDE_FIELD_PROPERTY] ?? NULL; |
| 263 | |
| 264 | if (!$field_name) { |
| 265 | continue; |
| 266 | } |
| 267 | $entity_storage[$entity_type] ??= $entityTypeManager->getStorage($entity_type); |
| 268 | $entity_query[$entity_type] ??= $entity_storage[$entity_type]->getQuery()->accessCheck(FALSE); |
| 269 | $instances = \array_merge($instances, self::collectInstancesByField($field_name, $entity_type, $instance_storage, $entity_query[$entity_type])); |
| 270 | } |
| 271 | |
| 272 | return $instances; |
| 273 | } |
| 274 | |
| 275 | /** |
| 276 | * {@inheritdoc} |
| 277 | */ |
| 278 | protected function getInitializationMessage(): TranslatableMarkup { |
| 279 | if ($this->initialDataSource === 'display_builder') { |
| 280 | return $this->t('Copy from Entity View Display configuration.'); |
| 281 | } |
| 282 | |
| 283 | if ($this->initialDataSource === 'layout_builder_override') { |
| 284 | return $this->t('Import from Layout Builder override.'); |
| 285 | } |
| 286 | |
| 287 | return $this->t('Initialization from existing content.'); |
| 288 | } |
| 289 | |
| 290 | /** |
| 291 | * {@inheritdoc} |
| 292 | */ |
| 293 | protected function getInitialSources(): array { |
| 294 | $sources = $this->getSources(); |
| 295 | |
| 296 | // 1. Keep the existing override value if existing. |
| 297 | if (\count($sources) > 0) { |
| 298 | return $sources; |
| 299 | } |
| 300 | |
| 301 | // 2. Convert the Layout Builder Override if exists. |
| 302 | // There is always a single Layout Builder override per bundle: `default`. |
| 303 | // There could be many Display Builder overrides per bundle, one for each |
| 304 | // display, so we need to check. |
| 305 | if ($this->display->getMode() === 'default') { |
| 306 | $entity = $this->field->getEntity(); |
| 307 | // @see: use Drupal\layout_builder\Plugin\SectionStorage\OverridesSectionStorage::$FIELD_NAME |
| 308 | $field_name = 'layout_builder__layout'; |
| 309 | |
| 310 | if ($entity->hasField($field_name) && !$entity->get($field_name)->isEmpty()) { |
| 311 | $content = $entity->get($field_name)->first()->getValue(); |
| 312 | $this->initialDataSource = 'layout_builder_override'; |
| 313 | |
| 314 | return $this->dataConverter()->convertFromLayoutBuilder($content); |
| 315 | } |
| 316 | } |
| 317 | |
| 318 | // 3. Copy entity view display value. |
| 319 | \assert(\is_string($this->field->getName())); |
| 320 | /** @var \Drupal\display_builder\DisplayBuildableInterface $buildable */ |
| 321 | $buildable = $this->displayBuildableManager()->createInstance('entity_view', ['entity' => $this->display]); |
| 322 | |
| 323 | if ($buildable->getProfile() !== NULL) { |
| 324 | $sources = $buildable->getSources(); |
| 325 | } |
| 326 | $this->initialDataSource = 'display_builder'; |
| 327 | |
| 328 | return $sources; |
| 329 | } |
| 330 | |
| 331 | /** |
| 332 | * {@inheritdoc} |
| 333 | */ |
| 334 | protected function getInitialContext(): array { |
| 335 | $entity = $this->field->getEntity(); |
| 336 | $bundle = $entity->bundle(); |
| 337 | \assert(\is_string($this->field->getName())); |
| 338 | |
| 339 | $view_mode = $this->display->getMode(); |
| 340 | $contexts = [ |
| 341 | 'entity' => EntityContext::fromEntity($entity), |
| 342 | 'bundle' => new Context(ContextDefinition::create('string'), $bundle), |
| 343 | 'view_mode' => new Context(ContextDefinition::create('string'), $view_mode), |
| 344 | ]; |
| 345 | |
| 346 | return RequirementsContext::addToContext([self::getContextRequirement()], $contexts); |
| 347 | } |
| 348 | |
| 349 | /** |
| 350 | * Collect instances by field storage. |
| 351 | * |
| 352 | * @param string $field_name |
| 353 | * Field name. |
| 354 | * @param string $entity_type |
| 355 | * Entity type ID. |
| 356 | * @param \Drupal\Core\Entity\EntityStorageInterface $instance_storage |
| 357 | * Instance entity storage handler. |
| 358 | * @param \Drupal\Core\Entity\Query\QueryInterface $entity_query |
| 359 | * Entity query handler. |
| 360 | * |
| 361 | * @return array |
| 362 | * A associative array of Instance entities or null values. |
| 363 | */ |
| 364 | protected static function collectInstancesByField(string $field_name, string $entity_type, EntityStorageInterface $instance_storage, QueryInterface $entity_query): array { |
| 365 | $instances = []; |
| 366 | $entity_query->exists($field_name); |
| 367 | // QueryInterface::execute() returns an integer for count queries or an |
| 368 | // array of ids. |
| 369 | /** @var array $ids */ |
| 370 | $ids = $entity_query->execute(); |
| 371 | |
| 372 | if (empty($ids)) { |
| 373 | return []; |
| 374 | } |
| 375 | |
| 376 | foreach ($ids as $id) { |
| 377 | $instance_id = \sprintf( |
| 378 | '%s%s__%s__%s', |
| 379 | self::getPrefix(), |
| 380 | $entity_type, |
| 381 | $id, |
| 382 | $field_name, |
| 383 | ); |
| 384 | // We are OK with keeping the null values if the instance entity |
| 385 | // doesn't exists in storage. So the caller can decide to create |
| 386 | // the missing Instance entities. |
| 387 | $instances[$instance_id] = $instance_storage->load($instance_id); |
| 388 | } |
| 389 | |
| 390 | return $instances; |
| 391 | } |
| 392 | |
| 393 | /** |
| 394 | * Get the time service. |
| 395 | * |
| 396 | * @return \Drupal\Component\Datetime\TimeInterface |
| 397 | * The time service. |
| 398 | */ |
| 399 | protected function time(): TimeInterface { |
| 400 | return $this->time ??= \Drupal::service('datetime.time'); |
| 401 | } |
| 402 | |
| 403 | /** |
| 404 | * Get the entity type manager. |
| 405 | * |
| 406 | * @return \Drupal\Core\Entity\EntityTypeManagerInterface |
| 407 | * The entity type manager. |
| 408 | */ |
| 409 | protected function entityTypeManager(): EntityTypeManagerInterface { |
| 410 | return $this->entityTypeManager ??= \Drupal::service('entity_type.manager'); |
| 411 | } |
| 412 | |
| 413 | /** |
| 414 | * Gets the display buildable manager. |
| 415 | * |
| 416 | * @return \Drupal\display_builder\DisplayBuildablePluginManager |
| 417 | * The manager for display buildable. |
| 418 | */ |
| 419 | protected function displayBuildableManager(): DisplayBuildablePluginManager { |
| 420 | return $this->displayBuildableManager ??= \Drupal::service('plugin.manager.display_buildable'); |
| 421 | } |
| 422 | |
| 423 | /** |
| 424 | * Get the data converter from Manage Display and Layout Builder. |
| 425 | * |
| 426 | * @return \Drupal\display_builder_entity_view\BuilderDataConverter |
| 427 | * The converter. |
| 428 | */ |
| 429 | protected function dataConverter(): BuilderDataConverter { |
| 430 | return $this->dataConverter ??= \Drupal::service('display_builder_entity_view.builder_data_converter'); |
| 431 | } |
| 432 | |
| 433 | /** |
| 434 | * Get entity view display entity. |
| 435 | * |
| 436 | * @param string $entity_type_id |
| 437 | * Entity type ID. |
| 438 | * @param string $bundle |
| 439 | * Entity's bundle which support fields. |
| 440 | * @param string $fieldName |
| 441 | * Field name of the display. |
| 442 | * |
| 443 | * @return \Drupal\display_builder_entity_view\Entity\DisplayBuilderEntityDisplayInterface|null |
| 444 | * The corresponding entity view display. |
| 445 | */ |
| 446 | private static function getEntityViewDisplay(string $entity_type_id, string $bundle, string $fieldName): ?DisplayBuilderEntityDisplayInterface { |
| 447 | /** @var \Drupal\display_builder_entity_view\Entity\DisplayBuilderEntityDisplayInterface[] $displays */ |
| 448 | $displays = \Drupal::entityTypeManager()->getStorage('entity_view_display')->loadByProperties([ |
| 449 | 'targetEntityType' => $entity_type_id, |
| 450 | ]); |
| 451 | |
| 452 | foreach ($displays as $display) { |
| 453 | if ($display instanceof DisplayBuilderOverridableInterface |
| 454 | && $display->getDisplayBuilderOverrideField() === $fieldName |
| 455 | && $display->getTargetEntityTypeId() |
| 456 | && $display->getTargetBundle() === $bundle |
| 457 | ) { |
| 458 | return $display; |
| 459 | } |
| 460 | } |
| 461 | |
| 462 | return NULL; |
| 463 | } |
| 464 | |
| 465 | } |
Below are the source code lines that represent each code branch as identified by Xdebug. Please note a branch is not
necessarily coterminous with a line, a line may contain multiple branches and therefore show up more than once.
Please also be aware that some branches may be implicit rather than explicit, e.g. an if statement
always has an else as part of its logical flow even if you didn't write one.
| 71 | public function __construct(array $configuration, $plugin_id, $plugin_definition) { |
| 72 | parent::__construct($configuration, $plugin_id, $plugin_definition); |
| 73 | $this->field = $configuration['field']; |
| 74 | \assert(\is_string($this->field->getName())); |
| 75 | $entity = $this->field->getEntity(); |
| 76 | $this->display = self::getEntityViewDisplay( |
| 77 | $entity->getEntityTypeId(), |
| 78 | $entity->bundle(), |
| 79 | $this->field->getName(), |
| 80 | ); |
| 81 | } |
| 189 | public static function checkAccess(string $instance_id, AccountInterface $account): AccessResultInterface { |
| 190 | [, $entity_type_id, $entity_id] = \explode('__', $instance_id); |
| 191 | $entity = \Drupal::entityTypeManager()->getStorage($entity_type_id)->load($entity_id); |
| 192 | |
| 193 | if (!$entity) { |
| 194 | return AccessResult::neutral(); |
| 197 | return $entity->access('update', $account, TRUE); |
| 198 | } |
| 108 | public static function checkInstanceId(string $instance_id): ?array { |
| 109 | if (!\str_starts_with($instance_id, self::getPrefix())) { |
| 110 | return NULL; |
| 112 | [, $entity_type_id, $entity_id, $field_name] = \explode('__', $instance_id); |
| 113 | |
| 114 | return [ |
| 115 | 'entity_type_id' => $entity_type_id, |
| 116 | 'entity_id' => $entity_id, |
| 117 | 'field_name' => $field_name, |
| 118 | ]; |
| 119 | } |
| 246 | public static function collectInstances(?EntityTypeManagerInterface $entityTypeManager = NULL): array { |
| 247 | $instances = []; |
| 248 | $entityTypeManager = \Drupal::service('entity_type.manager'); |
| 249 | $storage = $entityTypeManager->getStorage('entity_view_display'); |
| 250 | $instance_storage = $entityTypeManager->getStorage('display_builder_instance'); |
| 251 | $entity_query = $entity_storage = []; |
| 252 | |
| 253 | foreach ($storage->loadMultiple() as $display) { |
| 253 | foreach ($storage->loadMultiple() as $display) { |
| 255 | $display_builder = $display->getThirdPartySettings('display_builder'); |
| 256 | |
| 257 | if (!isset($display_builder[DisplayBuildableInterface::OVERRIDE_FIELD_PROPERTY], $display_builder[DisplayBuildableInterface::OVERRIDE_PROFILE_PROPERTY])) { |
| 257 | if (!isset($display_builder[DisplayBuildableInterface::OVERRIDE_FIELD_PROPERTY], $display_builder[DisplayBuildableInterface::OVERRIDE_PROFILE_PROPERTY])) { |
| 257 | if (!isset($display_builder[DisplayBuildableInterface::OVERRIDE_FIELD_PROPERTY], $display_builder[DisplayBuildableInterface::OVERRIDE_PROFILE_PROPERTY])) { |
| 258 | continue; |
| 261 | $entity_type = $display->getTargetEntityTypeId(); |
| 262 | $field_name = $display_builder[DisplayBuildableInterface::OVERRIDE_FIELD_PROPERTY] ?? NULL; |
| 263 | |
| 264 | if (!$field_name) { |
| 265 | continue; |
| 253 | foreach ($storage->loadMultiple() as $display) { |
| 254 | /** @var \Drupal\Core\Entity\Display\EntityViewDisplayInterface $display */ |
| 255 | $display_builder = $display->getThirdPartySettings('display_builder'); |
| 256 | |
| 257 | if (!isset($display_builder[DisplayBuildableInterface::OVERRIDE_FIELD_PROPERTY], $display_builder[DisplayBuildableInterface::OVERRIDE_PROFILE_PROPERTY])) { |
| 258 | continue; |
| 259 | } |
| 260 | |
| 261 | $entity_type = $display->getTargetEntityTypeId(); |
| 262 | $field_name = $display_builder[DisplayBuildableInterface::OVERRIDE_FIELD_PROPERTY] ?? NULL; |
| 263 | |
| 264 | if (!$field_name) { |
| 265 | continue; |
| 266 | } |
| 267 | $entity_storage[$entity_type] ??= $entityTypeManager->getStorage($entity_type); |
| 253 | foreach ($storage->loadMultiple() as $display) { |
| 254 | /** @var \Drupal\Core\Entity\Display\EntityViewDisplayInterface $display */ |
| 255 | $display_builder = $display->getThirdPartySettings('display_builder'); |
| 256 | |
| 257 | if (!isset($display_builder[DisplayBuildableInterface::OVERRIDE_FIELD_PROPERTY], $display_builder[DisplayBuildableInterface::OVERRIDE_PROFILE_PROPERTY])) { |
| 258 | continue; |
| 259 | } |
| 260 | |
| 261 | $entity_type = $display->getTargetEntityTypeId(); |
| 262 | $field_name = $display_builder[DisplayBuildableInterface::OVERRIDE_FIELD_PROPERTY] ?? NULL; |
| 263 | |
| 264 | if (!$field_name) { |
| 265 | continue; |
| 266 | } |
| 267 | $entity_storage[$entity_type] ??= $entityTypeManager->getStorage($entity_type); |
| 268 | $entity_query[$entity_type] ??= $entity_storage[$entity_type]->getQuery()->accessCheck(FALSE); |
| 269 | $instances = \array_merge($instances, self::collectInstancesByField($field_name, $entity_type, $instance_storage, $entity_query[$entity_type])); |
| 270 | } |
| 271 | |
| 272 | return $instances; |
| 273 | } |
| 364 | protected static function collectInstancesByField(string $field_name, string $entity_type, EntityStorageInterface $instance_storage, QueryInterface $entity_query): array { |
| 365 | $instances = []; |
| 366 | $entity_query->exists($field_name); |
| 367 | // QueryInterface::execute() returns an integer for count queries or an |
| 368 | // array of ids. |
| 369 | /** @var array $ids */ |
| 370 | $ids = $entity_query->execute(); |
| 371 | |
| 372 | if (empty($ids)) { |
| 373 | return []; |
| 376 | foreach ($ids as $id) { |
| 376 | foreach ($ids as $id) { |
| 376 | foreach ($ids as $id) { |
| 377 | $instance_id = \sprintf( |
| 378 | '%s%s__%s__%s', |
| 379 | self::getPrefix(), |
| 376 | foreach ($ids as $id) { |
| 377 | $instance_id = \sprintf( |
| 378 | '%s%s__%s__%s', |
| 379 | self::getPrefix(), |
| 380 | $entity_type, |
| 381 | $id, |
| 382 | $field_name, |
| 383 | ); |
| 384 | // We are OK with keeping the null values if the instance entity |
| 385 | // doesn't exists in storage. So the caller can decide to create |
| 386 | // the missing Instance entities. |
| 387 | $instances[$instance_id] = $instance_storage->load($instance_id); |
| 388 | } |
| 389 | |
| 390 | return $instances; |
| 391 | } |
| 430 | return $this->dataConverter ??= \Drupal::service('display_builder_entity_view.builder_data_converter'); |
| 431 | } |
| 420 | return $this->displayBuildableManager ??= \Drupal::service('plugin.manager.display_buildable'); |
| 421 | } |
| 410 | return $this->entityTypeManager ??= \Drupal::service('entity_type.manager'); |
| 411 | } |
| 94 | \assert(\is_string($this->field->getName())); |
| 95 | $entity = $this->field->getEntity(); |
| 96 | $entity_type_id = $this->display->getTargetEntityTypeId(); |
| 97 | $parameters = [ |
| 98 | $entity_type_id => $entity->id(), |
| 99 | 'view_mode_name' => $this->display->getMode(), |
| 100 | ]; |
| 101 | |
| 102 | return Url::fromRoute(\sprintf('entity.%s.display_builder.%s', $entity_type_id, $this->display->getMode()), $parameters); |
| 103 | } |
| 87 | return 'content'; |
| 88 | } |
| 151 | public static function getDisplayUrlFromInstanceId(string $instance_id): Url { |
| 152 | return Url::fromRoute('<front>'); |
| 153 | } |
| 446 | private static function getEntityViewDisplay(string $entity_type_id, string $bundle, string $fieldName): ?DisplayBuilderEntityDisplayInterface { |
| 447 | /** @var \Drupal\display_builder_entity_view\Entity\DisplayBuilderEntityDisplayInterface[] $displays */ |
| 448 | $displays = \Drupal::entityTypeManager()->getStorage('entity_view_display')->loadByProperties([ |
| 449 | 'targetEntityType' => $entity_type_id, |
| 450 | ]); |
| 451 | |
| 452 | foreach ($displays as $display) { |
| 452 | foreach ($displays as $display) { |
| 453 | if ($display instanceof DisplayBuilderOverridableInterface |
| 454 | && $display->getDisplayBuilderOverrideField() === $fieldName |
| 454 | && $display->getDisplayBuilderOverrideField() === $fieldName |
| 455 | && $display->getTargetEntityTypeId() |
| 455 | && $display->getTargetEntityTypeId() |
| 456 | && $display->getTargetBundle() === $bundle |
| 456 | && $display->getTargetBundle() === $bundle |
| 458 | return $display; |
| 452 | foreach ($displays as $display) { |
| 452 | foreach ($displays as $display) { |
| 453 | if ($display instanceof DisplayBuilderOverridableInterface |
| 454 | && $display->getDisplayBuilderOverrideField() === $fieldName |
| 455 | && $display->getTargetEntityTypeId() |
| 456 | && $display->getTargetBundle() === $bundle |
| 457 | ) { |
| 458 | return $display; |
| 459 | } |
| 460 | } |
| 461 | |
| 462 | return NULL; |
| 463 | } |
| 335 | $entity = $this->field->getEntity(); |
| 336 | $bundle = $entity->bundle(); |
| 337 | \assert(\is_string($this->field->getName())); |
| 338 | |
| 339 | $view_mode = $this->display->getMode(); |
| 340 | $contexts = [ |
| 341 | 'entity' => EntityContext::fromEntity($entity), |
| 342 | 'bundle' => new Context(ContextDefinition::create('string'), $bundle), |
| 343 | 'view_mode' => new Context(ContextDefinition::create('string'), $view_mode), |
| 344 | ]; |
| 345 | |
| 346 | return RequirementsContext::addToContext([self::getContextRequirement()], $contexts); |
| 347 | } |
| 294 | $sources = $this->getSources(); |
| 295 | |
| 296 | // 1. Keep the existing override value if existing. |
| 297 | if (\count($sources) > 0) { |
| 298 | return $sources; |
| 305 | if ($this->display->getMode() === 'default') { |
| 306 | $entity = $this->field->getEntity(); |
| 307 | // @see: use Drupal\layout_builder\Plugin\SectionStorage\OverridesSectionStorage::$FIELD_NAME |
| 308 | $field_name = 'layout_builder__layout'; |
| 309 | |
| 310 | if ($entity->hasField($field_name) && !$entity->get($field_name)->isEmpty()) { |
| 310 | if ($entity->hasField($field_name) && !$entity->get($field_name)->isEmpty()) { |
| 310 | if ($entity->hasField($field_name) && !$entity->get($field_name)->isEmpty()) { |
| 311 | $content = $entity->get($field_name)->first()->getValue(); |
| 312 | $this->initialDataSource = 'layout_builder_override'; |
| 313 | |
| 314 | return $this->dataConverter()->convertFromLayoutBuilder($content); |
| 319 | \assert(\is_string($this->field->getName())); |
| 320 | /** @var \Drupal\display_builder\DisplayBuildableInterface $buildable */ |
| 321 | $buildable = $this->displayBuildableManager()->createInstance('entity_view', ['entity' => $this->display]); |
| 322 | |
| 323 | if ($buildable->getProfile() !== NULL) { |
| 324 | $sources = $buildable->getSources(); |
| 325 | } |
| 326 | $this->initialDataSource = 'display_builder'; |
| 326 | $this->initialDataSource = 'display_builder'; |
| 327 | |
| 328 | return $sources; |
| 329 | } |
| 279 | if ($this->initialDataSource === 'display_builder') { |
| 280 | return $this->t('Copy from Entity View Display configuration.'); |
| 283 | if ($this->initialDataSource === 'layout_builder_override') { |
| 284 | return $this->t('Import from Layout Builder override.'); |
| 287 | return $this->t('Initialization from existing content.'); |
| 288 | } |
| 205 | if ($this->field->getEntity()->isNew()) { |
| 206 | return NULL; |
| 209 | $entity = $this->field->getEntity(); |
| 210 | |
| 211 | return \sprintf( |
| 212 | '%s%s__%s__%s', |
| 213 | self::getPrefix(), |
| 214 | $entity->getEntityTypeId(), |
| 215 | $entity->id(), |
| 216 | $this->field->getName() |
| 217 | ); |
| 218 | } |
| 159 | \assert($this->display instanceof DisplayBuilderOverridableInterface); |
| 160 | |
| 161 | return $this->display->getDisplayBuilderOverrideProfile(); |
| 162 | } |
| 168 | return $this->field->getValue(); |
| 169 | } |
| 124 | public static function getUrlFromInstanceId(string $instance_id): Url { |
| 125 | [, $entity_type_id, $entity_id, $field_name] = \explode('__', $instance_id); |
| 126 | |
| 127 | $entity = \Drupal::entityTypeManager()->getStorage($entity_type_id)->load($entity_id); |
| 128 | |
| 129 | if (!$entity) { |
| 130 | return Url::fromRoute('entity.display_builder_instance.collection'); |
| 133 | $display = self::getEntityViewDisplay($entity_type_id, $entity->bundle(), $field_name); |
| 134 | |
| 135 | if (!$display) { |
| 136 | return Url::fromRoute('entity.display_builder_instance.collection'); |
| 139 | $entity_type_id => $entity_id, |
| 140 | 'view_mode_name' => $display->getMode(), |
| 141 | ]; |
| 142 | |
| 143 | $route_name = \sprintf('entity.%s.display_builder.%s', $entity_type_id, $display->getMode()); |
| 144 | |
| 145 | return Url::fromRoute($route_name, $params); |
| 146 | } |
| 175 | $data = $this->getInstance()->getCurrentState(); |
| 176 | $entity = $this->field->getEntity(); |
| 177 | |
| 178 | if ($entity instanceof ContentEntityInterface) { |
| 179 | $this->setRevision($entity); |
| 180 | } |
| 181 | $entity->save(); |
| 181 | $entity->save(); |
| 182 | $this->field->setValue($data); |
| 183 | $this->field->getEntity()->save(); |
| 184 | } |
| 226 | public function setRevision(ContentEntityInterface $entity): void { |
| 227 | $bundle = $entity->getBundleEntity(); |
| 228 | |
| 229 | if ($bundle instanceof RevisionableEntityBundleInterface |
| 230 | && !$bundle->shouldCreateNewRevision() |
| 230 | && !$bundle->shouldCreateNewRevision() |
| 232 | return; |
| 235 | $entity->setNewRevision(); |
| 236 | |
| 237 | if ($entity instanceof RevisionLogInterface) { |
| 238 | $entity->setRevisionLogMessage($this->t('Updated using Display Builder.')->render()); |
| 239 | $entity->setRevisionCreationTime($this->time()->getCurrentTime()); |
| 240 | } |
| 241 | } |
| 241 | } |
| 400 | return $this->time ??= \Drupal::service('datetime.time'); |
| 401 | } |