Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Introduce concept of block templates for full site editing #17512

Closed
felixarntz opened this issue Sep 22, 2019 · 26 comments
Closed

Introduce concept of block templates for full site editing #17512

felixarntz opened this issue Sep 22, 2019 · 26 comments
Labels
Customization Issues related to Phase 2: Customization efforts Framework Issues related to broader framework topics, especially as it relates to javascript

Comments

@felixarntz
Copy link
Member

felixarntz commented Sep 22, 2019

Based on ideas previously explored in #3588 (and PR #4659) and recently discussed via @mtias in https://make.wordpress.org/core/2019/09/05/defining-content-block-areas/ (see also particularly this comment by @westonruter), let's further specify how today's paradigm of hard-coded theme template files can transition to enable full-site editing using blocks.

There are two major technical pieces needed for this:

  1. An infrastructure to store blocks outside regular post content (in "block templates") and load these templates based on the current context (e.g. query variables and the main WP_Query instance). This issue is focused on introducing said infrastructure.
  2. Block types to cover the minimum requirements, both functionally and semantically, to cover what is currently handled by theme template files. These necessary block types are discussed via Suite of Post Blocks #15623 (and explored in PRs like Add Site Title block and required functionality. #17207 and Editor: Introduce a new dynamic templates mode and add basic post title and content blocks. #17263).

Based on these two pieces, several UI decisions need to be made on how to expose these features to the user, and we will likely need some iterations to get this right. While we'll out of necessity probably touch on some of those ideas here, I suggest we focus on finding a solid under-the-hood solution first.

