Overview

This is in support of #3455629: [PP-1] [META] 7. Content Templates — aka "default layouts" — affects the tree+props data model, based on preliminary design discussions that happened 2 days ago, between @lauriii, @jessebaker, @callumharrod and I.

I promised to get an initial outline going of what a new config entity could look like.

Legend:

⚜️
Product question → @lauriii
🪄
Design question → @callumharrod
⚜️
Product question answered
🪄
Design question answered

There's AT LEAST two these major challenges, both design-wise and technically:

  1. ⚜️ View modes: we allow the Site Builder to craft a different component tree per view mode.

    XB aims to support multiple view modes from the start, and one XB Content Type Template per view mode. See #7.2 for details provided by @lauriii.

  2. 🪄⚜️ Internal/exposed or Locked/unlocked aka Exposed Content Type Slots aka unlocked subtrees: allow the Site the Site Builder-defined component tree for the content entity type+bundle+view mode to be customized by the Content Creator:
    • 0 exposed Content Type Slots means no freedom (Content Creator can only enter values for the content entity, not modify the component tree)
    • 1 exposed Content Type Slot at the root means complete freedom
    • anything in between (1–N exposed content type slots in the component tree) means selective freedom: freedom in some parts, but other parts are locked

    (For example: "product" content entities may have rigid presentation of key product information, but unlocked/exposed Hero and story behind the product subtrees.)

    The design/UX fir this is not yet defined.

    Related:

  3. 🪄⚜️ What about the existing EntityViewDisplay(s)? As soon as a content entity type+bundle is opted in to XB (see: #3498525: [later phase] [META] Allow XB to be used on any content entity type (bundle), as long as it has certain entity type characteristics), its existing EntityViewDisplay(s) if any are ignored (not deleted, to allow opting out of XB again and be back at the prior state).

    Design-wise: are they hidden (from the UI) as soon as you create an XB content type template?

    Insight-providing commit 1, insight-providing commit 2

  4. 🪄⚜️Must every field for the content entity type+bundle be used somewhere in the component tree, because otherwise some data is invisible? That seems wrong, because some fields might be meta fields. For that, we have #3463991: [RESEARCH] How to identify all meta fields for an arbitrary content entity?.

    So, rephrasing: assuming that we know which of the structured data are "meta" vs not, must every non-meta field be used somewhere in the component tree?
    Insight-providing commit

  5. ⚜️Are we okay with either ignoring or breaking existing hook_entity_display_build_alter() implementations? This hook very much assumes the linear/sequential design of EntityViewDisplays (a flat list of fields, each presented by some formatter), but that won't be true for XB's Content Type Template's component tree. @lauriii has confirmed this is okay.

    Insight-providing commit

  6. 🪄⚜️Gotcha for Exposed Content Type Slots aka unlocked subtrees. Do we allow creating none, and hence having zero creative freedom for the Content Creator other than populating the content entity form in the Page Data tab? Or do we require >=1? Can that one be the entire component tree, granting full creative freedom?

    0, 1 or N must be supported. Design still needed. Folded into question 2.

    Insight-providing commit

  7. 🪄⚜️Gotcha for Exposed Content Type Slots aka unlocked subtrees. However many we decide to allow per the above: do we allow those exposed Content Type Slots to contain be populated using structured data of the content entity (i.e. using DynamicPropSources) or not? If we do allow this, it's possible that some structured data will NOT be exposed.

    I can think of use cases, but then this needs affordances (or worst case: confirmation modals) to convey this consequence to the Site Builder.

    Insight-providing commit

  8. 🪄Gotcha for Exposed Content Type Slots aka unlocked subtrees. They need (translatable) human labels, arguably also machine names (this would be one way of adding view mode support in the future — then if a Content Type Slot with the same machine name appears in multiple view modes' Content Type Template, the same per-entity override is used).

    Insight-providing commit

  9. 🪄⚜️Gotcha for Exposed Content Type Slots aka unlocked subtrees. Once content exists that populates a Content Type Slot, it can NEVER be removed (its label can still change though), otherwise values in individual content entities' XB fields would disappear!
    Per @lauriii, this is considered acceptable: When building a Content Type Template, the UI must warn user when they are taking actions that may be considered to result in perceived data loss. Stale data can remain in each content entity’s XB field until content has been resaved.

    Still needed: design/UX.

    Insight-providing commit

  10. 🪄⚜️Translatability. A component tree will typically contain some static values. For example: a button that points to the current content entity's webshop URL, with that URL being mapped into it dynamically (DynamicPropSource), but the button label being defined statically because it makes no sense as structured data (so: StaticPropSource).

    Any component input populated using a DynamicPropSource is automatically translatable, because it fetches the value from the host content entity.

    But any component input populated using a StaticPropSource is NOT translatable. At least not at this time. Because component trees in config are currently enormous JSON blobs. That's not compatible with Drupal's config translation functionality.

(A third technical challenge: translatability of this config. Specifically, the exposed/unlocked subtrees' labels should be translatable. This should avoid the mistakes e.g. CKEditor 5 made — see #3382464: [Style] CKEditor 5 styles config storage is not compatible with config ovverides.)

Proposed resolution

Details TBD.

Out of scope:

P.S.: I think the name ContentTypeTemplate is not great. We might land on a better name later, when development on this truly begins. Until then, we've always referred to it using this term, so might as well use it for now. It's purely internal anyway.

User interface changes

None; this is purely supporting back-end infrastructure.

Command icon Show commands

Start within a Git clone of the project using the version control instructions.

Or, if you do not have SSH keys set up on git.drupalcode.org:

Comments

wim leers created an issue. See original summary.

wim leers’s picture

Updated issue summary with new design/product questions that I identified while building the PoC to predict future technical challenges: now there's not 2 but 9 challenges! 😅

FYI: I landed on the term Content Type Slot for what @jessebaker alluded to in #3498819-5: [Needs design] Adjust representation of Regions in the Layers UI.

Note that I built this config entity type PoC while taking into account all discussion-surfaced requirements outlined in the meta.

Omitted from the work I did:

  • Per-component input (un)locking: this seems too overwhelming/too granular.
  • Mostly omitted: view mode concerns. The config entity type supports storing it, but none of the semantical or UX consequences are addressed. For example: it the XB content type template for a "teaser" view mode exposes a different Content Type Slot than the one for the "default" view mode, how would the content author ever populate it? It'd always remain empty! An obvious solution seems: the "default" view mode defines the total set of Content Type Slots, and each view mode can choose to contain only a subset of them. But does this meet @lauriii's product vision? Impossible to tell: it's currently neither written down nor expressed in designs. He did express that anything beyond "default" view mode is a concern for later, so I didn't delve deeper.
wim leers’s picture

In not-yet-public docs, I encountered a functional requirement I had not read before, but it makes sense (it ties into XB product requirement 14. Configuration management), it reads:

I can make changes to the content type templates outside of the production site

… which is the non-UI, but config-management-level-enforced equivalent of point 9 I surfaced in the issue summary above: Once content exists that populates a Content Type Slot, it can NEVER be removed (its label can still change though), otherwise values in individual content entities' XB fields would disappear.

wim leers’s picture

Issue summary: View changes

Additional notes while digging into this problem space and trying to think through everything:

  1. 🪄⚜️Translatability. A component tree will typically contain some static values. For example: a button that points to the current content entity's webshop URL, with that URL being mapped into it dynamically (DynamicPropSource), but the button label being defined statically because it makes no sense as structured data (so: StaticPropSource).

    Any component input populated using a DynamicPropSource is automatically translatable, because it fetches the value from the host content entity.

    But any component input populated using a StaticPropSource is NOT translatable. At least not at this time. Because component trees in config are currently enormous JSON blobs. That's not compatible with Drupal's config translation functionality.

  2. (More to follow.)

catch’s picture

So, rephrasing: assuming that we know which of the structured data are "meta" vs not, must every non-meta field be used somewhere in the component tree?

It's common to have fields that are only used on specific view modes, including ones that don't show on the default. An example would be a 'short summary' field that only shows on a card view mode and not on the main content.

wim leers’s picture

Based on discussing #3455629: [PP-1] [META] 7. Content Templates — aka "default layouts" — affects the tree+props data model with @lauriii this morning, a few things surfaced:

  1. Changing/deleting Content Type Slots MUST be allowed even if it results in user-entered data being "lost" (it still exists, just won't be visible anymore). In fact, it MUST explicitly delete such "lost" data upon re-saving the content entity.

    Per @lauriii, this is necessary to allow iterating on XB Content Type Templates. There MUST be a "heavy" warning, an explicit confirmation by the Site Builder, but this must be allowed.

  2. Supporting multiple view modes MUST be supported from the start. But, only the default view mode will be customizable per entity. So, a teaser view mode for example would look the same for all entities of that content entity type+bundle: the same exact XB component tree would be used for all (i.e. the one in the "teaser" Content Type Template config entity), while of course resolving the structured data mapped into the component tree (i.e. using DynamicPropSources).

    This avoids the entire "keep Content Type Slots in sync across all Content Type Templates (one per view mode) for this content entity type+bundle" problem. 👍

  3. XB is used for either all or none of the view modes. IOW: as soon as you chose to use XB, all existing EntityViewDisplays are ignored. Those that don't have an XB Content Type Template defined yet render nothing.

    Note: this will change in the far future per @lauriii: he intends to allow specifying an XB element (see #3455036: Clarify "components" vs "elements" vs "patterns") to be associated with each field type, which essentially becomes the XB equivalent of "the default formatter".

  4. Last but not least: per @lauriii, there won't be a "search" or "search index" view mode for XB — search must be configured to use the "default" view mode's representation of content entities both for 1) crafting a search index to power the search, 2) presenting search results. Related: #3462219: [META] Support alternative renderings of prop data added for the 'full' view mode such as for search indexing or newsletters and #3483301: Create a configurable search api processor for XB data.

    It's not clear to me yet how that avoids the

    but without elements that would 'pollute' the search results like field labels, related content blocks, CTAs, social widgets etc. so that if I search for "newsletter" or "Facebook" I don't get every article on the site back in the results, but only the ones with content actually mentioning newsletters or Facebook.

    problem described in #3462219: [META] Support alternative renderings of prop data added for the 'full' view mode such as for search indexing or newsletters, though.

wim leers credited lauriii.

wim leers’s picture

callumharrod’s picture

Responses

Hey @wim-leers - the answers below is just my take on the problems listed 😄

  1. View modes - Not for me to answer
  2. Internal/exposed or Locked/unlocked aka Exposed Content Type Slots aka unlocked subtrees - Any slots that are used within the template should be able to be an exposed slot. They will not be able to be exposed if they currently contain any child items.
  3. What about the existing EntityViewDisplay(s)? - Unsure on this one, would be keen to discuss it further.
  4. Must every field for the content entity type+bundle be used somewhere in the component tree, because otherwise some data is invisible? - No, it would be up to whoever is creating their content template to decide what fields are mapped to their template. For example: If a product content type has a required field of ‘Price’ it is up the person creating the template to ensure that it is tied to their template.
    I think a good way to think of the creation of templates is similar to how it would be done with a headless CMS, in that the content model and content creation are separate from the templating layer.
  5. Are we okay with either ignoring or breaking existing hook_entity_display_build_alter() implementations? - Not for me to answer
  6. Gotcha for Exposed Content Type Slots aka unlocked subtrees. - Correct, we should allow having no exposed slots for the content type.

    If a user wanted to they could make the entire page a single slot, but they would still need to add a component and expose the slot in the exact same way as exposing any other slot.

  7. Gotcha for Exposed Content Type Slots aka unlocked subtrees. - I don’t believe we should be allowing exposed slots to include anything. By including items in a slot to also be exposed to a content editor, we open lots of ways to break templates that aren't necessary. By going this route I don't think we impede the template builder in any way. In the user flows this should become clearer.
  8. Gotcha for Exposed Content Type Slots aka unlocked subtrees. - Good point, as part of exposing a slot, we can ensure that there’s a name and machine name here. Will need some designs and work on the front-end to cater for this.
  9. Gotcha for Exposed Content Type Slots aka unlocked subtrees. - Are you saying that when a component is added to a slot when editing content, it cannot be deleted? Or is it just that the data cannot be deleted?

Questions from me:

  1. Once you have made a slot exposed, are we able to revert this change? Can we turn off an exposed slot?
wim leers’s picture

Issue summary: View changes

I see that I failed to update the issue summary with @lauriii's input from #7. Now updated. Some questions are fully answered, some are merged.

wim leers’s picture

#10:
RE: your question: per discussion this morning with @lauriii: as the Site Builder modifying/creating an XB Content Type Template, you have complete freedom to completely change your mind about adding/removing exposed Content Type Slots. So the answer is: "yes, you can revert".

  1. ✅ Sorry 🙈 This has changed — see #7.2. Issue summary updated in #11 👍
  2. I suspect we are on the same page, but … I disagree with your specific wording 😇 I know I'm gonna sound super nitpicky, but all of this is going to have enormous consequences for UX and data integrity, so I went to be painfully precise 🤓 😜
    • It should not be "any slot", because slots can live within other slots.
    • Furthermore, I'm not convinced yet that only slots must be exposable as a Content Type Slot, but any component instance. That way:
      • if it's a component instance without slots, it means you can modify this component instance's inputs — for example: XB's my-section SDC would then be able to have inputs (image, text next to the image) that are defined in the Content Type Template, but if the Content Creator chooses, they could modify the inputs without the ability to add more component instances
      • if it's a component instance with >=1 slot(s), it means the Content Creator can modify as much as they like: remove the entire component tree in one of those slots, keep it empty, replace it with other component instances, or keep it all the same as in the Content Type Template
  3. ✅ This has been answered by @lauriii. Issue summary updated in #11 👍
  4. 👍 WFM! But would there then be affordances to ensure the Site Builder is explicitly aware of which fields (whose values) are not (yet) being presented?
  5. ✅ This has been answered by @lauriii. Issue summary updated in #11 👍
  6. 👍 This ties well into what @lauriii has confirmed Product-wise — see the updated information for Question 2 in the issue summary.
  7. 🤔 I'm not sure how to interpret this. Are you saying that a Site Builder should or should not be able to expose a Content Type Slot that contains some component instances populated with structured data?
  8. 👍
  9. ✅ Sorry 🙈 This has changed — see #7.1. Issue summary updated in #11 👍
    (And no, that's not what I meant: I was referring to disallowing deleting Content Type Slots, but @lauriii has since said this is fine, and it's fine to lose Content Creator-crafted data.)
catch’s picture

Last but not least: per @lauriii, there won't be a "search" or "search index" view mode for XB — search must be configured to use the "default" view mode's representation of content entities both for 1) crafting a search index to power the search,

afaict this is incompatible with #3483301: Create a configurable search api processor for XB data - which is suggesting not rendering XB at all when configuring search indexing, except via the special search API processor. I'm not convinced by that issue yet (and left a question on there which hasn't been answered yet) but has that been abandoned or is this a mistake? Using #3483301: Create a configurable search api processor for XB data would mean either not using a view mode at all in search indexing, or a view mode minus XB data that only shows entity fields, with the XB data then provided by the separate processor.

If you had to use the default view mode and the processor it would mean double-rendering when search indexing.

2) presenting search results.

This seems unnecessarily restrictive and also unenforceable. e.g. you can configure search API views to return results from a search index, then use any view mode on the entity that you like right now - like displaying search results as a grid of cards or whatever.

borisson_’s picture

Last but not least: per @lauriii, there won't be a "search" or "search index" view mode for XB — search must be configured to use the "default" view mode's representation of content entities both for 1) crafting a search index to power the search, 2) presenting search results. Related: #3462219: [META] Support alternative renderings of prop data added for the 'full' view mode such as for search indexing or newsletters and #3483301: Create a configurable search api processor for XB data

