-
Notifications
You must be signed in to change notification settings - Fork 4.2k
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
Comments
Just adding that this seems related to #20542. |
php is a great template engine and you need to use it. |
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. |
@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. |
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). |
@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:
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: Structural vs Starter contentI think that what is
For them, everything is
Therefore, I would argue that Many blocks affectedTo try and narrow it down, I tested all of the
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: |
Related to this is the translations. |
Related: #21204 |
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? |
ACF are another dynamic example. For example, having a page where the content is already included from using |
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.
Using block context (in the example case: to the
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 |
Some more thoughts on 'contextual' blocks here: #21728 |
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 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 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 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. |
@epiqueras Very interesting thought! Can you explain a bit more about this?
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? |
Yeah, that was basically my proposal. @epiqueras Can you elaborate why that's a no-go?
Agree.
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 (
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 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
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. |
The image block would know when to insert it or offer a setting to do so.
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.
We can avoid changing anything with the current upload process. When exporting a theme, we include the referenced images in the templates in the
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. |
@epiqueras if the |
The images can be copied to the uploads folder when that happens. |
@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. |
I don't think a single |
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. |
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 One thing to consider is that demarcations of the |
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 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. |
Maybe we could tag attributes as dynamic with something other than html comments, like using a The <!-- 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: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 <!-- wp:paragraph -->
<p>
<!-- wp:i18n {"text":"Text to translate"} -->
Text to translate
<!-- /wp:i18n -->
</p>
<!-- /wp:paragraph --> |
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 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! |
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 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. |
No worries, I'm in there too, also late to the discussion.
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. |
It's kind of a combination of both, since #27910 was merged. The file being pulled into the database as a
Is it? Theme customisation, yes, but theme creation?
I agree with you on this, but I think it's more relevant to #27144, so see my reply there. |
Yes, this is an actual goal (see #27941). |
When it comes to images and paths I think it's worth considering that any path to an image could be defined as either:
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). |
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). |
@cr0ybot I believe it is still an issue yes. |
I wonder if this issue will be solved with the completion of the Block Bindings API, which is currently a work in progress? |
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
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 |
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? |
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 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. |
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. |
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). |
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 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 |
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. |
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 aswp_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:
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.
The text was updated successfully, but these errors were encountered: