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

Site Editor: Create auto-drafts for modified files only #26383

Merged
merged 3 commits into from
Nov 16, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 7 additions & 7 deletions docs/architecture/fse-templates.md
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
### Template and template parts flows
### Template and template part 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 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).
When a user edits 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.
These capabilities mean that at any point in 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

Expand All @@ -26,15 +26,15 @@ This means:

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 editing the template and template parts, the site editor frontend fetches the edited and available templates through the REST API. This means that for all `GET` API requests performed to the `wp-templates` and `wp-template-parts` endpoint 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.
- When exporting a block-based theme, we need to export all its templates back as files. The synchronization is required 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 "header" template part of 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.
Expand Down
3 changes: 0 additions & 3 deletions lib/template-loader.php
Original file line number Diff line number Diff line change
Expand Up @@ -93,9 +93,6 @@ function get_template_hierarchy( $template_type ) {
function gutenberg_override_query_template( $template, $type, array $templates = array() ) {
global $_wp_current_template_content;

// Create auto-drafts for theme templates.
_gutenberg_synchronize_theme_templates( 'template' );

$current_template = gutenberg_resolve_template( $type, $templates );

if ( $current_template ) {
Expand Down
22 changes: 0 additions & 22 deletions lib/template-parts.php
Original file line number Diff line number Diff line change
Expand Up @@ -203,25 +203,3 @@ function filter_rest_wp_template_part_query( $args, $request ) {
return $args;
}
add_filter( 'rest_wp_template_part_query', 'filter_rest_wp_template_part_query', 99, 2 );

/**
* Run synchrnonization for template part API requests
*
* @param mixed $dispatch_result Dispatch result, will be used if not empty.
* @param WP_REST_Request $request Request used to generate the response.
* @param string $route Route matched for the request.
* @return mixed Dispatch result.
*/
function gutenberg_filter_rest_wp_template_part_dispatch( $dispatch_result, $request, $route ) {
if ( null !== $dispatch_result ) {
return $dispatch_result;
}

if ( 0 === strpos( $route, '/wp/v2/template-parts' ) && 'GET' === $request->get_method() ) {
_gutenberg_synchronize_theme_templates( 'template-part' );
}

return null;
}

add_filter( 'rest_dispatch_request', 'gutenberg_filter_rest_wp_template_part_dispatch', 10, 3 );
86 changes: 76 additions & 10 deletions lib/templates-sync.php
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,11 @@ function _gutenberg_create_auto_draft_for_template( $post_type, $slug, $theme, $
'post_name' => $slug,
)
);
} else {
// Potentially we could decide to update the content if different.
} elseif ( 'auto-draft' === $post->post_status && $content !== $post->post_content ) {
// If the template already exists, but it was never changed by the user
// and the template file content changed then update the content of auto-draft.
$post->post_content = $content;
wp_insert_post( $post );
}
}

Expand Down Expand Up @@ -86,23 +89,86 @@ function _gutenberg_synchronize_theme_templates( $template_type ) {
'template' => 'block-templates',
'template-part' => 'block-template-parts',
);
$themes = array(
get_stylesheet() => get_stylesheet_directory(),
get_template() => get_template_directory(),
);

