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

Extending available templates via plugins #41401

Open
mtias opened this issue May 27, 2022 · 18 comments · May be fixed by #61577
Open

Extending available templates via plugins #41401

mtias opened this issue May 27, 2022 · 18 comments · May be fixed by #61577
Labels
[Feature] Extensibility The ability to extend blocks or the editing experience [Feature] Templates API Related to API powering block template functionality in the Site Editor Needs Design Needs design efforts. [Type] Enhancement A suggestion for improvement.

Comments

@mtias
Copy link
Member

mtias commented May 27, 2022

There are many plugins that come with their own set of templates (plugins for commerce, newsletters, education, etc) that would need to extend the list of templates available when creating new ones. So this list needs to be easy to filter and extend. From a design perspective, we also need to group them well both for creation flows (in the dropdown) and for management flows.

@mtias mtias added [Feature] Extensibility The ability to extend blocks or the editing experience Needs Design Needs design efforts. [Feature] Templates API Related to API powering block template functionality in the Site Editor labels May 27, 2022
@jordesign jordesign added the [Type] Enhancement A suggestion for improvement. label Sep 5, 2023
@carolinan
Copy link
Contributor

I am getting increased number of questions and support requests about adding templates (and to some degree parts) via plugins, so there is a big interest from developers/ extenders to have this solved.

@Aljullu
Copy link
Contributor

Aljullu commented Apr 30, 2024

Howdy folks! In WooCommerce we have been using custom templates for some time, and we are looking into contributing upstream to standardize a way for plugins to register block templates on WordPress.

Goal

Like @gigitux highlighted in #42362 (comment), currently any plugin that wants to register custom block templates needs to hook into three filters: pre_get_block_template, pre_get_block_file_template and get_block_templates. In addition to that, returning the correct value for those filters is not easy and usually requires complex code, which ultimately led WooCommerce to copy-and-paste some internal methods from Gutenberg into WooCommerce.

So the goal of introducing a new API is:

  1. Make it so plugins don't need to hook into multiple filters. Ideally, make it so they don't need to hook into any filter and instead provide a more explicit API.
  2. Make it so Gutenberg takes care of the 'heavy' part of creating template objects, retrieving templates from the file system and the database. In fact, Gutenberg already has all this logic, we should avoid plugins having to reinvent the wheel.

The API

While the current way plugins like WooCommerce are registering templates is via PHP filters, it would be better to have a more explicit API that is solely intended to register templates and template parts. This allows us to decouple the Gutenberg internal logic from the extensibility API shape, which ultimately makes it easier to maintain Gutenberg code, and allows us to build an easier and more flexible API for plugins.

Based on the way post types, taxonomies, blocks and block patterns are registered, my suggestion is to create two new functions: register_block_template() and register_block_template_part() which would have these parameters:

  • namespace/template_name: Namespace and template name.
  • args:
    • title: User friendly title of the template being created, as it will be shown in the admin.
    • description: description displayed in the Site Editor to identify the template.
    • content: HTML content of the template.
    • plugin: to identify the plugin that registered it. Used in the Site Editor templates list to display the correct author.
    • slug: Slug of the template used in the editor, to store it in the database and for themes to override the template.
    • post_types: Which post types the template can be applied to. This parameter only applies to templates.
    • area: Which area the template part belongs to. This parameter only applies to template parts.

Things this API won't cover

Theme <> plugin templates relation

  • Plugin template customizations should be "attached to the theme". That means, if the site modifies a template and then switches to another theme, the edits to that template should no longer be visible.
  • If a theme and a plugin provide a template with the same name, the theme template should take priority.
    • Example: if a theme provides a product-catalog template, it would override the WooCommerce product-catalog template.

You might have noticed this means template names are not namespaced, so there is the risk of template names colliding between plugins or between plugins and a theme. We could consider forcing the plugin namespace like [plugin_name]_[template_name] (ie: woocommerce_product-catalog); however, I'm not sure if the benefits outweigh the extra complexity.

Next steps

