From 0e6da941b265d8a4ec165386886139ac7a5b8288 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A1vid=20Szab=C3=B3?= Date: Mon, 16 Nov 2020 19:18:21 +0100 Subject: [PATCH] Site Editor: Create auto-drafts for modified files only (#26383) Co-authored-by: Jorge --- docs/architecture/fse-templates.md | 14 ++--- lib/template-loader.php | 3 -- lib/template-parts.php | 22 -------- lib/templates-sync.php | 86 ++++++++++++++++++++++++++---- lib/templates.php | 21 -------- 5 files changed, 83 insertions(+), 63 deletions(-) diff --git a/docs/architecture/fse-templates.md b/docs/architecture/fse-templates.md index 0bf79b012670d..293b7f1f7bd30 100644 --- a/docs/architecture/fse-templates.md +++ b/docs/architecture/fse-templates.md @@ -1,6 +1,6 @@ -### 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). @@ -8,9 +8,9 @@ This document will explain the internals of how templates and templates parts ar 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 @@ -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. diff --git a/lib/template-loader.php b/lib/template-loader.php index 9862c337c4679..68291ca561e63 100644 --- a/lib/template-loader.php +++ b/lib/template-loader.php @@ -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 ) { diff --git a/lib/template-parts.php b/lib/template-parts.php index 503ab89e31979..c3bf1d43e9f43 100644 --- a/lib/template-parts.php +++ b/lib/template-parts.php @@ -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 ); diff --git a/lib/templates-sync.php b/lib/templates-sync.php index 934c885d8656c..7f72f5e167c64 100644 --- a/lib/templates-sync.php +++ b/lib/templates-sync.php @@ -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 ); } } @@ -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 ); diff --git a/lib/templates.php b/lib/templates.php index 198804fccb6f5..c46fb76e95168 100644 --- a/lib/templates.php +++ b/lib/templates.php @@ -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 );