// Get file paths for all theme supplied template that changed since last check.
$template_files = array();
$option_name = 'gutenberg_last_synchronize_theme_' . $template_type . '_checks';
$last_checks = get_option( $option_name, array() );
$current_time = time();
foreach ( $themes as $theme_slug => $theme_dir ) {
$last_check = isset( $last_checks[ $theme_slug ] ) ? $last_checks[ $theme_slug ] : 0;

$theme_template_files = _gutenberg_get_template_paths( $theme_dir . '/' . $template_base_paths[ $template_type ] );
foreach ( $theme_template_files as $template_file ) {
if ( filemtime( $template_file ) > $last_check ) {
$template_files[] = array(
'path' => $template_file,
'theme' => $theme_slug,
);
}
}

// Get file paths for all theme supplied template.
$template_files = _gutenberg_get_template_paths( get_stylesheet_directory() . '/' . $template_base_paths[ $template_type ] );
if ( is_child_theme() ) {
$template_files = array_merge( $template_files, _gutenberg_get_template_paths( get_template_directory() . '/' . $template_base_paths[ $template_type ] ) );
$last_checks[ $theme_slug ] = $current_time;
}

// Build and save each template part.
foreach ( $template_files as $template_file ) {
$content = file_get_contents( $template_file );
$path = $template_file['path'];
$theme = $template_file['theme'];
$template_base_path = $template_base_paths[ $template_type ];

$content = file_get_contents( $path );
$slug = substr(
$template_file,
$path,
// Starting position of slug.
strpos( $template_file, $template_base_paths[ $template_type ] . '/' ) + 1 + strlen( $template_base_paths[ $template_type ] ),
strpos( $path, $template_base_path . '/' ) + 1 + strlen( $template_base_path ),
// Subtract ending '.html'.
-5
);
_gutenberg_create_auto_draft_for_template( $template_post_types[ $template_type ], $slug, wp_get_theme()->get_stylesheet(), $content );
_gutenberg_create_auto_draft_for_template( $template_post_types[ $template_type ], $slug, $theme, $content );
}
david-szabo97 marked this conversation as resolved.
Show resolved Hide resolved

update_option( $option_name, $last_checks );
}

/**
* Synchronize changed template and template part files after WordPress is loaded
*/
function gutenberg_synchronize_theme_templates_on_load() {
if ( ! gutenberg_is_fse_theme() ) {
return;
}

_gutenberg_synchronize_theme_templates( 'template-part' );
_gutenberg_synchronize_theme_templates( 'template' );
}
add_action( 'wp_loaded', 'gutenberg_synchronize_theme_templates_on_load' );
david-szabo97 marked this conversation as resolved.
Show resolved Hide resolved

/**
* Clears synchronization last check timestamps.
*/
function gutenberg_clear_synchronize_last_checks() {
update_option( 'gutenberg_last_synchronize_theme_template_checks', array() );
update_option( 'gutenberg_last_synchronize_theme_template-part_checks', array() );
}

// Clear synchronization last check timestamps after trashing a template or template part.
add_action( 'trash_wp_template', 'gutenberg_clear_synchronize_last_checks' );
add_action( 'trash_wp_template_part', 'gutenberg_clear_synchronize_last_checks' );

/**
* Clear synchronization last check timestamps after deleting a template or template part.
*
* @param int $post_id ID of the deleted post.
* @param WP_Post $post WP_Post instance of the deleted post.
*/
function gutenberg_clear_synchronize_last_checks_after_delete( $postid, $post ) {
if ( 'wp_template' !== $post->post_type || 'wp_template_part' !== $post->post_type ) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this be 'wp_template' === $post->post_type?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do we have a PR to fix the issues introduced here so I can backport them to 9.4?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I started one here #27068.

gutenberg_clear_synchronize_last_checks();
}
}
add_action( 'after_delete_post', 'gutenberg_clear_synchronize_last_checks_after_delete', 10, 2 );
21 changes: 0 additions & 21 deletions lib/templates.php
Original file line number Diff line number Diff line change
Expand Up @@ -258,24 +258,3 @@ function filter_rest_wp_template_query( $args, $request ) {
}
add_filter( 'rest_wp_template_query', 'filter_rest_wp_template_query', 99, 2 );

/**
* Run synchrnonization for template API requests
*
* @param mixed $dispatch_result Dispatch result, will be used if not empty.
* @param WP_REST_Request $request Request used to generate the response.
* @param string $route Route matched for the request.
* @return mixed Dispatch result.
*/
function gutenberg_filter_rest_wp_template_dispatch( $dispatch_result, $request, $route ) {
if ( null !== $dispatch_result ) {
return $dispatch_result;
}

if ( 0 === strpos( $route, '/wp/v2/templates' ) && 'GET' === $request->get_method() ) {
_gutenberg_synchronize_theme_templates( 'template' );
}

return null;
}

add_filter( 'rest_dispatch_request', 'gutenberg_filter_rest_wp_template_dispatch', 10, 3 );