I would like some feedback on the above topics:

  • Do the register_block_template() and register_block_template_part() functions sound like a good approach to handle plugin template registration? If not, what other ideas do you have?
  • Any thoughts about the parameters I defined? I don't consider it to be a closed list that can't be modified, but more like a draft to start working on it.
  • Do you agree with the things this API won't cover?
  • What do you think about namespacing or not namespacing the templates (aka, forcing or not forcing plugin templates to begin with the plugin name)?
  • Any other feedback or suggestions?

I'm going to ping some folks here (hope you don't mind the noise 🙏 ) as I would really appreciate your feedback. But You are welcome to ignore the ping or pass the baton to somebody else if you prefer.

@gziolo @ntsekouras @nerrad @annezazu

@nerrad
Copy link
Contributor

nerrad commented Apr 30, 2024

@annezazu - who would be the best folks to partner with to contribute here? Woo is willing to contribute the majority of any work that's needed while ensuring we're providing an API that solves the needs of all plugins wanting to extend templates/template parts.

@annezazu
Copy link
Contributor

This is awesome to see contributions here! I know it comes up a fair amount from broader extenders I've spoken to. In terms of who can help with this, it depends on what cycle it might get into. Beta 1 for 6.6 is just over a month away so we're on a tight time table for that.

I would be curious if this is something @gziolo can chime in on this and see if folks he works with might be able to help, as there's a lot of API related work happening there (interactivity api, block binding api, HTML api) and I imagine lots can be learned to unblock the work here.

@nerrad
Copy link
Contributor

nerrad commented May 1, 2024

area: Which area the template part belongs to. This parameter only applies to templates.

I'm assuming you meant this only applies to template parts?

@nerrad
Copy link
Contributor

nerrad commented May 1, 2024

Determine when each template is rendered: plugins can use the template_redirect action or the template_include filter for this.
Template hierarchy: plugins can use template_hierarchy filters for this.

I'm curious what the rationale is for excluding this from the API?

@youknowriad
Copy link
Contributor

Sounds like a decent plan, I also wonder why we should exclude the rendering part. It seems inherent to registering templates for me.

The other aspect thing on my mind is: is this an API that we can actually use even for "core templates"?

Other than that, I think it would be fine to start exploring this Gutenberg plugin-only and get some feedback/iterations on it.

@nerrad
Copy link
Contributor

nerrad commented May 1, 2024

The other aspect thing on my mind is: is this an API that we can actually use even for "core templates"?

I was wondering the same thing. I think ideally any API introduced here would also be consumed by core.

Plugin template customizations should be "attached to the theme".

This makes me think that the API should be a convenience wrapper over filtering theme.json to merge the template & template parts with it before processing. This would make explicit the attachment and handle things like hierarchy (but might also necessitate something like a path property).

Albert, I know you had mentioned to me you had explored extending theme.json. If that included the above and it's not doable I'd be interested in knowing the problems you faced.

With that said, I think one of the open questions for me about explicit attachment to theme is I'm aware I had seen some discussions somewhere about eventually having templates and template parts be agnostic to themes. If true, it's something that might surface as a part of the work here.

@Aljullu
Copy link
Contributor

Aljullu commented May 2, 2024

Thanks for all the feedback and comments!

I'm assuming you meant this only applies to template parts?

Right, that was a typo. 🤦‍♂️ I just fixed it.

I'm curious what the rationale is for excluding this from the API?

Sounds like a decent plan, I also wonder why we should exclude the rendering part. It seems inherent to registering templates for me.

Good question. My main two reasons to keep it out of the API were:

  1. Not all templates might need it. Ie: a plugin might want to register a template which by default is never rendered, but it's available to be selected as a template when modifying a specific post type. Similarly to some themes including several post or page templates to select from.
  2. There are existing hooks which can be used to achieve that and they are currently being used broadly in block and classic themes. So I was a bit wary about introducing a new one.

Having said that, I acknowledge none of them is a real blocker. And I see that adding an optional parameter to the API for this could help make plugin authors' life easier, so they don't need to use hooks. So I'm happy to add it.

The other aspect thing on my mind is: is this an API that we can actually use even for "core templates"?

I'm not very familiar with how "core templates" are registered, but technically, I don't think there should be anything blocking us from using it, right? I guess the same API could be used for templates and template parts provided by the theme.

This makes me think that the API should be a convenience wrapper over filtering theme.json to merge the template & template parts with it before processing. This would make explicit the attachment and handle things like hierarchy (but might also necessitate something like a path property).