It's not clear to me yet how that avoids the

but without elements that would 'pollute' the search results like field labels, related content blocks, CTAs, social widgets etc. so that if I search for "newsletter" or "Facebook" I don't get every article on the site back in the results, but only the ones with content actually mentioning newsletters or Facebook.

problem described in #3462219: [META] Support alternative renderings of prop data added for the 'full' view mode such as for search indexing or newsletters though.

Rendering Search results in the same way as the content's detail page doe not seem like the most commonly chosen way of configuring it. I did a quick poll at the contribution room at Drupal Mountain Camp, and everyone who answered me always configures some kind of teaser, card or search-result view mode when rendering a search result page.

For the indexation part, I also don't see a way that would remove those pollutions without having a seperate view mode, but I personally think that's not as bad. When needing complete control over what all is indexed, it should be indexed using fields, because then it's possible to boost per-field. It gives a lot more control.

This however would mean something like https://www.drupal.org/project/layoutbuilder_search_api for all components/props, which is another approach that could be taken. This removes the double rendering that catch talked about in #13.

catch’s picture

When needing complete control over what all is indexed, it should be indexed using fields, because then it's possible to boost per-field. It gives a lot more control.

This however would mean something like https://www.drupal.org/project/layoutbuilder_search_api for all components/props

I think that #3477428: [PP-1] Refactor (or decide not to) the XB field type to be multi-valued, to de-jsonify the tree, and to reference the field_union type of the prop values would also handle this - because the props would be available as field data to search API then without an additional translation layer.

