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

Block Based Themes: Dynamic values in static HTML theme files #20966

Open
johnstonphilip opened this issue Mar 17, 2020 · 48 comments
Open

Block Based Themes: Dynamic values in static HTML theme files #20966

johnstonphilip opened this issue Mar 17, 2020 · 48 comments
Labels
[Feature] Themes Questions or issues with incorporating or styling blocks in a theme. [Type] Enhancement A suggestion for improvement.

Comments

@johnstonphilip
Copy link
Contributor

johnstonphilip commented Mar 17, 2020

The problem

There isn't currently a way to define a dynamic URL which uses the current site's domain name as the base in static HTML files for distribution. While this previously could be done in a PHP template file using get_bloginfo( 'url' ), there's no way to define an equivalent variable value in static HTML.

Themes will often include things like images with the theme as part of the design (backgrounds, etc). Blocks facilitate this, in that some blocks have image URL settings. For example, the cover block allows for a background image.

The Theme Experiments repo is currently pulling all included images from an outside source.

But pulling from an outside server is probably not the right approach to delivering a theme.

Possible solutions (all have drawbacks)

1: Parse the HTML files for dynamic values and replace on-the-fly.
Some sort of placeholder could be used in static html files to represent the current site's URL. However, this would require that each HTML file's code be parsed, and the strings be found-and-replaced on-the-fly. This would cause large page slowdowns on every page load.

2: Parse the HTML files for dynamic values and replace once.
Instead of on-the-fly replacing, this would find-and-replace each base TLD in the theme file and re-write the theme's html files upon theme activation, so that the parsing only happens once. But this would result in overwritten theme core files, which would be wiped out upon theme updates. It would also need to be re-done if a theme file was manually edited in any way. This approach would open a whole host of issues like this.

3: Theme files as JSON.
Instead of using static HTML files, theme files could be JSON files, with url values being easy to parse, and the resulting parsed block templates being stored in the database as wp_template. Similar in approach to Alternative 2 (above), the theme files would never be loaded except during theme activation, and then essentially would be forgotten.

JSON is a better format than static HTML when it comes to parsing-and-extracting a value. However, it has similar issues when it comes to editing a theme file after-the-fact, in that the "default" block template in the database would need to be overwritten again. If that default were kept as an immutable post (except via theme json files), it may work.

4: Theme files as PHP
Instead of being used as they have in the past, a PHP file could return a block template with the dynamic portions manually coded. For example:

function my_theme_front_page() {
	return '<!-- wp:cover {"url":"' . get_template_directory_uri() .'/block-background-image.png","id":273,"dimRatio":0,"minHeight":647,"align":"wide"} -->';
}

There are many drawbacks to this approach as well, though it's likely the least code-complex solution, as it would not require any parsing or finding-and-replacing of values.

Other ideas are definitely welcome, as all of these have problems and drawbacks.

@jffng
Copy link
Contributor

jffng commented Mar 18, 2020

Just adding that this seems related to #20542.

@petrozavodsky
Copy link

petrozavodsky commented Mar 20, 2020

php is a great template engine and you need to use it.
Development of blocks for WordPress themes is somehow related to php and this does not introduce additional problems.
Html has no reception, and carries performance degradation😔

@nicpelletier
Copy link

I guest that could translate to option 1 or 2, but could Twig be used? It works well for Symfony and Drupal. Having one less templating language to learn means more time to build cool things.

@mtias
Copy link
Member

mtias commented Mar 23, 2020

@johnstonphilip it'd help to classify the use cases a bit more. The images, for example, depends a bit on whether it's structural or starter content (meant to be replaced by the user). Blocks can be dynamic sources, the fact they can be expressed in an HTML file doesn't affect that. (The site home url, for example, would be handled by the site title block.) That said, the HTML representation is not final as there still needs to be proper handling of translations, which is in the same problem space as mapping urls to the site or theme folder, so gathering more of the examples that would not be handled by a specific block would be useful.

@rilwis
Copy link
Contributor

rilwis commented Mar 24, 2020

Thanks @mtias for the comment.

I think the dynamic data sources in blocks can be treated similarly as in dynamic blocks, which is already available in Gutenberg (like recent posts, recent comments or widget block).

@johnstonphilip
Copy link
Contributor Author

johnstonphilip commented Mar 24, 2020

@mtias I hope I've understood your question properly here. I believe that the uses cases that are relevant here are any blocks that hardcode dynamic values in the HTML.