Would you mind clarifying what would be the use-case here? You mean allowing themes to override template metadata? While I think that it makes sense that theme template files have priority over plugin ones, so we allow themes to override the content of plugin templates; I'm wary about doing the same with template metadata (template title, post types, template part area...). I think themes shouldn't be able to override that kind of metadata to avoid breaking plugins.

With that said, I think one of the open questions for me about explicit attachment to theme is I'm aware I had seen some discussions somewhere about eventually having templates and template parts be agnostic to themes. If true, it's something that might surface as a part of the work here.

Good call. I may have been overly influenced by the perspective of WooCommerce on this. We currently have the issue that somebody might modify a specific template, assign the current theme's header template part and after switching themes notice the header is broken as it still points to the previous theme template part (woocommerce/woocommerce#42181). That's why I opted for templates being "attached to the theme".

However, I acknowledge there might be cases where this might not be needed, like some template parts, or specific templates not used in the frontend (ie: email templates?) or using headers/footers provided by the plugin. Based on that, I guess the best option would be to make it optional via an extra parameter in the API?

@nerrad
Copy link
Contributor

nerrad commented May 2, 2024

Would you mind clarifying what would be the use-case here? You mean allowing themes to override template metadata? While I think that it makes sense that theme template files have priority over plugin ones, so we allow themes to override the content of plugin templates; I'm wary about doing the same with template metadata (template title, post types, template part area...). I think themes shouldn't be able to override that kind of metadata to avoid breaking plugins.

I only had passing familiarity with the customTemplates key defined in theme.json and thought that any API could wrap the filters for theme json and inject template metadata for from the caller into that json. The thinking was that would allow for tapping into how theme.json is processed by WP for loading the relevant templates defined by the theme. However, I see that this property is used by themes to provide additional custom templates that can be selected for entities of post types.

This could still be a good option for template options that need to be selected for a custom post type (which plugins might still want to register). But otherwise, likely not for templates attached to specific routes as the default.

@Aljullu
Copy link
Contributor

Aljullu commented May 2, 2024

I only had passing familiarity with the customTemplates key defined in theme.json and thought that any API could wrap the filters for theme json and inject template metadata for from the caller into that json. The thinking was that would allow for tapping into how theme.json is processed by WP for loading the relevant templates defined by the theme. However, I see that this property is used by themes to provide additional custom templates that can be selected for entities of post types.

This could still be a good option for template options that need to be selected for a custom post type (which plugins might still want to register). But otherwise, likely not for templates attached to specific routes as the default.

It makes sense, thanks for clarifying, @nerrad!

I didn't put a lot of thinking into the implementation yet, as I wanted to keep the discussion limited to the API for now. My initial idea was to create something similar to WP_Block_Type_Registry for templates, which would contain any template/template part registered via register_block_template() and register_block_template_part(). For templates/template parts registered via theme.json, I was thinking Gutenberg would call those methods behind-the-scenes and that would keep the relation between templates from theme.json and WP_Block_Type_Registry. But as mentioned, I did dive yet into the code, so not sure if that's a good way forward. 🙃

@ntsekouras
Copy link
Contributor

@Aljullu thanks for tackling this! My first thoughts regarding not including the rendered and hierarchy info are the same as yours, but I guess having a first iteration of the PR will tell us more. I think a registry makes sense to start with, although I'd love other thoughts.

@gziolo
Copy link
Member

gziolo commented May 6, 2024

@annezazu and @Aljullu, unfortunately, I don't have capacity in the coming weeks and enough expertise to help you with these efforts. However, it looks like @ntsekouras provided all the necessary information to move forward with the issue.

@Aljullu
Copy link
Contributor

Aljullu commented May 10, 2024