lauriii’s picture

Rendering Search results in the same way as the content's detail page doe not seem like the most commonly chosen way of configuring it. I did a quick poll at the contribution room at Drupal Mountain Camp, and everyone who answered me always configures some kind of teaser, card or search-result view mode when rendering a search result page.

We should definitely support configuring customizing how content is displayed in the search results page. I'm not sure we need to do that in this issue though because it might make sense to design and implement solution specifically for that, because it's such a common use case.

catch’s picture

@lauriii that's the opposite of what this says in #7:

Last but not least: per @lauriii, there won't be a "search" or "search index" view mode for XB — search must be configured to use the "default" view mode's representation of content entities both for 1) crafting a search index to power the search, 2) presenting search results.

is there a miscommunication somewhere?

If search results use a view mode for rendering (which a lot of sites do), then it shouldn't require any special handling in XB, it just needs to not break using view modes for rendering.

The place where it might get tricky is 'results snippet' type situations where the search result is trying to show the searched keywords in the context of the results. I'm not sure how that's generally implemented in search API or not or whether it would need special handling. For me that's the least important aspect to worry about - it's the initial indexing that feels unresolved still.

wim leers’s picture

#17: that's not matching what you said earlier.

Unless you meant that that would be the initial state, and you intend to only use the default content type template for search indexing and a different content type template for presenting search results?

