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

Fix REST API auto-draft creation #26650

Merged
merged 13 commits into from Nov 9, 2020
44 changes: 44 additions & 0 deletions docs/architecture/fse-templates.md
@@ -0,0 +1,44 @@
### Template and template parts flows

> This is the documentation for the current implementation of the block-based templates and template parts themes. This is part of the Full Site Editing project. These features are still experimental in the plugin. “Experimental” means this is just an early implementation that is subject to potential drastic and breaking changes in iterations based on feedback from users, contributors and theme authors.

This document will explain the internals of how templates and templates parts are rendered in the frontend and edited in the backend. For an introduction about block-based themes and Full site editing templates, refer to the [block-based themes documentation](/docs/designers-developers/developers/themes/block-based-themes.md).

## Storage

Just like the regular templates, the block-based templates live initially as files in the theme folder but the main difference is that the user can edit these templates in the UI in the Site Editor.

When a user edit a template (or template-part), the initial theme template file is kept as is but a forked version of the template is saved to the `wp_template` custom post type (or `wp_template_part` for template parts).

These capabilities mean that any point of time, a mix of template files (from the theme) and CPT templates (the edited templates) are used to render the frontend of the site.

## Synchronization

In order to simplify the algorithm used to edit and render the templates from two different places, we performed an operation called "template synchronization".

The synchronization consists of duplicating the theme templates in the `wp_template` (and `wp_template_part`) custom templates with an `auto-draft` status. When a user edits these templates, the status is updated to `publish`.

This means:

- The rendering/fetching of templates only need to consider the custom post type templates. It is not necessary to fetch the template files from the theme folder directly. The synchronization will ensure these are duplicated in the CPT.
- Untouched theme templates have the `auto-draft` status.
- Edited theme templates have the `publish` status.

The synchronization is important for two different flows:

- When editing the template and template parts, the site editor frontend fetches the edited and available templates throught the REST API. This means that for all `GET` API requests performed to the `wp-templates` and `wp-template-parts` end point, the synchronization is required.
- When rendering a template (sometimes referred to as "resolving a template"): this is the algorithm that WordPress follows to traverse the template hierarchy and find the right template to render for the current page being loaded.
- When exporting a block-based theme, we need to export all its templates back as files. The synchronizaion is required in order to simplify the operation and only export the CPT templates.

## Switching themes

Since block-based themes make use of templates that can refer to each other and that can be saved to a custom post type, it becomes possible to mix templates and template parts from different themes. For example:

- A user might like the "header" template part of a theme A and would like to use it in theme B.
- A user might like the "contact" template from theme A and would like to use it in theme B.

Enabling these flows will require well thought UIs and experience. For the current phase of Full-site editing, we're starting by forbidding these possibilities and making template and template-parts theme specific.

That said, it is still important to keep track of where the template and template part come from initially. From which theme, it's based. We do so by saving a `theme` post meta containing the theme identifier for each template and template part CPT entry.