Here's the concrete suggestion:

  • Introduce a post type wp_template, of which each post represents a single top-level template. In other words, a template that renders an entire page's markup (to be differentiated from so-called template parts).
  • Fully maintain the existing template hierarchy, which is currently used to determine which theme /*.php template file to load. Basically, each wp_template post represents a specific one of those template files.
  • Provide a basic skeleton taking care of printing the non-visual markup (<html>, <head>, wp_head hook, <body> opening and closing tag, etc.). Visually, it will render the actual blocks from the current template.
  • Skip the templates provided by themes entirely, since a hybrid approach would present unnecessary obstacles and confuse the user. Since this will drastically change how themes need to be developed, let's put it behind an experimental flag for now. Eventually, it could possibly become something that requires explicit add_theme_support(), at least in a transitional phase.

Based on this, a few further thoughts:

  • We will also need a way to reuse certain "template parts". This could be accomplished with e.g. the existing reusable blocks feature, or a separate post type wp_template_part, or an alternative variant of reusable blocks that only applies to template content (i.e. not individual post content, for better separation).
  • WordPress theme developers are typically familiar with the template hierarchy, and thus will easily be able to understand the block-based version of it. Given that the necessary block types are present, porting existing templates over will be trivial.
  • For the eventual UX, we probably should not expose template identifiers (such as index, category, singular, single-product, etc.) to the user because these are rather technical. While every theme developer has been exposed to them, they have (for a reason) never been used in WordPress UI. We will need to find a solid way for a user to understand which template they are editing, and for them to decide in which context/scope they'd like to edit the template. For an initial (experimental) implementation, it's fine to just expose block templates with these names.
  • We also need to think about how themes can provide default block templates. A theme could for example provide general layouts to choose from when creating a new wp_template post. Or it could include default block content (in place of today's template files) to use even if no user-created variant of that template exists as a wp_template post. Or on theme activation, these wp_template posts could be automatically created.
  • Developing a theme will be greatly simplified by this methodology. A theme will be responsible for the style and default layouts/templates, and most likely also it will still control some level of user-facing customization options (whether using the Customizer or an alternative interface). However, several other theme features today will become obsolete. In a block-based world, something like custom-header, widget areas, or nav menu locations probably don't make sense anymore. Other "features" like adding theme support for title-tag could potentially be automatically enabled.
@felixarntz felixarntz added Customization Issues related to Phase 2: Customization efforts Framework Issues related to broader framework topics, especially as it relates to javascript labels Sep 22, 2019
@epiqueras
Copy link
Contributor

We will also need a way to reuse certain "template parts". This could be accomplished with e.g. the existing reusable blocks feature, or a separate post type wp_template_part, or an alternative variant of reusable blocks that only applies to template content (i.e. not individual post content, for better separation).

I'd say a separate post type and block for template parts makes sense. The design will probably differ a lot from reusable blocks so it will be better to keep the code separated early on.

We also need to think about how themes can provide default block templates. A theme could for example provide general layouts to choose from when creating a new wp_template post. Or it could include default block content (in place of today's template files) to use even if no user-created variant of that template exists as a wp_template post. Or on theme activation, these wp_template posts could be automatically created.

See the patterns proposal: #17335.

@westonruter
Copy link
Member

We may want to consider keeping track of which theme was active when a given wp_template post was created. While ideally all themes that support wp_template should be able to simply swap out the template, some themes may have stylesheets that expect certain template block configurations. If the wp_template posts were tagged for the active theme, then it could provide a way to filter the templates based on this. This comes to mind based on the Additional CSS feature in the Customizer, which stores CSS in a custom_css post type, but the CSS is theme-specific so each theme gets their own dedicated custom_css post. We were thinking at some point to provide a way to import CSS from one theme into another, and a similar idea may apply here for templates. In any case, the default case for wp_template posts is that they should persist by default when switching themes.

@westonruter
Copy link
Member

  • We also need to think about how themes can provide default block templates. A theme could for example provide general layouts to choose from when creating a new wp_template post. Or it could include default block content (in place of today's template files) to use even if no user-created variant of that template exists as a wp_template post. Or on theme activation, these wp_template posts could be automatically created.

Yes, a theme should provide an templates block data that get merged with wp_template posts into the template hierarchy. Whatever is defined in wp_template posts should override what is defined in the theme. As soon as a wp_template is removed, then the theme-provided template data should be used. I don't think the theme-provided template data should get inserted as wp_template posts upon activation.

When creating a wp_template post, then you could decide to use one of the theme-provided templates block data as the starting point. Something like what @jeremyfelt shows in a tweet.

@noahtallen
Copy link
Member

When creating a wp_template post, then you could decide to use one of the theme-provided templates block data as the starting point.

I think this is definitely a good idea - regardless of the current content of a template part, the user should be able to choose the theme's default content for that area. I think users are often confused towards why the theme's screenshot or demo looks different from their site when they activate their theme, and this would help with that.

Perhaps there could be a way for the user to choose, on theme activation, to use the default content from the theme.

I don't think the theme-provided template data should get inserted as wp_template posts upon activation.

What if a theme defines another content area for which the user doesn't have a template part yet? I can see a case for auto-populating the default content for that area in this case.

@westonruter
Copy link
Member

What if a theme defines another content area for which the user doesn't have a template part yet? I can see a case for auto-populating the default content for that area in this case.

Yes, in this case I believe the template part can be populated directly with data from the theme (e.g. in JSON), without needing to actually create a wp_template_part post in the DB.

@vindl
Copy link
Member

vindl commented Oct 21, 2019

Since we already did some Full Site Editing work on WordPress.com, I’ll try to convey some of the things we’ve tried so far and some of our takeaways. I’d like to preface that with a comment that our work was intentionally scoped down for two reasons:

  • We wanted to have a smaller proof-of-concept solution that we could test with our users and gather feedback that would inform our future work (and possibly Core).
  • We decided to develop this as a plugin, which posed additional limitations, since this work ultimately requires Core changes.

During our work we experimented with a couple of approaches:

  1. Hijacking the template hierarchy from the plugin.

    • Pro: it works with existing themes without needing to modify them.
    • Con: imposing one template for all themes introduces visual and structural inconsistencies.
    • Note: we also looked into overriding get_template_part, but in case of Header it pulls in <head> tag too, and not just <header> content.

  1. “Blank theme” approach - essentially a theme with only index.php defined that replicates the template hierarchy using predefined page templates (in wp_template CPTs).


  1. Adapting existing themes to make them FSE-aware. If they detect that FSE is active, they would delegate template part rendering to it, otherwise they fall back to their default non-FSE content.

    • Pro: can be made to work with existing themes.
    • Con: themes have to be updated to make them FSE aware; introduces slight coupling between themes and FSE functionality.

We ended up using (3) in the end, because it seemed like the most seamless way to get what we need for our scoped-down solution. While this worked for us, it’s not necessarily the best long-term approach for Core, but some pieces could still be reused. This code now lives in:

Populating and storing template data

Our initial template data population happens on after_switch_theme hook, and this can work regardless of the final data storing approach. It provides a way for each theme to register its own specific template data. In order to avoid duplicating this code across all themes that want to support FSE, this functionality is now implemented in FSE plugin, but it seems like something that could be incorporated into core down the road. As a side note, we also attempt this on plugin activation, to cover the case when FSE supporting theme is already active. Overall it follows these steps:

  1. Data population executes on after_switch_theme or register_activation_hook.
  2. We check if template data for this particular theme is already present and bail if it is.
  3. Fetch template data for the theme being activated from a centralized API.
  4. Populate corresponding template CPTs with fetched data.
  5. Mark CPTs with corresponding theme specific taxonomy terms (e.g. maywood_header or similar).

In our case the benefit of (3) is that we don’t have to release a new theme version if we want to publish a fix or update to template data.

As a result of (2) and (5), it’s possible to preserve existing customizations to template parts across theme switches. For example, if you want to just try out a couple of new themes on your site, and then revert to your original one, the previous theme specific customizations to your header would be restored.

For storing the data, we are now only using wp_template_part CPT, but that’s just because our solution didn’t require more. When we tried to envision what core is going to do and align with that a couple of months ago, we leaned toward the wp_template and wp_template_part split, as opposed to just wp_template CPT with hierarchical taxonomies. To clarify, both options seem viable from the implementation standpoint, but it seems like the former requires less wiring, might perform slightly better, and could better aligns with existing WP concepts and infrastructure. Some benefits in my opinion:

  1. There is a nice parallel with terminology in existing template hierarchy (page templates and template parts).
  2. We are trying to model a tree structure, and that’s easier to enforce with the above split. In order to prevent it from becoming a cyclic graph, that could result in infinite loops in editing and rendering context, our query would have to be more complex and filter out page templates in some contexts (e.g. we want to prevent template A containing template B, which also contains A).
  3. Similar to (2), designating a template part as a full page template wouldn’t make much sense, again requiring additional code, and likely another table join with taxonomies to filter them out (depending on implementation). For example: assigning the Header template part as the Homepage template doesn't make sense because a "full-fledged template" would entail several template parts (header, footer, post content etc.).

While all of the above can easily be worked around, they might hint that we are trying to clump together two conceptually different entities under the same roof, and we might discover additional work on account of that down the road too.

It seems to me like the model would become much simpler if we enforced no nesting of entities of the same type, both for wp_template and wp_template_part. So page template could only contain template parts, and template parts would in turn be composed of static, dynamic, and reusable blocks. I’m still not sure if this is too constraining, and it definitely requires some more thought. So something along the following lines:

FSE templates (4)

P.S.: I tried to condense this as much as possible, but it still turned out a bit longer than expected - sorry about that. :) If anything is unclear because of that I'd be happy to clarify. Also, if there is any way that we can further help the core FSE work based on what we currently have on WordPress.com (be it feature testing or user feedback) feel free to let me know.

Kudos to @Copons for reviewing this and suggesting edits. ❤

@epiqueras
Copy link
Contributor

Thanks for writing this up @vindl!

During our work we experimented with a couple of approaches:

I think that a mixture of approach 1 and 2 would be optimal here. Themes should register template posts that hijack the hierarchy, but they should also be able to manually include them in PHP files. We're starting with a limited version of the former here: #17626, and of course the manual approach is always possible by including the CPT posts with custom code, but we might want to expose an explicit API for it in the future, although I also see value in just making the hijack the "supported" approach.

As a result of (2) and (5), it’s possible to preserve existing customizations to template parts across theme switches.

This is something we need to do as well.

It seems to me like the model would become much simpler if we enforced no nesting of entities of the same type, both for wp_template and wp_template_part. So page template could only contain template parts, and template parts would in turn be composed of static, dynamic, and reusable blocks.

I agree, this approach also makes the most sense to me.

@Copons
Copy link
Contributor

Copons commented Oct 22, 2019

If I may chime in, as a person who worked hard on the WordPress.com Blank Theme experiment, and struggled immensely in explaining it to peers and stakeholders alike: we desperately need the best and clearest terminology we can come up with.

wp_template, wp_template_part, template hierarchy, template files: it's painfully obvious to me that the "template" word is overloaded of meanings!

I don't have a good answer, and I know this seems like bikeshedding, but if we manage to figure out some unambiguous terms, then FSE will become 100% easier to discuss, and the project itself will move forward quicker.

@epiqueras
Copy link
Contributor

How about:

wp_templates: The new "Template Posts".
wp_template_parts: The new "Template Part Posts".
Template Hierarchy: The classic WP template hierarchy.
Template Files: The classic .php template files.

@Copons
Copy link
Contributor

Copons commented Oct 22, 2019

How about:

wp_templates: The new "Template Posts".
wp_template_parts: The new "Template Part Posts".
Template Hierarchy: The classic WP template hierarchy.
Template Files: The classic .php template files.

I think I was misunderstood: the meaning of each term is clear when you list them in a glossary.
But I promise you: casually mentioning, for example, wp_template in a conversation about Full Site Editing will create major confusion and questions, depending on the level of familiarity of the people involved. 😅

In the end, it was inevitable to me to alwayt add a bit of description and details every time I mentioned a %template% term for the first time in a post or comment, which in turn makes everything else less clear and harder to read and follow.

@epiqueras
Copy link
Contributor

We should just link to a glossary.

@noahtallen
Copy link
Member

noahtallen commented Oct 22, 2019 via email

@epiqueras
Copy link
Contributor

• The block template API against which blocks are validated. (I.e. inserted blocks can be required to match the global block template and some other block components include templates)

Those are called block templates.

• Starter page templates: while not in core per se, this is the case where blocks are auto-populated into a page/block area.

Those are now being called block patterns.

Haha, yeah this will get confusing.

@koke
Copy link
Contributor

koke commented Oct 23, 2019

Those are now being called block patterns.

At least for mobile we are calling them starter page templates for now (see #18055).

Based on the conversation in #17335, I understand block patterns are meant to be parts that get inserted into a page rather than a full page template to get started.

@epiqueras
Copy link
Contributor

Based on the conversation in #17335, I understand block patterns are meant to be parts that get inserted into a page rather than a full page template to get started.

A full page template can just be a pattern that gets applied automatically. Even if we decide to name them differently in the UI, I think they should share the same implementation.

@timhibberd
Copy link

timhibberd commented Dec 7, 2019

@vindl I like your template diagram. Is there any reason there is a Post Content Block in the middle of the page template? Why would that not just be another template part? So... a template is just a set of template parts.

It seems reasonable as well that a template could be a combination of horizontal and vertical template parts (otherwise how would we get a sidebar!).

Before we go down the slippery slope of non-differentiation between a complex block and a template-part...I would suggest that a template-part should be a theme-specific construct while the block is a theme-agnostic construct. There was also a related discussion in #18736

Thoughts?

@epiqueras
Copy link
Contributor

Why would that not just be another template part?

You might not want to introduce the overhead of a template part wrapper if you are not reusing the block/set of blocks in another template.

It seems reasonable as well that a template could be a combination of horizontal and vertical template parts (otherwise how would we get a sidebar!).

Definitely, I've been exploring grids as well, #18600.

I would suggest that a template-part should be a theme-specific construct while the block is a theme-agnostic construct.

Yeah, that's how it currently works.

@timhibberd
Copy link

Cheers @epiqueras - the explanation is much appreciated. Sounds good. I concur with your comment about not needing a template part wrapper for non-reused blocks. The diagram above had made me concerned that the "post-content block" component was a hard-coded component rather than a sample "some block" as you explained above. So it sounds like a page can be all blocks, all template-parts or a combination of both...which is great. My compliments to the team. Looking forward to theming with the template-parts :-)

@epiqueras
Copy link
Contributor

epiqueras commented Dec 7, 2019 via email

@vindl
Copy link
Member

vindl commented Dec 17, 2019

@vindl I like your template diagram. Is there any reason there is a Post Content Block in the middle of the page template? Why would that not just be another template part? So... a template is just a set of template parts.

Thanks @timhibberd! Basically what Enrique already said. They can be both surfaced to users as Template Parts, however they are conceptually different in implementation detail in my opinion. Template Parts will be preserving their post_content in corresponding wp_template_part CPTs that will be reused across multiple Page Templates. On the other hand, Post Content Block will be referencing the post_content of the current post being rendered, so it doesn't require the same scaffolding and data storage as Template Parts do. Another way to formulate the difference would be to say that Template Parts are reusable and static (although they can still contain dynamic blocks), and Post Content Block is reusable and dynamic.

It seems reasonable as well that a template could be a combination of horizontal and vertical template parts (otherwise how would we get a sidebar!).

That's true! I wanted to keep my example as simple as possible, so I left those out, but I didn't foresee any problems with incorporating them into the above model. My main intention was to represent the data relationships and the way they are stored. That Page Template in the diagram also has some resemblance to the actual layout, so I see how that could've been misleading. In any case, even in the above example, any (or both) of the Template Parts could be styled and presented visually as horizontal ones, turning that into a 2 (or 3) column page layout.

Before we go down the slippery slope of non-differentiation between a complex block and a template-part...I would suggest that a template-part should be a theme-specific construct while the block is a theme-agnostic construct.

I'm not sure if I'm interpreting this part correctly and to which extent it should apply, but I think that this kind of separation could be useful. My addition of theme specific taxonomy terms in the diagram (wp_template_theme and wp_template_part_theme) was partly motivated by that.

@timhibberd
Copy link

Cheers @vindl - much appreciated :-) I hope my comments came across as intended...to elicit clarifications (which you and Enrique did clarify nicely). I thought your proposal and diagram were spot-on.

Regarding my comment about differentiation between a complex block and a template-part, I think you and Enrique have clarified that. My comment was intended to ensure that we can answer the question: "What is / are the difference(s) between a complex block and a template-part?". The definition of a "theme" in the Gutenberg era is still emerging and the answer to this question will, in part, help existing theme designers to make the mental shift from the old world to the new world.

@youknowriad
Copy link
Contributor

Most of the architecture work for this issue has already landed. I'm going to close this issue now, let's focus on smaller targetted issues now.

@JoshuaJarman
Copy link

we have launched a number of sites using wp_template custom post types.
we cannot get woocommerce pages to override using this method.
any ideas or tips? we are desperate. thanks!

@bph
Copy link
Contributor

bph commented Jul 22, 2020

@JoshuaJarman you might want to start a new issue, instead of posting to a closed issue.
It then will also guide you through some information needed to actually assist in what goes wrong. And what you already tried to fix it. Be as specific as you can with code examples etc.

@JoshuaJarman
Copy link

@bph i'll try again. even mentioning the block templates and custom post_type wp_templates and its routing seems to confuse most people, so i hoped to ask in one of the few corners where people know anything about it.

@noahtallen
Copy link
Member

I responded on #24129 :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Customization Issues related to Phase 2: Customization efforts Framework Issues related to broader framework topics, especially as it relates to javascript
Projects
None yet
Development

No branches or pull requests