But if we do that, then it follows that the information that was matched is absent from the search result's presentation, meaning highlighting is impossible.

lauriii’s picture

I feel like we are talking past each other with different use cases and scenarios and concerns that are not being impacted by Experience Builder. My understanding is that there are two primary ways of configuring Search API: text based search and faceted search (as well as combination of the two).

When using text based search and the intent is to index the whole page, the "Rendered HTML output" with the default content type template (same as what user would see when navigating to the page) should be used. Search API would allow configuring other view modes to be used for indexing but those wouldn't include the content added to the slots, but only content that is being rendered through those view modes. So long as we keep view modes working, this should all work without specifically having to accommodate them. This is the same experience that you would get when using Layout Builder today.

When using the text based search, the results would often display an excerpt which is generated based on what is in the index. This allows highlighting the text that is being matched. This shouldn't require any special accommodations from XB so long as what is stored in the index is sensible.

When implementing a faceted search, it's common to configure the results to display to using a view mode like cards or search result. In this scenario, text is not being highlighted. Even in this scenario, the index may include the full rendered page, highlighting that the index is a separate concern from the presentation of the results.

larowlan’s picture

Layout builder uses third party settings on the entity view display for this rather than a separate config entity.
I think that is a valid approach worth considering.

I also think #3462238: Decouple tree storage, introduce tree storage plugins is important here because our present code-path \Drupal\experience_builder\InternalXbFieldNameResolver::getXbFieldName falls over here

