diff --git a/README.md b/README.md index 74f3f3272fd02f1ece0d0f8d98db899af6bdc52b..8d399502a4f71ec678612b01d565f457e6619d37 100644 --- a/README.md +++ b/README.md @@ -66,10 +66,12 @@ elements, the entity is rendered into custom-elements this way: * When there is a custom-elements display-config for the entity, it's applied. The display is configured to use custom-element field formatter plugins to render individual fields. See "Custom element displays" for more details. -* Else it falls back to rendering via custom elements processors, which makes - it possible to adjust the output via processor services. This was the default - rendering strategy in version 2.x and is what the "auto" CE-field formatter - does. See "Automatic rendering with processors" for more information below. +* The display config has a config-only option "forceAutoProcessing" which + disables the per-field component configuration. Instead, it applies automatic + processing via processor services to the entity-level, as it was default in + version 2.x and is the same as the "auto" CE-field formatter does on field + level. For backwards compatibility with 2.x, this config options may be + enabled. See "Automatic rendering with processors" for more information below. The custom elements UI sub-module may be enabled to configure the custom-elements display config via UI. @@ -123,8 +125,8 @@ displays are applied, when an entity is being rendered: * If an entity is rendered by Drupal's standard pipeline (e.g. as part of a list of node teasers), custom elements only becomes active if Drupal's - regular display for the specified (or default) view mode has the "Use Custom - Element Display" option to take-over rendering enabled. + regular display for the specified (or default) view mode has the "Force + rendering as custom element" enabled. * When not using layout builder, custom elements renders a certain entity using a display specific to the entity type/bundle, similarly to Drupal's standard @@ -133,11 +135,14 @@ displays are applied, when an entity is being rendered: mode, then it is used. * Otherwise, if a 'default' custom elements display is enabled, then it is used. - * Otherwise, the automatic rendering via custom element processors kicks in + * If the custom element display has "forceAutoProcessing" enabled, the + automatic rendering via custom element processors kicks in as it was default in 2.x: For that Drupal's regular display (for the specified view mode or default) is taken as a basis: all enabled fields are rendered, with a processor being automatically selected for each field (i.e. like the "Auto" option in a custom elements display). + * When no custom element entity-display config is available, a config with + reasonable defaults is auto-generated and applied. ## Upgrade from 2.x @@ -155,12 +160,10 @@ because of their very small install base.) third_party_settings.custom_elements.enabled = true.) -TODO: By default custom-element display config is generated for all entity -bundles, thus rendering is changed to follow this config. If that's not desired, -regular displays can be still used by simply deleting this config entries for -the respective entity-view. That way, the automatic-rendering via processors -is applied again as it was default in 2.x. - +TODO: By default custom-element display config is applied. If that's not +desired, regular displays can be still used by enabling "forceAutoProcessing" +in the respective entity-ce-display. That way, the automatic-rendering via +processors is applied again as it was default in 2.x. ## Credits diff --git a/config/schema/custom_elements.schema.yml b/config/schema/custom_elements.schema.yml index e45d631960b08ee41c2cfc1510da0cfd231d4409..8a2488d361fd6e2f8637f9bb28e54c7a099a9255 100644 --- a/config/schema/custom_elements.schema.yml +++ b/config/schema/custom_elements.schema.yml @@ -22,6 +22,9 @@ custom_elements.entity_ce_display.*.*.*: mode: type: string label: 'View or form mode machine name' + forceAutoProcessing: + type: bool + label: 'When true, ignore this config and fall back to using processors' customElementName: type: string label: 'Custom element name' diff --git a/custom_elements.install b/custom_elements.install index 13cc2fae6cfd3b6795df2dbb70e2abcce1f59c06..9d94858ea036533bf0ac01e72c2fd322a8639fa5 100644 --- a/custom_elements.install +++ b/custom_elements.install @@ -12,11 +12,6 @@ function custom_elements_install() { // Make sure our module comes after layout_builder. // @see custom_elements_module_implements_alter(). module_set_weight('custom_elements', 10); - - // Auto-generate default CE display config per entity type. - /** @var \Drupal\custom_elements\CustomElementsConfigGenerator $config_generator */ - $config_generator = \Drupal::service('custom_elements.default_config_generator'); - $config_generator->initializeConfig(); } /** diff --git a/custom_elements.module b/custom_elements.module index ef75e2de7311dbd926adabcff6ac386b4865ce0f..3350772e2b84f45ff650f8a2e73669bcf31a0a42 100644 --- a/custom_elements.module +++ b/custom_elements.module @@ -6,7 +6,6 @@ */ use Drupal\Component\Render\MarkupInterface; -use Drupal\Core\Config\Entity\ConfigEntityBundleBase; use Drupal\Core\Entity\Display\EntityViewDisplayInterface; use Drupal\Core\Entity\EntityInterface; use Drupal\Core\Render\BubbleableMetadata; @@ -202,15 +201,3 @@ function custom_elements_entity_view_alter(array &$build, EntityInterface $entit $build['#theme'] = 'custom_element'; } } - -/** - * Implements hook_entity_insert(). - */ -function custom_elements_entity_insert(EntityInterface $entity) { - if ($entity instanceof ConfigEntityBundleBase) { - $entity_type_id = $entity->getEntityType()->getBundleOf(); - $bundle = $entity->id(); - \Drupal::service('custom_elements.default_config_generator') - ->createBundleDefaultConfig($entity_type_id, $bundle); - } -} diff --git a/custom_elements.services.yml b/custom_elements.services.yml index c1340d148355c91c5dc684f0698157a72db1d174..8d4369172712b1ecd293553d24dbdc65b0f3bccb 100644 --- a/custom_elements.services.yml +++ b/custom_elements.services.yml @@ -4,9 +4,6 @@ services: arguments: ['@module_handler', '@entity.repository', '@entity_type.manager'] tags: - { name: service_collector, tag: custom_elements_processor, call: addProcessor } - custom_elements.default_config_generator: - class: Drupal\custom_elements\CustomElementsConfigGenerator - arguments: ['@entity_type.manager', '@entity_type.bundle.info'] custom_elements.plugin.manager.field.custom_element_formatter: class: Drupal\custom_elements\CustomElementsFieldFormatterPluginManager arguments: ['@container.namespaces', '@cache.discovery', '@module_handler', '@plugin.manager.field.field_type'] diff --git a/src/CustomElementGenerator.php b/src/CustomElementGenerator.php index 6b73d84cdcce57ea01e269aa1c1d703137715af7..2191b54ca73a58ec7b0d9518217387208e244e6c 100644 --- a/src/CustomElementGenerator.php +++ b/src/CustomElementGenerator.php @@ -10,6 +10,7 @@ use Drupal\Core\Entity\EntityTypeManagerInterface; use Drupal\Core\Extension\ModuleHandlerInterface; use Drupal\Core\Render\BubbleableMetadata; use Drupal\Core\Render\Element; +use Drupal\Core\Session\AccountInterface; use Drupal\custom_elements\Entity\EntityCeDisplayInterface; use Drupal\custom_elements\Processor\CustomElementProcessorInterface; @@ -107,6 +108,22 @@ class CustomElementGenerator { return $this->sortedProcessors; } + /** + * Gets the defaults to apply for the given entity type, bundle and view mode. + * + * @return \Drupal\custom_elements\CustomElement + * A new custom element with the defaults set. + */ + public function getViewModeDefaults(string $entityType, string $bundle, string $viewMode) { + $bundle_key = $this->entityTypeManager->getDefinition($entityType) + ->getKey('bundle'); + /** @var \Drupal\Core\Entity\ContentEntityInterface $entity */ + $entity = $this->entityTypeManager->getStorage($entityType) + ->create($bundle_key && $bundle ? [$bundle_key => $bundle] : []); + + return $this->getEntityDefaults($entity, $viewMode); + } + /** * Gets the defaults to apply for the given entity and view mode. * @@ -168,7 +185,7 @@ class CustomElementGenerator { // Get desired entity translation. $entity = $this->entityRepository->getTranslationFromContext($entity, $langcode); $custom_element = $this->getEntityDefaults($entity, $viewMode); - $this->addEntity($entity, $custom_element, $viewMode); + $this->buildEntityContent($entity, $custom_element, $viewMode); $this->moduleHandler->alter('custom_element_entity', $custom_element, $entity, $viewMode); return $custom_element; } @@ -181,7 +198,7 @@ class CustomElementGenerator { * @param \Drupal\custom_elements\CustomElement $custom_element * The custom element to which to add it. * @param string $viewMode - * The current view-mode. + * The current view mode. * @param string $key * (Optional) Name to use for adding data into (a slot / attribute in) the * custom element. Processors can use this how they see fit. Most use it @@ -198,7 +215,7 @@ class CustomElementGenerator { } /** - * Adds an entity's content to the custom element. + * Builds an entity's content as custom element. * * @param \Drupal\Core\Entity\ContentEntityInterface $entity * The entity. @@ -207,16 +224,16 @@ class CustomElementGenerator { * @param string $viewMode * The current view mode. */ - protected function addEntity(ContentEntityInterface $entity, CustomElement $element, $viewMode) { + public function buildEntityContent(ContentEntityInterface $entity, CustomElement $element, $viewMode) { // Check the "Use layout builder" setting on the original (non-CE) display. $displays = EntityViewDisplay::collectRenderDisplays([$entity], $viewMode); $display = reset($displays); if ($display->getThirdPartySetting('layout_builder', 'enabled')) { // Skip processing of the fields and let the layout builder render it all. - $this->addLayoutBuilderContent($entity, $element, $display); + $this->buildLayoutBuilderContent($entity, $element, $display); } - elseif ($entity_ce_display = $this->getCeRenderDisplay($entity->getEntityTypeId(), $entity->bundle(), $viewMode)) { - $this->addCeDisplayContent($entity, $element, $entity_ce_display); + elseif ($entity_ce_display = $this->getEntityCeDisplay($entity->getEntityTypeId(), $entity->bundle(), $viewMode)) { + $this->buildCeDisplay($entity, $element, $entity_ce_display, $viewMode); } else { // When no configuration is there or enabled, fall back to invoking @@ -226,7 +243,7 @@ class CustomElementGenerator { } /** - * Adds an entity's content to the custom element using its layout's regions. + * Builds an entity's content a custom element using its layout's regions. * * @param \Drupal\Core\Entity\EntityInterface $entity * The entity. @@ -235,7 +252,7 @@ class CustomElementGenerator { * @param \Drupal\custom_elements\CustomElementsLayoutBuilderEntityViewDisplay $display * The view display of the current view mode. */ - protected function addLayoutBuilderContent(EntityInterface $entity, CustomElement $custom_element, CustomElementsLayoutBuilderEntityViewDisplay $display) { + protected function buildLayoutBuilderContent(EntityInterface $entity, CustomElement $custom_element, CustomElementsLayoutBuilderEntityViewDisplay $display) { $section_elements = []; $build = $display->buildLayoutSections($entity); @@ -262,7 +279,9 @@ class CustomElementGenerator { } /** - * Adds an entity's content to the custom element using a CE display. + * Builds an entity's content as custom element using a CE display. + * + * Not accessible or empty fields are not built. * * @param \Drupal\Core\Entity\EntityInterface $entity * The entity. @@ -270,12 +289,26 @@ class CustomElementGenerator { * The custom element. * @param \Drupal\custom_elements\Entity\EntityCeDisplayInterface $display * Entity custom element display entity. + * @param string $view_mode + * The view mode being built. + * @param \Drupal\Core\Session\AccountInterface|null $account + * (optional) The user for which to check access, or NULL to check access + * for the current user. Defaults to NULL. */ - protected function addCeDisplayContent(EntityInterface $entity, CustomElement $custom_element, EntityCeDisplayInterface $display) { - // Forward custom element name if configured. + public function buildCeDisplay(EntityInterface $entity, CustomElement $custom_element, EntityCeDisplayInterface $display, string $view_mode, AccountInterface $account = NULL) { + $display->setOriginalMode($view_mode); $custom_element->setTag($display->getCustomElementName()); + + // When configured, only do auto-processing on entity-level and skip the + // config of individual components. This is the same behaviour as it was + // default in 2.x. + if ($display->getForceAutoProcessing()) { + $this->process($entity, $custom_element, $view_mode); + return; + } + // Else render using the individual display components. foreach ($display->getComponents() as $field_name => $display_component) { - if ($this->fieldIsAccessible($entity, $field_name, $custom_element)) { + if ($this->fieldIsAccessible($entity, $field_name, $custom_element, $account)) { if ($formatter = $display->getRenderer($field_name)) { // @todo Move prepareBuild to prepareView phase. $formatter->prepareBuild([$entity->get($field_name)]); @@ -286,32 +319,43 @@ class CustomElementGenerator { } /** - * Gets the Custom Elements display that applies to the bundle/view mode. + * Gets a Custom Elements display for the entity type, bundle and view mode. * - * @param string $entityType + * @param string $entityTypeId * The entity type. * @param string $bundle * The bundle. * @param string $viewMode * The view mode. * - * @return \Drupal\custom_elements\Entity\EntityCeDisplayInterface|null - * The applicable CE display, or NULL if none exist / all are disabled. + * @return \Drupal\custom_elements\Entity\EntityCeDisplayInterface + * The applicable CE display. If none exists, a suiting display is + * auto-generated. Disabled entity-ce-display objects are considered not + * existing and won't be returned. */ - protected function getCeRenderDisplay(string $entityType, string $bundle, string $viewMode) { - $display_id = "$entityType.$bundle.$viewMode"; + public function getEntityCeDisplay(string $entityTypeId, string $bundle, string $viewMode) { + /** @var \Drupal\custom_elements\Entity\EntityCeDisplayInterface $entity_ce_display */ $entity_ce_display = $this->entityTypeManager ->getStorage('entity_ce_display') - ->load($display_id); - // Fallback to default view mode. - if (empty($entity_ce_display) || !$entity_ce_display->status()) { - $default_id = "$entityType.$bundle.default"; + ->load("$entityTypeId.$bundle.$viewMode"); + + if (!$entity_ce_display || !$entity_ce_display->status()) { + // Fall back to default view mode. If it's not in use, return NULL. $entity_ce_display = $this->entityTypeManager ->getStorage('entity_ce_display') - ->load($default_id); + ->load("$entityTypeId.$bundle.default"); + if (!$entity_ce_display || !$entity_ce_display->status()) { + // No usable default, so auto-create a suiting entity. + $entity_ce_display = $this->entityTypeManager->getStorage('entity_ce_display')->create([ + 'id' => "$entityTypeId.$bundle.$viewMode", + 'targetEntityType' => $entityTypeId, + 'bundle' => $bundle, + 'mode' => $viewMode, + 'status' => TRUE, + ]); + } } - - return $entity_ce_display && $entity_ce_display->status() ? $entity_ce_display : NULL; + return $entity_ce_display; } } diff --git a/src/CustomElementGeneratorTrait.php b/src/CustomElementGeneratorTrait.php index 6c1aef1cd3921b638f8278515d5f4113daf0b27f..0cd7f6d529843449b170f724a2dc17c2c57cf943 100644 --- a/src/CustomElementGeneratorTrait.php +++ b/src/CustomElementGeneratorTrait.php @@ -22,7 +22,7 @@ trait CustomElementGeneratorTrait { * * @return $this */ - public function setCustomElementGenerat(CustomElementGenerator $custom_element_generator) { + public function setCustomElementGenerator(CustomElementGenerator $custom_element_generator) { $this->customElementGenerator = $custom_element_generator; return $this; } diff --git a/src/CustomElementsConfigGenerator.php b/src/CustomElementsConfigGenerator.php deleted file mode 100644 index f3aa686211dcfb373cbc36c67eccec39950063ba..0000000000000000000000000000000000000000 --- a/src/CustomElementsConfigGenerator.php +++ /dev/null @@ -1,188 +0,0 @@ -<?php - -declare(strict_types=1); - -namespace Drupal\custom_elements; - -use Drupal\Core\Entity\ContentEntityTypeInterface; -use Drupal\Core\Entity\EntityTypeBundleInfoInterface; -use Drupal\Core\Entity\EntityTypeInterface; -use Drupal\Core\Entity\EntityTypeManagerInterface; - -/** - * Custom elements default config generator service. - */ -class CustomElementsConfigGenerator implements CustomElementsConfigGeneratorInterface { - - /** - * EntityTypeManager service. - * - * @var \Drupal\Core\Entity\EntityTypeManagerInterface - */ - protected EntityTypeManagerInterface $entityTypeManager; - - /** - * EntityTypeBundleInfo service. - * - * @var \Drupal\Core\Entity\EntityTypeBundleInfoInterface - */ - protected EntityTypeBundleInfoInterface $entityTypeBundleInfo; - - /** - * CustomElementsConfigGenerator constructor. - * - * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager - * EntityTypeManager service. - * @param \Drupal\Core\Entity\EntityTypeBundleInfoInterface $bundle_info - * EntityTypeBundleInfo service. - */ - public function __construct(EntityTypeManagerInterface $entity_type_manager, EntityTypeBundleInfoInterface $bundle_info) { - $this->entityTypeManager = $entity_type_manager; - $this->entityTypeBundleInfo = $bundle_info; - } - - /** - * {@inheritdoc} - */ - public function createBundleDefaultConfig(string $entity_type_id, string $bundle):void { - if (!$this->configExists($entity_type_id, $bundle)) { - $id = $entity_type_id . '.' . $bundle . '.default'; - $element_name = $entity_type_id . '-' . $bundle . '-default'; - - $config = $this->entityTypeManager->getStorage('entity_ce_display')->create([ - 'id' => $id, - 'targetEntityType' => $entity_type_id, - 'bundle' => $bundle, - 'customElementName' => $element_name, - 'mode' => 'default', - 'status' => TRUE, - ]); - // Note that the entity class takes care of setting suiting defaults. - // @see \Drupal\custom_elements\Entity\EntityCeDisplay::postCreate() - $config->save(); - } - } - - /** - * {@inheritdoc} - */ - public function initializeConfig():void { - $entities = $this->getContentEntityTypes(); - foreach ($entities as $entity_type) { - if ($this->isContentEntityType($entity_type)) { - foreach ($this->getEntityBundles($entity_type) as $bundle) { - $this->createBundleDefaultConfig($entity_type->id(), $bundle); - } - } - } - } - - /** - * Gets entity bundles. - * - * @param \Drupal\Core\Entity\EntityTypeInterface $entity_type - * Entity type. - * - * @return array - * The selected bundles, empty array if there is no bundle at all. - */ - protected function getEntityBundles(EntityTypeInterface $entity_type):array { - $bundle_names = []; - if (!$this->hasBundles($entity_type)) { - return $bundle_names; - } - - $bundles = $this->entityTypeBundleInfo->getBundleInfo($entity_type->id()); - - foreach ($bundles as $key => $bundle) { - $bundle_names[] = $key; - } - - return $bundle_names; - } - - /** - * Checks that the config is already present. - * - * @param string $entity_type_id - * Entity type id. - * @param string $bundle - * Entity bundle. - * - * @return bool - * True if config is already present, false otherwise. - * - * @throws \Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException - * @throws \Drupal\Component\Plugin\Exception\PluginNotFoundException - */ - protected function configExists(string $entity_type_id, string $bundle):bool { - $id = $entity_type_id . '.' . $bundle . '.default'; - $config = $this->entityTypeManager->getStorage('entity_ce_display')->load($id); - return $config ? TRUE : FALSE; - } - - /** - * Checks that given entity type is a content entity. - * - * @param \Drupal\Core\Entity\EntityTypeInterface $entity_type - * Entity type. - * - * @return bool - * True if it's a content entity, false otherwise. - */ - protected function isContentEntityType(EntityTypeInterface $entity_type):bool { - if (!$entity_type instanceof ContentEntityTypeInterface) { - return FALSE; - } - - return TRUE; - } - - /** - * Gets all content entity types. - * - * @return array - * Collected content entities. - */ - protected function getContentEntityTypes():array { - $entity_types = $this->entityTypeManager->getDefinitions(); - - $entities = []; - foreach ($entity_types as $entity_type) { - if (!$this->hasBundles($entity_type)) { - continue; - } - - if (!$this->isContentEntityType($entity_type)) { - continue; - } - $entities[] = $entity_type; - - } - - return $entities; - } - - /** - * Checks that given entity type has bundles. - * - * @param \Drupal\Core\Entity\EntityTypeInterface $entity_type - * Entity type. - * - * @return bool - * True if bundles are available, false otherwise. - */ - private function hasBundles(EntityTypeInterface $entity_type): bool { - $bundles = $this->entityTypeBundleInfo->getBundleInfo($entity_type->id()); - // The outer array is keyed by the entity type name - // if the entity does not have bundles so no work on it - // there is no good check for determining entity's use of bundles. - // https://www.drupal.org/project/drupal/issues/3279574 - if (array_key_exists($entity_type->id(), $bundles)) { - return FALSE; - } - - return TRUE; - } - -} diff --git a/src/CustomElementsConfigGeneratorInterface.php b/src/CustomElementsConfigGeneratorInterface.php deleted file mode 100644 index 042ca536d733c20b7698f397c4bb9b3b0d4c7839..0000000000000000000000000000000000000000 --- a/src/CustomElementsConfigGeneratorInterface.php +++ /dev/null @@ -1,30 +0,0 @@ -<?php - -namespace Drupal\custom_elements; - -/** - * Custom Elements default config auto generator. - */ -interface CustomElementsConfigGeneratorInterface { - - /** - * Creates default CE display for a certain entity type and bundle. - * - * @param string $entity_type_id - * Entity type ID. - * @param string $bundle - * Bundle name. - */ - public function createBundleDefaultConfig(string $entity_type_id, string $bundle): void; - - /** - * Auto-generates default CE display config per entity type. - * - * It only includes content entities. - * - * Content entities usually have at least one default bundle - * if a bundle is missing, the content entity type is excluded. - */ - public function initializeConfig(): void; - -} diff --git a/src/CustomElementsFieldFormatterBase.php b/src/CustomElementsFieldFormatterBase.php index 31bf175dd35e77acf78e19b3f155e14410bfab72..0bc3546ebb8cd40b9c9ef8cebf1e16735e1d379b 100644 --- a/src/CustomElementsFieldFormatterBase.php +++ b/src/CustomElementsFieldFormatterBase.php @@ -101,13 +101,6 @@ abstract class CustomElementsFieldFormatterBase extends PluginBase implements Cu return $this->configuration['name'] ?? ''; } - /** - * Gets the display component's configured weight. - */ - protected function getComponentWeight() { - return $this->configuration['weight'] ?? 0; - } - /** * Checks that item is a slot or not. */ diff --git a/src/Entity/EntityCeDisplay.php b/src/Entity/EntityCeDisplay.php index e8f8ddb9e959cf0addfc3b3d7661beab034a406c..bc0523637d273da2b58de6711584dd9ead5740a4 100644 --- a/src/Entity/EntityCeDisplay.php +++ b/src/Entity/EntityCeDisplay.php @@ -31,6 +31,7 @@ use Drupal\custom_elements\CustomElementGenerator; * "bundle", * "mode", * "customElementName", + * "forceAutoProcessing", * "content", * "hidden", * } @@ -50,6 +51,13 @@ class EntityCeDisplay extends EntityDisplayBase implements EntityCeDisplayInterf */ protected $customElementName; + /** + * Status for 'ignore this config / fall back to using processors'. + * + * @var bool + */ + protected $forceAutoProcessing = FALSE; + /** * Custom elements generator. * @@ -68,26 +76,21 @@ class EntityCeDisplay extends EntityDisplayBase implements EntityCeDisplayInterf // Enable components options as done in the regular entity_view_display. $entity_view_display = \Drupal::service('entity_display.repository') ->getViewDisplay($this->targetEntityType, $this->bundle, $this->originalMode); - $bundle_key = \Drupal::entityTypeManager() - ->getDefinition($this->targetEntityType) - ->getKey('bundle'); - /** @var \Drupal\Core\Entity\ContentEntityInterface $entity */ - $entity = \Drupal::entityTypeManager()->getStorage($this->targetEntityType) - ->create($bundle_key && $this->bundle ? [$bundle_key => $this->bundle] : []); - - $custom_element = $this->ceGenerator->getEntityDefaults($entity, $this->originalMode); + $field_definitions = \Drupal::service('entity_field.manager')->getFieldDefinitions($this->targetEntityType, $this->bundle); + + $custom_element = $this->ceGenerator->getViewModeDefaults($this->targetEntityType, $this->bundle, $this->originalMode); $this->setCustomElementName($custom_element->getPrefixedTag()); // @todo Add support for statically set values. // Enable every component with "auto" that is enabled in the display. foreach ($entity_view_display->getComponents() as $name => $component) { // Ignore extra-fields. - if ($field_definition = $entity->getFieldDefinition($name)) { + if (isset($field_definitions[$name])) { $this->setComponent($name, [ 'name' => $name, 'formatter' => 'auto', 'weight' => $component['weight'], 'region' => 'content', - 'is_slot' => str_starts_with($field_definition->getType(), 'text') ? 1 : 0, + 'is_slot' => str_starts_with($field_definitions[$name]->getType(), 'text') ? 1 : 0, ]); } } @@ -102,6 +105,29 @@ class EntityCeDisplay extends EntityDisplayBase implements EntityCeDisplayInterf parent::__construct($values, $entity_type); } + /** + * {@inheritdoc} + */ + public function setOriginalMode(string $viewMode): self { + $this->originalMode = $viewMode; + return $this; + } + + /** + * {@inheritdoc} + */ + public function getForceAutoProcessing(): bool { + return !empty($this->forceAutoProcessing); + } + + /** + * {@inheritdoc} + */ + public function setForceAutoProcessing(bool $status): self { + $this->forceAutoProcessing = $status; + return $this; + } + /** * {@inheritdoc} */ diff --git a/src/Entity/EntityCeDisplayInterface.php b/src/Entity/EntityCeDisplayInterface.php index 09fd48ea2916d11f612b80f5a33c93ddb874c8eb..4ef7e2d61f9a95c3fb9a318548a80588759822a9 100644 --- a/src/Entity/EntityCeDisplayInterface.php +++ b/src/Entity/EntityCeDisplayInterface.php @@ -9,6 +9,30 @@ use Drupal\Core\Entity\Display\EntityDisplayInterface; */ interface EntityCeDisplayInterface extends EntityDisplayInterface { + /** + * Gets the flag to force-enable auto processing for the entity. + * + * When enabled, the component options of individual fields are ignored. + * Instead, automatic processing using CE-processors is applied on entity + * level, such that processors can take complete control on how to process + * an entity. This is the same behaviour as it was default in 2.x and may + * be used for backwards-compatibility. + * + * @return bool + * Whether auto-processing is forced. + */ + public function getForceAutoProcessing(): bool; + + /** + * Sets the flag to force-enable auto processing for the entity. + * + * @param bool $status + * The status. + * + * @return $this + */ + public function setForceAutoProcessing(bool $status): self; + /** * Gets the configured custom element name. * @@ -35,4 +59,11 @@ interface EntityCeDisplayInterface extends EntityDisplayInterface { */ public function getRenderer($field_name); + /** + * Sets the 'original' view mode. + * + * Only to be used when 'rendering' a default CE display. + */ + public function setOriginalMode(string $viewMode): self; + } diff --git a/src/Processor/DefaultContentEntityProcessor.php b/src/Processor/DefaultContentEntityProcessor.php index 30ac7eab2ee35dfcb633bdde8bcf0d15911788ea..412eaa4424909d588a441dc469c65702f24a0d79 100644 --- a/src/Processor/DefaultContentEntityProcessor.php +++ b/src/Processor/DefaultContentEntityProcessor.php @@ -5,7 +5,6 @@ namespace Drupal\custom_elements\Processor; use Drupal\Core\Entity\ContentEntityInterface; use Drupal\Core\Entity\Entity\EntityViewDisplay; use Drupal\custom_elements\CustomElement; -use Drupal\custom_elements\CustomElementGenerator; use Drupal\custom_elements\CustomElementGeneratorTrait; use Drupal\custom_elements\CustomElementsBlockRenderHelperTrait; use Drupal\custom_elements\CustomElementsProcessorFieldUtilsTrait; @@ -19,16 +18,6 @@ class DefaultContentEntityProcessor implements CustomElementProcessorWithKeyInte use CustomElementsBlockRenderHelperTrait; use CustomElementsProcessorFieldUtilsTrait; - /** - * DefaultContentEntityProcessor constructor. - * - * @param \Drupal\custom_elements\CustomElementGenerator $custom_elements_generator - * The custom elements generator. - */ - public function __construct(CustomElementGenerator $custom_elements_generator) { - $this->setCustomElementGenerat($custom_elements_generator); - } - /** * {@inheritdoc} */ diff --git a/tests/src/Functional/CustomElementsRenderMarkupTest.php b/tests/src/Functional/CustomElementsRenderMarkupTest.php index 59c1602517edc6ef70dde3d645c00e3a992ca986..ee3c7d366d519144d19126acb86376e2a9b18426 100644 --- a/tests/src/Functional/CustomElementsRenderMarkupTest.php +++ b/tests/src/Functional/CustomElementsRenderMarkupTest.php @@ -65,11 +65,6 @@ class CustomElementsRenderMarkupTest extends BrowserTestBase { parent::setUp(); $this->ceDisplayStorage = \Drupal::service('entity_type.manager')->getStorage('entity_ce_display'); - // @todo Somehow the configuration is not correctly creating during - // module install of the thunder-submodule. Fix that at #3445132. - \Drupal::service('custom_elements.default_config_generator') - ->initializeConfig(); - $this->node = Node::create([ 'type' => 'article', 'title' => 'test', @@ -80,7 +75,7 @@ class CustomElementsRenderMarkupTest extends BrowserTestBase { ]); $this->image->save(); - $ce_display = $this->ceDisplayStorage->load('node.article.default'); + $ce_display = $this->getCustomElementGenerator()->getEntityCeDisplay('node', 'article', 'default'); $ce_display->setComponent('field_paragraphs', [ 'name' => 'paragraphs', 'formatter' => 'auto', @@ -94,7 +89,7 @@ class CustomElementsRenderMarkupTest extends BrowserTestBase { $ce_display->save(); /** @var \Drupal\custom_elements\Entity\EntityCeDisplayInterface $ce_display */ - $ce_display = $this->ceDisplayStorage->load('paragraph.text.default'); + $ce_display = $this->getCustomElementGenerator()->getEntityCeDisplay('paragraph', 'text', 'default'); $ce_display ->setCustomElementName('pg-text') ->setComponent('field_text', [ @@ -200,7 +195,7 @@ EOF; */ public function doTestQuoteParagraph() { /** @var \Drupal\custom_elements\Entity\EntityCeDisplayInterface $ce_display */ - $ce_display = $this->ceDisplayStorage->load('paragraph.quote.default'); + $ce_display = $this->getCustomElementGenerator()->getEntityCeDisplay('paragraph', 'quote', 'default'); $ce_display ->setCustomElementName('pg-quote') ->setComponent('field_text', [ @@ -234,7 +229,7 @@ EOF; */ public function doTestLinkParagraph($expected_markup = '') { /** @var \Drupal\custom_elements\Entity\EntityCeDisplayInterface $ce_display */ - $ce_display = $this->ceDisplayStorage->load('paragraph.link.default'); + $ce_display = $this->getCustomElementGenerator()->getEntityCeDisplay('paragraph', 'link', 'default'); $ce_display ->setCustomElementName('pg-link') ->setComponent('field_link', [ @@ -278,7 +273,7 @@ EOF; public function doTestTwitterParagraph($expected_markup = '') { // @todo Make use of nested entity-reference configs to make this nicer. /** @var \Drupal\custom_elements\Entity\EntityCeDisplayInterface $ce_display */ - $ce_display = $this->ceDisplayStorage->load('paragraph.twitter.default'); + $ce_display = $this->getCustomElementGenerator()->getEntityCeDisplay('paragraph', 'twitter', 'default'); $ce_display ->setCustomElementName('pg-twitter') ->setComponent('field_media', [ @@ -315,11 +310,12 @@ EOF; * @covers \Drupal\custom_elements_thunder\Processor\ParagraphVideoProcessor */ public function doTestVideoParagraph($expected_markup = '') { - $ce_display = $this->ceDisplayStorage->load('paragraph.video.default'); + $ce_display = $this->getCustomElementGenerator()->getEntityCeDisplay('paragraph', 'video', 'default'); // @todo Make this possible with config. Until that, delete to let // processors take over. // @todo Keep a copy of this to test processors taking overs also. - $ce_display->delete(); + $ce_display->setForceAutoProcessing(TRUE) + ->save(); $paragraph = Paragraph::create([ 'type' => 'video', @@ -348,7 +344,7 @@ EOF; public function doTestImageParagraph() { // @todo Make use of nested entity-reference configs to make this nicer. /** @var \Drupal\custom_elements\Entity\EntityCeDisplayInterface $ce_display */ - $ce_display = $this->ceDisplayStorage->load('paragraph.image.default'); + $ce_display = $this->getCustomElementGenerator()->getEntityCeDisplay('paragraph', 'image', 'default'); $ce_display ->setCustomElementName('pg-image') ->setComponent('field_image', [ @@ -388,10 +384,11 @@ EOF; * @covers \Drupal\custom_elements_thunder\Processor\ParagraphGalleryProcessor */ public function doTestGalleryParagraph($expected_markup = '') { - $ce_display = $this->ceDisplayStorage->load('paragraph.gallery.default'); + $ce_display = $this->getCustomElementGenerator()->getEntityCeDisplay('paragraph', 'gallery', 'default'); // @todo Make this possible via some plugin. Until that, delete to let // processors take over. - $ce_display->delete(); + $ce_display->setForceAutoProcessing(TRUE) + ->save(); $media_image_data = [ 'bundle' => 'image', @@ -463,7 +460,7 @@ EOF; * @covers \Drupal\custom_elements\Processor\DefaultContentEntityProcessor */ public function testNodeRendering() { - $ce_display = $this->ceDisplayStorage->load('media.image.default'); + $ce_display = $this->getCustomElementGenerator()->getEntityCeDisplay('media', 'image', 'default'); $ce_display->setComponent('field_image', [ 'name' => 'image', 'formatter' => 'auto', @@ -501,20 +498,17 @@ EOF; // The results should not contain unpublished media. $expected_markup = <<<EOF <node type="article" view-mode="full" created="{$this->node->created->value}" title="test" uid="0"> - <pg-text type="text" view-mode="default" slot="paragraphs"> + <pg-text type="text" view-mode="full" slot="paragraphs"> <p>Some example text</p> </pg-text> </node> EOF; $this->assertMarkupEquals($expected_markup, $markup); - // Test behavior with missing CE display (situation post-upgrade-from-v2). + // Test behavior with auto-processing being forced (BC-mode). /** @var \Drupal\custom_elements\Entity\EntityCeDisplayInterface $ce_display */ $ce_display = $this->ceDisplayStorage->load('node.article.default'); - $ce_display->setStatus(FALSE); - $ce_display->save(); - $ce_display = $this->ceDisplayStorage->load('media.image.default'); - $ce_display->setStatus(FALSE); + $ce_display->setForceAutoProcessing(TRUE); $ce_display->save(); $custom_element = $this->getCustomElementGenerator() @@ -541,29 +535,26 @@ EOF; $this->node->field_teaser_media->setValue($media2); $this->node->field_paragraphs->setValue([]); - // First test situation post-upgrade-from-v2, then newer situation. + // First test with auto-processing. $custom_element = $this->getCustomElementGenerator() ->generate($this->node, 'full'); $markup = $this->renderCustomElement($custom_element); // The results should contain published media. $expected_markup = <<<EOF <node type="article" view-mode="full" uid="0" title="test" created="{$this->node->created->value}"> - <drupal-media type="image" view-mode="full" slot="teaser-media"> + <drupal-drupal-media type="image" view-mode="full" slot="teaser-media"> <field-image slot="image"> <div> <img loading="lazy" src="{$this->image->uri->url}" width="88" height="100" /> </div> </field-image> - </drupal-media> + </drupal-drupal-media> </node> EOF; $this->assertMarkupEquals($expected_markup, $markup); $ce_display = $this->ceDisplayStorage->load('node.article.default'); - $ce_display->setStatus(TRUE); - $ce_display->save(); - $ce_display = $this->ceDisplayStorage->load('media.image.default'); - $ce_display->setStatus(TRUE); + $ce_display->setForceAutoProcessing(FALSE); $ce_display->save(); $custom_element = $this->getCustomElementGenerator() @@ -572,7 +563,7 @@ EOF; // @todo This should be drupal-media. Create issue and fix. $expected_markup = <<<EOF <node type="article" view-mode="full" created="{$this->node->created->value}" title="test" uid="0"> - <drupal-drupal-media type="image" view-mode="default" created="{$this->node->created->value}" uid="0" slot="teaser-media"> + <drupal-drupal-media type="image" view-mode="full" slot="teaser-media"> <field-image slot="image"> <div> <img loading="lazy" src="{$this->image->uri->url}" width="88" height="100" /> diff --git a/tests/src/Functional/CustomElementsRenderMarkupVue3Test.php b/tests/src/Functional/CustomElementsRenderMarkupVue3Test.php index 727a4c23c222da44f5f57edab64a5c09d37f40e1..3670fee713a9c6d3f73b9e02edc69a3fe4aac332 100644 --- a/tests/src/Functional/CustomElementsRenderMarkupVue3Test.php +++ b/tests/src/Functional/CustomElementsRenderMarkupVue3Test.php @@ -123,7 +123,7 @@ EOF; $expected_markup = <<<EOF <node type="article" view-mode="full" created="{$this->node->created->value}" title="test" uid="0"> <template #paragraphs> - <pg-text type="text" view-mode="default"> + <pg-text type="text" view-mode="full"> <p>Some example text</p> </pg-text> </template> @@ -131,11 +131,10 @@ EOF; EOF; $this->assertMarkupEquals($expected_markup, $markup); - // Test behavior with missing CE display (situation post-upgrade-from-v2). - /** @var \Drupal\custom_elements\Entity\EntityCeDisplayInterface $ce_display */ - $ce_display = $this->ceDisplayStorage->load('node.article.default'); - $ce_display->setStatus(FALSE); - $ce_display->save(); + // Test behavior with auto-processing enabled. + $this->ceDisplayStorage->load('node.article.default') + ->setForceAutoProcessing(TRUE) + ->save(); $custom_element = $this->getCustomElementGenerator() ->generate($this->node, 'full'); diff --git a/tests/src/Kernel/EntityCeDisplayTest.php b/tests/src/Kernel/EntityCeDisplayTest.php index a83e7781beff7bbafca81925816ce86bbd784777..8640377fb500046b4577ff39368b202894ee9aef 100644 --- a/tests/src/Kernel/EntityCeDisplayTest.php +++ b/tests/src/Kernel/EntityCeDisplayTest.php @@ -2,7 +2,9 @@ namespace Drupal\Tests\custom_elements\Kernel; +use Drupal\Core\Entity\Entity\EntityViewMode; use Drupal\Core\StringTranslation\StringTranslationTrait; +use Drupal\custom_elements\CustomElementGeneratorTrait; use Drupal\custom_elements\CustomElementsFieldFormatterInterface; use Drupal\custom_elements\Entity\EntityCeDisplay; use Drupal\KernelTests\KernelTestBase; @@ -14,6 +16,7 @@ use Drupal\KernelTests\KernelTestBase; */ class EntityCeDisplayTest extends KernelTestBase { + use CustomElementGeneratorTrait; use StringTranslationTrait; /** @@ -69,6 +72,12 @@ class EntityCeDisplayTest extends KernelTestBase { $expected['component_1']['region'] = 'content'; $this->assertEquals($expected['component_1'], $ce_display->getComponent('component_1')); + // Testing saving auto-processing. + $ce_display->setForceAutoProcessing(TRUE); + $ce_display->save(); + $ce_display = EntityCeDisplay::load($ce_display->id()); + $this->assertTrue($ce_display->getForceAutoProcessing()); + // Delete the entity. $ce_display_id = $ce_display->id(); $ce_display->delete(); @@ -107,4 +116,41 @@ class EntityCeDisplayTest extends KernelTestBase { $this->assertEquals('some_teaser', $formatter->getSetting('view_mode')); } + /** + * Tests that the default config acts as fallback. + */ + public function testViewModeFallback() { + EntityViewMode::create([ + 'id' => 'entity_test.full', + 'targetEntityType' => 'entity_test', + ])->save(); + $ce_display = EntityCeDisplay::create([ + 'targetEntityType' => 'entity_test', + 'customElementName' => 'name_test', + 'bundle' => 'entity_test', + 'mode' => 'default', + ]); + $ce_display->save(); + $loaded_display = $this->getCustomElementGenerator()->getEntityCeDisplay('entity_test', 'entity_test', 'full'); + $this->assertSame($ce_display->getCustomElementName(), $loaded_display->getCustomElementName()); + + $ce_display_full = EntityCeDisplay::create([ + 'targetEntityType' => 'entity_test', + 'customElementName' => 'name_test_full', + 'bundle' => 'entity_test', + 'mode' => 'full', + ]); + $ce_display_full->save(); + $loaded_display = $this->getCustomElementGenerator()->getEntityCeDisplay('entity_test', 'entity_test', 'full'); + $this->assertSame($ce_display_full->getCustomElementName(), $loaded_display->getCustomElementName()); + + $ce_display_full->setStatus(FALSE)->save(); + $loaded_display = $this->getCustomElementGenerator()->getEntityCeDisplay('entity_test', 'entity_test', 'full'); + $this->assertSame($ce_display->getCustomElementName(), $loaded_display->getCustomElementName()); + + $ce_display->setStatus(FALSE)->save(); + $loaded_display = $this->getCustomElementGenerator()->getEntityCeDisplay('entity_test', 'entity_test', 'full'); + $this->assertSame('drupal-entity-test', $loaded_display->getCustomElementName()); + } + }