I updated the API proposal comment above with some changes based on the discussion in this issue and some findings after starting to work on the implementation (very WIP PR: #61577).

Basically, I added some extra attributes to the API:

  • description: description displayed in the Site Editor to identify the template.
  • plugin: to identify the plugin that registered it. Used in the Site Editor templates list to display the correct author.
  • theme_attached: true if the template modifications are bound to the theme, so switching themes doesn't carry over customizations.
  • is_removable: controls whether the template should be displayed in the default list (when the value is false) or in the "Add new template" menu (true). It also impacts whether the UI allows reverting vs. deleting the template.

Other considerations:

  • I'm also wondering if the path attribute should be replaced by a content one so instead of receiving a file path, it would receive the HTML contents directly. This allows more flexibility when registering the template, but requires plugins to handle reading the file.
  • I'm keeping the rendering logic out of the API for now. Mostly because it's easier to start without it and add it later than the other way around. 🙃

As usual, all feedback is appreciated.

@youknowriad
Copy link
Contributor

theme_attached: true if the template modifications are bound to the theme, so switching themes doesn't carry over customizations.

I wonder if this should remain a framework thing. I believe having that flag might hurt us later.

is_removable

This one is also a bit unclear to me. Let's see with implementation, we should probably start conservative.

@Aljullu
Copy link
Contributor

Aljullu commented May 13, 2024

I wonder if this should remain a framework thing. I believe having that flag might hurt us later.

Thanks for the feedback, @youknowriad! I would like to get this one right, as we have use cases for both approaches, and whatever the default behavior is will have big implications on how the API is used.

To recap, there are two use cases to register templates out there: allowing theme-attached templates and not attached templates. That means, if the user switches themes, should modifications be reset or persist? If we want to keep the API simple and only support one use case by default we can:

  1. Built the API so templates are always attached to the theme. This would make our lives (in WooCommerce) much easier, as this is our use case, and I assume the use case of many other plugins. However, this would make it very tricky for plugins to register templates not-attached to the theme, as this API won't support it and I can't think of an easy way for plugins to leverage the API to do it without having to hook into the PHP filters we mentioned in previous comments.
  2. We could take the opposite approach and make templates not attached to the theme. In cases where the plugin wants the template to be attached to the theme, like WooCommerce, there is an easy work-around: plugins can register their templates with a name dependent on the current theme. Ie: register_block_template( 'woocommerce/' . get_stylesheet() . '-single-product', ... );.

The second one feels a bit more flexible to me, but it adds some extra complexity for the use case which I think is more common. And if at some point in the future we want to add support for the theme_attached property (or a similar approach), plugins won't have an easy upgrade path to migrate template modifications, as each plugin will probably have saved them with a different format.

  1. A third option would be to think about alternatives to the theme_attached property, but still build the API to easily support both use cases. For example, the API could allow a plugin and theme properties. If the theme property is defined, the template is attached to the theme, if the theme property is not defined, the template is attached to the plugin.

I will keep thinking about this as I continue working on the implementation, but wanted to share some early thoughts as I think this is a key part of the API.

This one is also a bit unclear to me. Let's see with implementation, we should probably start conservative.

Yeah, I'm fine leaving this one for later. This is something we would like to have in WooCommerce, but I understand it's better to start small and it's not a blocker for the first version of the API.

@youknowriad
Copy link
Contributor

Built the API so templates are always attached to the theme. This would make our lives (in WooCommerce) much easier, as this is our use case, and I assume the use case of many other plugins. However, this would make it very tricky for plugins to register templates not-attached to the theme, as this API won't support it and I can't think of an easy way for plugins to leverage the API to do it without having to hook into the PHP filters we mentioned in previous comments.

We've discussed that we'd want to keep core templates theme specific but at the same time if a user made a modification to a theme template, we'd still surface this alternative template in the UI for users after they switch themes. So I'd say the first option you shared would match that thing and may even address the use-cases of the second alternative.

@Aljullu
Copy link
Contributor

Aljullu commented May 13, 2024

We've discussed that we'd want to keep core templates theme specific but at the same time if a user made a modification to a theme template, we'd still surface this alternative template in the UI for users after they switch themes. So I'd say the first option you shared would match that thing and may even address the use-cases of the second alternative.

Oh, I wasn't aware of that, thanks for the insights, @youknowriad! I will continue exploring the first approach, then.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
[Feature] Extensibility The ability to extend blocks or the editing experience [Feature] Templates API Related to API powering block template functionality in the Site Editor Needs Design Needs design efforts. [Type] Enhancement A suggestion for improvement.
Projects
Development

Successfully merging a pull request may close this issue.

9 participants