wim leers’s picture

Layout builder uses third party settings on the entity view display for this rather than a separate config entity.

I considered that, but I think there are strong reasons against it:

  1. The consequence of that will be though that this new config entity type is not validatable, because EntityViewDisplay config entities are not yet fully validatable, with an unclear ETA for that in core. This would significantly weaken XB's 14. Configuration management product requirement.
  2. LB predominantly uses field formatters to present structured content: it uses FieldBlock, which contains field formatter settings (which IIRC are treated as the default formatter settings when placing a field block). So, most of the information in the EntityViewDisplay is still somewhat relevant when using LB.
  3. @lauriii has indicated that it should be possible to try XB and be able to abort and go back to what you had before: plain core entity view displays, or Layout Builder entity view displays. Not touching them at all makes that an easy promise to keep.
  4. Finally, and probably the strongest argument: LB does a quite heavy-handed override at \Drupal\layout_builder\Hook\LayoutBuilderHooks::entityTypeAlter() that causes all EntityViewDisplay config entities to use its own LayoutBuilderEntityViewDisplay class instead! XB cannot do the same, or it'd either break LB (race: who wins?) or require LB (definitely not the intent either). That's in the MR:
     * This MUST be a new config entity type because doing something like Layout
     * Builder's `LayoutBuilderEntityViewDisplay` is impossible if XB wants to
     * provide a smooth upgrade path from Layout Builder, due to
     * \Drupal\layout_builder\Hook\LayoutBuilderHooks::entityTypeAlter() — only one
     * module can do that!
    

