Skip to content

Commit

Permalink
Site Editor: Create auto-drafts for modified files only (#26383)
Browse files Browse the repository at this point in the history
Co-authored-by: Jorge <[email protected]>
  • Loading branch information
david-szabo97 and jorgefilipecosta authored Nov 16, 2020
1 parent b245989 commit 0e6da94
Show file tree
Hide file tree
Showing 5 changed files with 83 additions and 63 deletions.
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 );
}

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' );

/**
* 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 ) {
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 );

0 comments on commit 0e6da94

Please sign in to comment.