Here are a few of the things I see getting hardcoded into the HTML that would need to be replaced:

  • Strings which are hardcoded into the html and require translation.
  • All hardcoded URLs (images, videos, any file) which are hardcoded in the HTML and are intended to be local to the site.
  • Navigation Menu URLs (Navigation Block: Add support for a dynamic home URL #20542) which are hardcoded into the HTML.

You're right in that non-hardcoded/dynamic blocks (which do not hardcode any URLs or replaceable things in the HTML) are not part of this issue. For example, the site title block does not hardcode the URL at all in the HTML. It simply adds this: <!-- wp:site-title /-->

Structural vs Starter content

I think that what is structural content and what is starter content will vary based on the situation. For example, a client who says:

Make the whole site for me and send a theme.zip, I don't want to have to do or edit anything myself

For them, everything is structural as they do not want to edit anything themselves.

"that's what I'm paying you for!!" -- Client

Therefore, I would argue that structural vs starter does not matter, and that every block which hardcodes anything should have these capabilities.

Many blocks affected

To try and narrow it down, I tested all of the Common blocks to see which ones allow for hardcoded URLs. All of them actually do (I tested more than just these but kept to these to keep this brief). For example:

  • Cover Block (hardcoded image URL)
    <!-- wp:cover {"url":"https://mystagingsite.local/wp-content/themes/custom-theme-for-client/image.png","id":1575} -->

  • Image Block (hardcoded image URL)
    <!-- wp:image {"id":1575,"sizeSlug":"large"} --><figure class="wp-block-image size-large"><img src="https://mystagingsite.local/wp-content/themes/custom-theme-for-client/image.png" alt="" class="wp-image-1575"/></figure><!-- /wp:image -->

  • Paragraph Block (hardcoded inline image URL)
    <!-- wp:paragraph --><p>Here is some text with an inline image:<img class="wp-image-1575" style="width: 100px;" src="https://mystagingsite.local/wp-content/themes/custom-theme-for-client/image.png" alt=""></p><!-- /wp:paragraph -->

  • Heading Block (hardcoded inline image URL)
    <!-- wp:heading --><h2>Here's the heading<img class="wp-image-1575" style="width: 100px;" src="https://mystagingsite.local/wp-content/themes/custom-theme-for-client/image.png" alt=""></h2><!-- /wp:heading -->

  • List Block (hardcoded inline image urls)
    <!-- wp:list --><ul><li>Image 1 <img class="wp-image-1575" style="width: 100px;" src="https://wpengine1.local/wp-content/themes/custom-theme-for-client/image.png" alt=""></li></ul><!-- /wp:list -->

  • Quote Block (hardcoded inline image urls)
    <!-- wp:quote --><blockquote class="wp-block-quote"><p>Hello world <img class="wp-image-1575" style="width: 100px;" src="https://mystagingsite.local/wp-content/themes/custom-theme-for-client/image.png" alt=""></p><cite>Brian Kernighan</cite></blockquote><!-- /wp:quote -->

  • Audio File Block (hardcoded audio url)
    <!-- wp:audio {"id":234} --><figure class="wp-block-audio"><audio controls src="https://mystagingsite.local/wp-content/themes/custom-theme-for-client/song.mp3"></audio></figure><!-- /wp:audio -->

Another possible solution

@nicpelletier While Twig is an approach that could work, I wonder about frontend-only blocks. They wouldn't be rendered on the server, and would require this type of dynamic templating on the frontend.

@BMO-tech mentioned in the core-editor meeting that we should look at Mustache templates for this as they are language agnostic, and could be used in either PHP or Javascript.

Here's how it might work with Mustache templates:
<!-- wp:cover {"url":"{{template_directory_uri}}/image.png","id":1575} -->

@carolinan
Copy link
Contributor

Related to this is the translations.
For block patterns and templates to be able to showcase to a user what they look like, they do not only need image content or links, they may also need text.
We can't translate text in plain html.

@wpscholar
Copy link
Member

wpscholar commented Mar 27, 2020

Related: #21204

@wpscholar
Copy link
Member

Unlike PHP theme templates, HTML templates don’t allow us to conditionally apply classes to a parent element based on whether or not, for example, a featured image exists on a post. Are we going to lean on JavaScript for this?

@talldan talldan added the [Type] Enhancement A suggestion for improvement. label Apr 7, 2020
@skorasaurus
Copy link
Member

ACF are another dynamic example. For example, having a page where the content is already included from using get_field

@ockham
Copy link
Contributor

ockham commented Apr 17, 2020

I wonder if block context, as discussed at #19685 (comment) and implemented in #21467 could be part of a solution to this problem. In short, block context would allow passing information from an ancestor to a descendant block. The canonical example for block context is a Post block (that doesn't necessarily render anything visible itself) that communicate the post's ID to its children, and a wrapped Post Title block that uses it to display that block's title.

The problem we're dealing with in this issue is that in a page template, we're using a number of established general-purpose blocks (such as Cover or Image block) that reference assets that are part of the theme -- which means we cannot simply hardcode absolute URLs. See the examples in #20966 (comment), e.g.

<!-- wp:cover {"url":"https://mystagingsite.local/wp-content/themes/custom-theme-for-client/image.png","id":1575} -->

Using block context (in the example case: to the url attribute from block context) for the present problem would however require some changes/additions:

  • The (arguably) easiest part would be some all-encompassing site block, or just some generally available site context that provides infomation akin to get_bloginfo(). (Maybe only in template editing mode.)
  • In the example, we wouldn't want the entire url attribute to be provided from context; rather, we'd concatenate it from the site's URL, plus the theme's path to the image. That means we'd need to allow for some logic that doesn't map context to attributes 1:1, but allows for some processing of context.
  • UI-wise, we might need to get cute there, as we still want to allow users to pick their image from the file browser; meaning that we'd have to basically remove the blog URL from the full image path in order to get a relative URL.
  • Unlike a Post Title (or Post Author, etc) block, the blocks we're using in templates are general-purpose -- they aren't coupled to the presence of a context but can be used without. Two potential solutions I can think of:
    1. We provide two flavors of those blocks: the general-purpose ones, and opinionated ones that expect their attributes from context. (Downside: Confusing, potentially hard to maintain.)
    2. We merge both flavors into one: Blocks are either general-purpose, or opinionated, depending on where they're used. (E.g. template-editing mode could always imply using the opinionated ones. Also see the very first item in this list)
  • The Cover block is the most-straightforward example, since we're talking about an actual attribute there. The Image block is probably also simple enough, since its src (while attached to and img element) is available through the usual block attributes API (IIRC). However, other examples listed in Block Based Themes: Dynamic values in static HTML theme files #20966 (comment) might be harder to tackle, as asset URLs are more implicit there. I don't have a clear answer there.

While the above sounds complex, I think it's still within the expected complexity of full-site editing, and it would use a technology (block context) that seems natural enough in this case.

cc/ @aduth

@ockham
Copy link
Contributor

ockham commented Apr 20, 2020

Some more thoughts on 'contextual' blocks here: #21728

@epiqueras
Copy link
Contributor

I think we should look at this from a higher level.

The core issue being discussed is that serialized block content's only way of being dynamic is through dynamic blocks. This is an issue for two reasons:

Block Context is not something that can solve this. We would just be creating template versions of Core blocks or introducing different render paths in the current ones. We would also be forcing all of them to have render_callbacks. Block Context is for hierarchical provision and consumption of data.

Inline PHP in the templates also doesn't solve this, because then parsing and validation would be dependent on the environment and any edits would remove the dynamic parts. Inline PHP conflicts with our goals of full direct manipulation.

I think the root cause is that the granularity for defining dynamic parts of content is too coarse. We can only make entire blocks dynamic, but we also clearly need inline dynamic content. I think the best way to move forward would be to enhance the inline tokens/images/etc. API so that block edits can define inline dynamic tokens.

For example:

function ParagraphEdit() {
	return <I18NRichText />;
}

function ImageEdit() {
	return (
		<img
			src={
				<>
					<BaseURL />
					/path
				</>
			}
		/>
	);
}

These inline blocks should only be available programmatically, not through any inserter, and of course, they'll have their own edit and render_callback implementations. This is very powerful because not only does it provide a way of having inline dynamic content, but it also allows us to customize the editor experience. I.e., we can expose inline UI controls for any needed configurations.

What does everyone think? Have I overlooked anything obvious? @mtias I know you had reservations about adding inline tokens like these, but they still seem like the best way to support what we need both in the editor and front-end.

A more straightforward solution that doesn't solve i18n in post content would be to translate all text in templates and replace all asset URLs. Still, I think it doesn't make sense to add more complexity here when we need something like the inline block approach for i18n later down the line anyway.

@johnstonphilip
Copy link
Contributor Author

@epiqueras Very interesting thought! Can you explain a bit more about this?

These inline blocks should only be available programmatically, not through any inserter

Would the ability to choose a "local" URL as a prefix to an image be available in the Gutenberg UI? If not, how would I use a local path in an image block? Would I need to hardcode something in the post_content?

@ockham
Copy link
Contributor

ockham commented Apr 21, 2020

[...]
Block Context is not something that can solve this. We would just be creating template versions of Core blocks or introducing different render paths in the current ones.

Yeah, that was basically my proposal. @epiqueras Can you elaborate why that's a no-go?

Inline PHP in the templates also doesn't solve this, because then parsing and validation would be dependent on the environment and any edits would remove the dynamic parts. Inline PHP conflicts with our goals of full direct manipulation.

Agree.

[...]
What does everyone think? Have I overlooked anything obvious? @mtias I know you had reservations about adding inline tokens like these, but they still seem like the best way to support what we need both in the editor and front-end.

I'm trying to think through how the second example would work in practice, upon image upload. Currently, there are different directories for uploaded files (wp-content/uploads/ if memory serves) and themes (wp-content/themes/). Both are customizable through filters -- I don't think there's any guarantee about how they relate to each other. For a theme to be distributable, its assets would need to be colocated with its source (templates). Upon file upload into an image block (in content or template editing mode regardless):

  • Would files still unconditionally end up in the usual uploads directory?
  • Would we then have the block infer the image file's location relative to the uploads folder, and express that location via <BaseURL /> + path?
  • If we were to export the theme for distribution, we'd need to include the image asset, and change the URL in the image block accordingly to point to the asset within the themes folder. I'm not sure that's feasible if we hard-wire BaseURL in the image block. 🤔

I'm not sure how to avoid the latter, unless we implement indeed a different behavior for blocks in template editing: all uploads go to the current theme's folder, and the image block refers to <ThemeBaseURL /> + path from the start.


My hypothesis in #21728 was that a lot of the requirements we have for site editing don't translate back to content (i.e. post/page) editing: strings generally don't need to be translated, and asset URLs work fine when left absolute. (This is what lead me to different code paths or different versions of blocks for site vs content editing.) I'd say that in content editing, we wouldn't normally want e.g. translations at individual block level such as

function ParagraphEdit() {
	return <I18NRichText />;
}

and there's a bit of a risk that such a solution might preclude a later solution for multilingual content (as @noahtallen pointed out in #21728) -- assuming that it might e.g. be post-based rather than block-based.

I'd invite you to join #21728, I think there's been some rather insightful discussion.

@epiqueras
Copy link
Contributor

@johnstonphilip

Would the ability to choose a "local" URL as a prefix to an image be available in the Gutenberg UI? If not, how would I use a local path in an image block? Would I need to hardcode something in the post_content?

The image block would know when to insert it or offer a setting to do so.

@ockham

Yeah, that was basically my proposal. @epiqueras Can you elaborate why that's a no-go?

It doesn't scale once you start needing things like translatable gallery captions, and it forces all of these types of blocks to be server-rendered even if they don't have any other reason to be so.

I'm trying to think through how the second example would work in practice, upon image upload.

We can avoid changing anything with the current upload process. <BaseURL /> would know what to render on the front end based on whether the template is a wp_template or an .html file. Things on the editor should just work.

When exporting a theme, we include the referenced images in the templates in the .zip file.

My hypothesis in #21728 was that a lot of the requirements we have for site editing don't translate back to content (i.e. post/page) editing: strings generally don't need to be translated,...
I'd say that in content editing, we wouldn't normally want e.g. translations at individual block level such as

Anything the user enters could need translation, and most blocks are more than one continuous blob of text: E.g. image captions, quotes, galleries, covers. Furthermore, I think it would be pretty cool if we could allow users to highlight only a specific part of a paragraph and mark it for translation.

@johnstonphilip
Copy link
Contributor Author

@epiqueras if the <BaseURL /> only operates if the source is an .html file, what would happen to all of the image links if the user makes an unrelated edit and saves, thus generating a wp_template?

@epiqueras
Copy link
Contributor

The images can be copied to the uploads folder when that happens.

@johnstonphilip
Copy link
Contributor Author

@epiqueras I was wondering that too. But what if the post has a ton of large images? Would it be necessary to do that migration in batches to ensure no server timeouts? It's also possible that it could cause a server disk space max-out and fail. Just a few thoughts about what might happen with that approach.

@epiqueras
Copy link
Contributor

I don't think a single wp_template will have enough images to cause issues.

@mcsf
Copy link
Contributor

mcsf commented Apr 23, 2020

I think the root cause is that the granularity for defining dynamic parts of content is too coarse. We can only make entire blocks dynamic, but we also clearly need inline dynamic content. I think the best way to move forward would be to enhance the inline tokens/images/etc. API so that block edits can define inline dynamic tokens.

These inline blocks should only be available programmatically, not through any inserter, and of course, they'll have their own edit and render_callback implementations. This is very powerful because not only does it provide a way of having inline dynamic content, but it also allows us to customize the editor experience. I.e., we can expose inline UI controls for any needed configurations.

It's not clear how these inline tokens would materialise in such a way that they could be dynamically handled by the server. Right now, consumers of the Inline API, such as Inline Images, don't output anything that looks like a block boundary or any other easily spotted demarcation.

@epiqueras
Copy link
Contributor

Could we add a new boundary to the parser's grammar?

@mcsf
Copy link
Contributor

mcsf commented Apr 24, 2020

Could we add a new boundary to the parser's grammar?

Technically yes, or you could even shoehorn something on the side like WordPress did for <!-- more -->, but at that point I'm worried about the overall fragility of this and I worry that we're not exploring our options deeply enough.

One thing to consider is that demarcations of the <!-- kind are just HTML comments. This conforms very well to the model — where our post is ultimately an HTML document — in which things like <!-- more --> sit at the root of the document, in between elements, and blocks are also well formed HTML nodes. However, it is ill-suited for inline tokens that are meant to be inserted inside HTML elements (e.g. as attribute values or even keys). It's not that it can't work — WordPress can guarantee that no <!-- inline-token --> is left on the rendered page — but suddenly we have posts that are no longer "just" runtime-agnostic HTML.

@epiqueras
Copy link
Contributor

I see, so basically with dynamic blocks, because of the delimiters being between elements, the whole document is still valid HTML and can be loaded, and the dynamic blocks will just be left out.

But, with inline tokens, that wouldn't be possible as the document would be invalid.

It wouldn't be "invalid" though, it would just be kind of broken. E.g., the image tag would have a broken href.

This makes me wonder if you want a block that depends on dynamic inline content like base URLs to render at all when opening the document statically. Wouldn't it make more sense to comment out all of the markup for the block, so it doesn't show unless the server renderer runs on the document and has a chance to deal with its inline tokens?

With translatable strings, it would be a bit different so, if the dynamic inline content has a default value, like a default locale, for example. We could leave an uncommented render of the markup in the document with those default values. Then under it include a commented version with the inline tokens that the server renderer can use to replace the uncommented one.

Does that make sense? We could still leverage inline tokens and still render as much as possible using default values when opening documents statically.

@p-jackson
Copy link
Member

Maybe we could tag attributes as dynamic with something other than html comments, like using a data- attribute.

The src attribute could have a corresponding data-gb-src attribute. The syntax inside the data-gb-src attribute could be similar to the <!-- syntax, because the inline tokens are very similar to blocks. They're just rendered inside attributes instead of as child content. It would mean the mean the raw html could continue to be valid because data- attributes are inert.

<!-- wp:image {"sizeSlug":"large"} -->
<figure class="wp-block-image size-large">
<img
	data-gb-src="wp:themefile {'path':'image.jpg'} /wp:themefile"
	src="https://mysite.com/path-to-theme-files/image.jpg"
	alt=""
/>
</figure>
<!-- /wp:image -->

That would allow inline tokens to be used for theme files, but the same technique could be used to represent dynamic content for translations. Here the wp:18n inline token translates the image's alt-text.

<!-- wp:image {"sizeSlug":"large"} -->
<figure class="wp-block-image size-large">
<img
	data-gb-src="wp:themefile {'path':'image.jpg'} /wp:themefile"
	src="https://mysite.com/path-to-theme-files/image.jpg"
	data-gb-alt="wp:i18n {'text':'Text to translate'} /wp:i18n"
	alt="Text to translate"
/>
</figure>
<!-- /wp:image -->

Maybe the same 18n inline token could also work like a block too? For translating paragraph content.

<!-- wp:paragraph -->
<p>
<!-- wp:i18n {"text":"Text to translate"} -->
Text to translate
<!-- /wp:i18n -->
</p>
<!-- /wp:paragraph -->

@cr0ybot
Copy link
Contributor

cr0ybot commented Feb 26, 2021

All of the problems and proposed solutions in this thread are solved with PHP. Literally all of them.

Introducing a new templating language—whether something established (i.e. Twig) or a custom syntax inserted into HTML comments—does not simplify template development, it complicates it. PHP is already a templating language.

Why do I get the impression that the problem being solved here isn't "how do we make dynamic templates from static files" but rather "how do we get rid of that pesky PHP"? If that is the case can we at least be honest about it?

If the real problem is "themes developed with PHP don't follow the rules often enough because of the low barrier to entry and bad code" then we have to decide if the likely number 1 reason why WordPress powers 40% of the web is worth discarding. Perhaps better documentation and educational resources would be a better approach, or maybe I am naïve. Or maybe the goal is to have less theme developers overall, instead relying on SquareSpace-style site building to increase that percentage further.

However, if the goal is for these theme files to be generated—as in, they are developed purely within the WordPress back-end and saved out/exported—and then imported on other sites, immediately pulled into the database as wp_template entries... I feel like I'm strangely ok with that? Is that the (potential) approach? If these are installed like any other theme with files sitting on the server and referenced during runtime then I would strongly advocate for PHP. But if they just serve as a reference package to be imported into the database and from there dynamically updated, I don't see why they couldn't just be the same kind of markup that WordPress already stores in post_content.

I'm sorry for bringing some sass here, especially fairly late-in-the-game sass. I've only recently discovered all of the discussions happening about FSE. Please feel free to tear me apart!

@pagelab
Copy link
Contributor

pagelab commented Feb 26, 2021

Why do I get the impression that the problem being solved here isn't "how do we make dynamic templates from static files" but rather "how do we get rid of that pesky PHP"? If that is the case can we at least be honest about it?

Maybe #27144 is a better place to discuss this topic, but the problem is not definitely with PHP itself. We owe PHP a lot to say that. But one of the main goals of the Site Editor is indeed to lower the barrier to theme creation, which, as a result, means reducing dependence on a powerful programming language.

By using HTML files for templates and JSON for configuration, we can better achieve this goal.

The issue is that, since WordPress never had a proper template system, simply using a php file gives access to the full spectrum of PHP features – and all the problems, idiosyncrasies and gotchas that come with it.

And let's not forget that traditional methods for theme creation will continue to exist. So there's not that much to lose, I think.

@cr0ybot
Copy link
Contributor

cr0ybot commented Feb 26, 2021

Maybe #27144 is a better place to discuss this topic

No worries, I'm in there too, also late to the discussion.

By using HTML files for templates and JSON for configuration, we can better achieve this goal.

Is that objectively true, though, when this entire discussion has revolved around inserting more and more functionality into HTML comments or otherwise adding a new templating syntax into the mix? To your point, it would restrict (but not eliminate, considering some of this thread's suggestions) server-side logic for templates, forcing FSE theme developers to do things a certain way.

I'm curious why we are talking about coding these templates manually at all if the ultimate goal is to lower the barrier to theme creation. Why not consider these as static files generated and packaged up for the purpose of "exporting" and "importing" via some WordPress back-end interface for building these themes in a visual editor?

Perhaps I'm getting off-topic, I do apologize. This issue is specifically about how to have dynamic values inside static HTML files and here I am just saying "don't", so on the whole this is probably not useful.

@carlomanf
Copy link

However, if the goal is for these theme files to be generated—as in, they are developed purely within the WordPress back-end and saved out/exported—and then imported on other sites, immediately pulled into the database as wp_template entries... I feel like I'm strangely ok with that? Is that the (potential) approach? If these are installed like any other theme with files sitting on the server and referenced during runtime then I would strongly advocate for PHP. But if they just serve as a reference package to be imported into the database and from there dynamically updated, I don't see why they couldn't just be the same kind of markup that WordPress already stores in post_content.

It's kind of a combination of both, since #27910 was merged. The file being pulled into the database as a wp_template entry does not happen immediately, it happens when the user "edits" it. Until that point, the file continues to be referenced during runtime, to allow the site to absorb any future updates that are released by the theme.

But one of the main goals of the Site Editor is indeed to lower the barrier to theme creation

Is it? Theme customisation, yes, but theme creation?

I'm curious why we are talking about coding these templates manually at all if the ultimate goal is to lower the barrier to theme creation. Why not consider these as static files generated and packaged up for the purpose of "exporting" and "importing" via some WordPress back-end interface for building these themes in a visual editor?

I agree with you on this, but I think it's more relevant to #27144, so see my reply there.

@pagelab
Copy link
Contributor

pagelab commented Feb 27, 2021

Is it? Theme customisation, yes, but theme creation?

Yes, this is an actual goal (see #27941).

@caseymilne
Copy link

When it comes to images and paths I think it's worth considering that any path to an image could be defined as either:

  1. Valid Path (publicly accessible image, or local readable image)
  2. Invalid Path (cannot read due to faulty path or non-public URL)
  3. Bundled Object (file/image reference is available directly as a base_64 object)
  4. Bundled in Export Package Location (file/image exists in the package being parsed such as /files/ format in a zipped export).

I'm not sure if blocks should be exported with full URL's to their images (or if tokens should be used instead) but let's imagine we do move a block or template that contains full URL's to a different site. I think at this point it's the job of the Site Editor to do a routine on save to validate and fix the block, which would look like:

If valid path, import image and reference it. If invalid path, show validation error and put block in invalid state. If bundled object import to media and reference it. If bundled in export package, import to media and reference it.

Something in the process has to be smart(er). So the question is smarter blocks, smarter export, smarter import? I say put the focus on the importer/validator. Do validate/import on save. Presume that whatever goes into the editor could be copy/paste from a different site or template reload (after local file changes to FSE theme). Handle it there in a similar way to sanitization, provide importing when possible, parse tokens if possible, invalidate the block if needed (or just render broken).

@cr0ybot
Copy link
Contributor

cr0ybot commented Aug 31, 2021

Is this still really an issue? Block templates and template parts use blocks. Those blocks can be dynamic blocks that get info server-side. Same way that content within a post can have dynamic values without some kind of custom filetype or markup (other than block comments, of course).

@johnstonphilip
Copy link
Contributor Author

@cr0ybot I believe it is still an issue yes.

@onetrev
Copy link

onetrev commented Feb 13, 2024

I wonder if this issue will be solved with the completion of the Block Bindings API, which is currently a work in progress?

@onetrev
Copy link

onetrev commented Feb 14, 2024

This is most definitely still an issue @cr0ybot. I'm just surprised there's not a lot more traction on this. How are other developers and agencies dealing with this? For anyone making bespoke sites for clients using the Site Editor, this is a major pain point, as @johnstonphilip noted. My example is you create a site-footer.html that has the client's logo. So you'd have something like this:

<!-- wp:image {"width":"34px","height":"auto","sizeSlug":"full","linkDestination":"none"} -->
<figure class="wp-block-image size-full is-resized">
<img src="https://my.localhost/wp-content/uploads/icon.png" alt="" class="" style="width:34px;height:auto"/>
</figure>

When you migrate the site to a staging server or migrate it from staging to a live server, your image fails to load in all cases since of course the path is to your local server still.

The only fix I've found for now is to remove the domain from the absolute path. So you have this as your image scr now <img src="/wp-content/uploads/icon.png">. While a pain, that works on the front end at least. However, in the Site Editor, that results in your content failing to load. It's odd because if you inspect the broken image the path is correct and it loads in your inspector, but the site editor doesn't load it. Either way, this is not a viable fix, but a desperate hack to get by for now.

@cr0ybot
Copy link
Contributor

cr0ybot commented Feb 14, 2024

It's so funny to come back to these threads and find a past me who just didn't get it and left some snarky comments, and then later came back to agree with how things were done after having used the new system for a while.

@onetrev In your example I'm not sure the Block Bindings API will do what you want in this case, since that (at least in the beginning) only supports post meta, and you're working on a template part that is likely outside of the context of any specific post. However, I am excited about what block bindings will bring. I think that will probably end up as the more direct answer to this issue's primary objective.

I'll assume there's a reason you aren't using the Site Logo block, which would solve this problem at least on the surface level.

When I last commented I was only thinking about dynamic blocks and how building custom ones solves this problem, since you can output whatever you want via PHP and insert the block into a template/part via standard HTML. However, I'd recommend checking out how core themes are handling this sort of thing. The answer is theme block patterns (available since WP 5.4, March 2020), which are PHP files and can be dynamic. This is how they're including an image from the theme in the hero banner pattern:

<!-- wp:image {"align":"wide","sizeSlug":"full","linkDestination":"none","className":"is-style-rounded"} -->
<figure class="wp-block-image alignwide size-full is-style-rounded">
	<img src="<?php echo esc_url( get_template_directory_uri() ); ?>/assets/images/building-exterior.webp" alt="<?php esc_attr_e( 'Building exterior in Toronto, Canada', 'twentytwentyfour' ); ?>" />
</figure>
<!-- /wp:image -->

You could make a pattern for the entire contents of the footer or just the logo, depending on how you envision a user editing it in the backend. Then you just include it into your template or part:

<!-- wp:pattern {"slug":"themeslug/footer-logo"} /-->

Therefore, there is a way to insert dynamic values driven by PHP into theme templates and parts, so I ask again: Is this really still an issue?

@onetrev
Copy link

onetrev commented Feb 15, 2024

No worries @cr0ybot I think back in those days and feel like a lot of cheese was moving, the Block Editor was most definitely not ready for prime time, and so it was easy to be a bit snarky. 😏

Good point regarding the Site Logo block, for sure can use that where applicable as the first option. For when you can't or other dynamic data, great idea to try using patterns. Thank you! Maybe a better solution with the Block API or something else will come along, but there's a working solution so I now say this issue could be closed.

One remaining pain point for me still though is if you want to go and edit a pattern in the Site Editor and you've added PHP to that pattern, if you drop the pattern code back into the Site Editor you get a lot of "This block contains unexpected or invalid content". And it mangles your code a bit sometimes, like converting > to HTML entity values. Major bummer because really you need to use the editor to efficiently edit patterns, it's not very easy to continue editing those just in your local code editor. Any workaround for this?

Note for anyone confused by this too. I misunderstood the note about patterns becoming static where it says:

"PHP blocks are only run upon loading the block editor in order to compute the patterns. Once inserted into a post, patterns are static."

I took that to mean it's static no matter where you use them. But, if patterns are added to a template (.html files), then it's still dynamic. It's only an issue if you insert the pattern directly into your post / page content.

@justintadlock
Copy link
Contributor

justintadlock commented Mar 25, 2024

I believe both Patterns and the Block Bindings API effectively solve the majority of issues around dynamic data. The biggest limitations for Block Bindings API right now (other than some UI polish) is the use of the API in more blocks. As of WP 6.5, it's limited to Image, Heading, Paragraph, and Button (and a subset of their attributes). So this issue is not fully solved, but it's well on its way.

The thing that it will need to be paired with is some sort of API for conditionals/rules for when a block gets displayed, but that's outside the scope of this ticket.

@carolinan
Copy link
Contributor

Translations still need to be solved...

@justintadlock
Copy link
Contributor

justintadlock commented Mar 25, 2024

Translations still need to be solved...

This is the one area I'm still having issues with and still forgot about it, Partially because I can't use patterns for some use cases (e.g. single templates are problematic when offloading to a template pattern).

@onetrev
Copy link

onetrev commented Apr 5, 2024

I believe both Patterns and the Block Bindings API effectively solve the majority of issues around dynamic data.

For clarification @justintadlock are you thinking that things like dynamic dates and theme image paths would be handled by the former (Patterns) since you can then use PHP functions for those in Patterns? I agree it solves it, but it is awkward in that once you do use PHP in your pattern you can no longer take your pattern code back into the WP editor.

Or, are you thinking that using the Block Bindings API and the ability to use a get_value_callback would be the way to go (as shown in this Make Core post)?

While using the callback function enables you to keep editing your pattern in the editor, it sure seems like a lot of work compared to just dropping in <?php echo date( 'Y' ); ?> in your pattern PHP file. 🤔 Long story short, ideally we can have the best of both worlds, quick and easy code while maintaining WP editor edit-ability.

@onetrev
Copy link

onetrev commented Jun 26, 2024

Reading about the Bits Proposal, I'm now wondering if that would be the full solution to this issue (if it ends up landing in WP)? There is also a discussion about it, so I'm linking it to ensure cross referencing with this issue.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
[Feature] Themes Questions or issues with incorporating or styling blocks in a theme. [Type] Enhancement A suggestion for improvement.
Projects
None yet
Development

No branches or pull requests