I don't think #3462238: Decouple tree storage, introduce tree storage plugins is a hard blocker for this issue, but I definitely agree it'd be nice to solve, especially in light of providing a smooth transition from Layout Builder into Experience Builder! (Note this will require either duplicating LB's FieldBlock in XB, or moving it to another module — otherwise Layout Builder will be required forever even on sites that have all their data moved from LB to XB!)

mglaman made their first commit to this issue’s fork.

mglaman’s picture

+1 to this approach! I had forgotten this issue and proof of concept existed when I had a chat with @phenaproxima where we chatted over this idea. The outcome happened to be nearly the same as this issue, except I didn't consider the possibility of having the config entity implement EntityViewDisplayInterface so we could swap it for the view display object.

I pushed a test and some wonky code to further explore. I like that it mimics how page regions work. If something exists we execute a new code path versus embedding into the existing code path.

mglaman’s picture

#3366425: move static collect*() methods from display entity classes to EntityDisplayRepository would make this much easier. Then we could swap the display objects not and need hook_entity_prepare_view and hook_entity_view_alter

wim leers’s picture

Title: [PoC] Introduce a `ContentTypeTemplate` config entity » Introduce a `ContentTypeTemplate` config entity
Assigned: Unassigned » phenaproxima
Issue tags: +Needs issue summary update

@callumharrod walked us (@lauriii, myself and David Bee) through the designs he crafted. All of us asked various questions. The PoC here is still aligned with the designs, with one exception: we're not going to be exposing a component instance as an exposed slot, but a slot in a component instance.

Walked @phenaproxima through that change, and captured this change with him together, as a way to hand this off to him: https://git.drupalcode.org/project/experience_builder/-/merge_requests/7... 😊 (I'm still around, obviously!)

Basically, content type template config entities will look like this:

langcode: en
status: true
dependencies: …
id: node.article.teaser
content_entity_type_id: node
content_entity_type_bundle: article
content_entity_type_view_mode: teaser
component_tree: …

and the one for the default view mode will be able to specify content type slots on top of that:

langcode: en
status: true
dependencies: …
id: node.article.default
content_entity_type_id: node
content_entity_type_bundle: article
content_entity_type_view_mode: default
component_tree: …
exposed_content_type_slots:
  profile_bio:
    label: 'Profile Bio!'
    subtree:
      instance: 28bcab26-e434-4ad4-9eaf-0520bdb32fcc
      slot_name: column_two
  outro:
    label: 'Outro'
    subtree:
      instance: 98bcab26-e434-4ad4-9eaf-0520bdb32fcc
      slot_name: body

phenaproxima made their first commit to this issue’s fork.

wim leers’s picture

Title: Introduce a `ContentTypeTemplate` config entity » [META] Introduce a `ContentTypeTemplate` config entity + related infrastructure
Category: Task » Plan

@phenaproxima has split this into multiple child issues 👍

wim leers’s picture

Assigned: phenaproxima » Unassigned
Status: Active » Fixed

I think this meta issue has served its purpose.

As this meta issue's summary says: This is in support of #3455629: [PP-1] [META] 7. Content Templates — aka "default layouts" — affects the tree+props data model — and the design + product decisions have been made, the config entity type exists, the fundamental infrastructure exists thanks to @phenaproxima:

… with now a subset of that infrastructure getting a UI built before 1.0 — see #3541000: [META] Content templates UI for 1.0: only nodes, no exposed slots, no replacement for the view mode/display UI

We still have #3455629 around for delivering the full scope — see #3455629-51: [PP-1] [META] 7. Content Templates — aka "default layouts" — affects the tree+props data model.

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.