In the future, we might consider allowing the user to mix template and template parts with different `theme` post meta values.
1 change: 1 addition & 0 deletions docs/architecture/readme.md
Expand Up @@ -10,3 +10,4 @@ Let’s look at the big picture and the architectural and UX principles of the b
- What are the decision decisions behind the Data Module?
- [Why is Puppeteer the tool of choice for end-to-end tests?](/docs/architecture/automated-testing.md)
- [What's the difference between the different editor packages? What's the purpose of each package?](/docs/architecture/modularity.md#whats-the-difference-between-the-different-editor-packages-whats-the-purpose-of-each-package)
- [Template and template parts flows](/docs/architecture/fse-templates.md)
Expand Up @@ -4,7 +4,7 @@

> Documentation has been shared early to surface what’s being worked on and invite feedback from those experimenting with the APIs. You can provide feedback in the weekly #core-editor chats where the latest progress of this effort will be shared and discussed, or async via Github issues.

**Note:** In order to use these features, make sure to enable the "Full Site Editing" flag from the **Experiments** page of the Gutenberg plugin.
**Note:** In order to use these features, make sure to use a block-based theme.

## What is a block-based theme?

Expand Down
6 changes: 6 additions & 0 deletions docs/manifest.json
Expand Up @@ -77,6 +77,12 @@
"markdown_source": "../docs/architecture/automated-testing.md",
"parent": "architecture"
},
{
"title": "FseTemplates",
"slug": "fse-templates",
"markdown_source": "../docs/architecture/fse-templates.md",
"parent": "architecture"
},
{
"title": "Developer Documentation",
"slug": "developers",
Expand Down
3 changes: 2 additions & 1 deletion docs/toc.json
Expand Up @@ -12,7 +12,8 @@
{ "docs/architecture/folder-structure.md": [] },
{ "docs/architecture/modularity.md": [] },
{ "docs/architecture/performance.md": [] },
{ "docs/architecture/automated-testing.md": [] }
{ "docs/architecture/automated-testing.md": [] },
{ "docs/architecture/fse-templates.md": [] }
] },
{ "docs/designers-developers/developers/README.md": [
{ "docs/designers-developers/developers/block-api/README.md": [
Expand Down
60 changes: 42 additions & 18 deletions lib/edit-site-export.php
Expand Up @@ -10,6 +10,11 @@
* and template parts from the site editor, and close the connection.
*/
function gutenberg_edit_site_export() {
// Theme templates and template parts need to be synchronized
// before the export.
_gutenberg_synchronize_theme_templates( 'template-part' );
_gutenberg_synchronize_theme_templates( 'template' );

// Create ZIP file and directories.
$filename = tempnam( get_temp_dir(), 'edit-site-export' );
$zip = new ZipArchive();
Expand All @@ -18,26 +23,44 @@ function gutenberg_edit_site_export() {
$zip->addEmptyDir( 'theme/block-templates' );
$zip->addEmptyDir( 'theme/block-template-parts' );

// Load files into ZIP file.
foreach ( get_template_types() as $template_type ) {
// Skip 'embed' for now because it is not a regular template type.
// Skip 'index' because it's a fallback that we handle differently.
if ( in_array( $template_type, array( 'embed', 'index' ), true ) ) {
continue;
}
$theme = wp_get_theme()->get_stylesheet();

$current_template = gutenberg_find_template_post_and_parts( $template_type );
if ( isset( $current_template ) ) {
$zip->addFromString(
'theme/block-templates/' . $current_template['template_post']->post_name . '.html',
gutenberg_strip_post_ids_from_template_part_blocks( $current_template['template_post']->post_content )
);
// Load templates into the zip file.
$template_query = new WP_Query(
array(
'post_type' => 'wp_template',
'post_status' => array( 'publish', 'auto-draft' ),
'meta_key' => 'theme',
'meta_value' => $theme,
'posts_per_page' => -1,
'no_found_rows' => true,
)
);
while ( $template_query->have_posts() ) {
$template = $template_query->next_post();
$zip->addFromString(
'theme/block-templates/' . $template->post_name . '.html',
gutenberg_strip_post_ids_from_template_part_blocks( $template->post_content )
);
}

foreach ( $current_template['template_part_ids'] as $template_part_id ) {
$template_part = get_post( $template_part_id );
$zip->addFromString( 'theme/block-template-parts/' . $template_part->post_name . '.html', $template_part->post_content );
}
}
// Load template parts into the zip file.
$template_part_query = new WP_Query(
array(
'post_type' => 'wp_template_part',
'post_status' => array( 'publish', 'auto-draft' ),
'meta_key' => 'theme',
'meta_value' => $theme,
'posts_per_page' => -1,
'no_found_rows' => true,
)
);
while ( $template_part_query->have_posts() ) {
$template_part = $template_part_query->next_post();
$zip->addFromString(
'theme/block-template-parts/' . $template_part->post_name . '.html',
gutenberg_strip_post_ids_from_template_part_blocks( $template_part->post_content )
);
}

// Send back the ZIP file.
Expand All @@ -49,6 +72,7 @@ function gutenberg_edit_site_export() {
echo readfile( $filename );
die();
}

add_action(
'rest_api_init',
function () {
Expand Down
3 changes: 3 additions & 0 deletions lib/load.php
Expand Up @@ -93,15 +93,18 @@ function gutenberg_is_experiment_enabled( $name ) {
if ( ! class_exists( 'WP_Block_List' ) ) {
require dirname( __FILE__ ) . '/class-wp-block-list.php';
}

if ( ! class_exists( 'WP_Widget_Block' ) ) {
require_once dirname( __FILE__ ) . '/class-wp-widget-block.php';
}

require_once dirname( __FILE__ ) . '/widgets-page.php';

require dirname( __FILE__ ) . '/compat.php';
require dirname( __FILE__ ) . '/utils.php';

require dirname( __FILE__ ) . '/full-site-editing.php';
require dirname( __FILE__ ) . '/templates-sync.php';
require dirname( __FILE__ ) . '/templates.php';
require dirname( __FILE__ ) . '/template-parts.php';
require dirname( __FILE__ ) . '/template-loader.php';
Expand Down