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
60 changes: 42 additions & 18 deletions lib/edit-site-export.php
Original file line number Diff line number Diff line change
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( 'TextDomain' );
youknowriad marked this conversation as resolved.
Show resolved Hide resolved

$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
Original file line number Diff line number Diff line change
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
226 changes: 23 additions & 203 deletions lib/template-loader.php
Original file line number Diff line number Diff line change
Expand Up @@ -93,13 +93,16 @@ function get_template_hierarchy( $template_type ) {
function gutenberg_override_query_template( $template, $type, array $templates = array() ) {
global $_wp_current_template_content;

$current_template = gutenberg_find_template_post_and_parts( $type, $templates );
// Create auto-drafts for theme templates.
_gutenberg_synchronize_theme_templates( 'template' );

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

if ( $current_template ) {
$_wp_current_template_content = empty( $current_template['template_post']->post_content ) ? __( 'Empty template.', 'gutenberg' ) : $current_template['template_post']->post_content;
$_wp_current_template_content = empty( $current_template->post_content ) ? __( 'Empty template.', 'gutenberg' ) : $current_template->post_content;

if ( isset( $_GET['_wp-find-template'] ) ) {
wp_send_json_success( $current_template['template_post'] );
wp_send_json_success( $current_template );
}
} else {
if ( 'index' === $type ) {
Expand All @@ -124,84 +127,7 @@ function gutenberg_override_query_template( $template, $type, array $templates =
}

/**
* Recursively traverses a block tree, creating auto drafts
* for any encountered template parts without a fixed post.
*
* @access private
*
* @param array $block The root block to start traversing from.
* @return int[] A list of template parts IDs for the given block.
*/
function create_auto_draft_for_template_part_block( $block ) {
$template_part_ids = array();

if ( 'core/template-part' === $block['blockName'] && isset( $block['attrs']['slug'] ) ) {
if ( isset( $block['attrs']['postId'] ) ) {
// Template part is customized.
$template_part_id = $block['attrs']['postId'];
} else {
// A published post might already exist if this template part
// was customized elsewhere or if it's part of a customized
// template. We also check if an auto-draft was already created
// because preloading can make this run twice, so, different code
// paths can end up with different posts for the same template part.
// E.g. The server could send back post ID 1 to the client, preload,
// and create another auto-draft. So, if the client tries to resolve the
// post ID from the slug and theme, it won't match with what the server sent.
$template_part_query = new WP_Query(
array(
'post_type' => 'wp_template_part',
'post_status' => array( 'publish', 'auto-draft' ),
'title' => $block['attrs']['slug'],
'meta_key' => 'theme',
'meta_value' => $block['attrs']['theme'],
'posts_per_page' => 1,
'no_found_rows' => true,
)
);
$template_part_post = $template_part_query->have_posts() ? $template_part_query->next_post() : null;
if ( $template_part_post && 'auto-draft' !== $template_part_post->post_status ) {
$template_part_id = $template_part_post->ID;
} else {
// Template part is not customized, get it from a file and make an auto-draft for it, unless one already exists
// and the underlying file hasn't changed.
$template_part_file_path = get_stylesheet_directory() . '/block-template-parts/' . $block['attrs']['slug'] . '.html';
if ( ! file_exists( $template_part_file_path ) ) {
$template_part_file_path = false;
}

if ( $template_part_file_path ) {
$file_contents = file_get_contents( $template_part_file_path );
if ( $template_part_post && $template_part_post->post_content === $file_contents ) {
$template_part_id = $template_part_post->ID;
} else {
$template_part_id = wp_insert_post(
array(
'post_content' => $file_contents,
'post_title' => $block['attrs']['slug'],
'post_status' => 'auto-draft',
'post_type' => 'wp_template_part',
'post_name' => $block['attrs']['slug'],
'meta_input' => array(
'theme' => $block['attrs']['theme'],
),
)
);
}
}
}
}
$template_part_ids[ $block['attrs']['slug'] ] = $template_part_id;
}

foreach ( $block['innerBlocks'] as $inner_block ) {
$template_part_ids = array_merge( $template_part_ids, create_auto_draft_for_template_part_block( $inner_block ) );
}
return $template_part_ids;
}

/**
* Return the correct 'wp_template' post and template part IDs for the current template.
* Return the correct 'wp_template' to render fot the request template type.
*
* Accepts an optional $template_hierarchy argument as a hint.
*
Expand All @@ -212,7 +138,7 @@ function create_auto_draft_for_template_part_block( $block ) {
* @type int[] A list of template parts IDs for the template.
* }
*/
function gutenberg_find_template_post_and_parts( $template_type, $template_hierarchy = array() ) {
function gutenberg_resolve_template( $template_type, $template_hierarchy = array() ) {
if ( ! $template_type ) {
return null;
}
Expand All @@ -230,110 +156,35 @@ function gutenberg_find_template_post_and_parts( $template_type, $template_hiera
$template_hierarchy
);

// Find most specific 'wp_template' post matching the hierarchy.
// Find all potential templates 'wp_template' post matching the hierarchy.
$template_query = new WP_Query(
array(
'post_type' => 'wp_template',
'post_status' => 'publish',
'post_status' => array( 'publish', 'auto-draft' ),
'post_name__in' => $slugs,
'orderby' => 'post_name__in',
'posts_per_page' => 1,
'posts_per_page' => -1,
'no_found_rows' => true,
'meta_key' => 'theme',
'meta_value' => wp_get_theme()->get( 'TextDomain' ),
youknowriad marked this conversation as resolved.
Show resolved Hide resolved
)
);
$templates = $template_query->get_posts();

$current_template_post = $template_query->have_posts() ? $template_query->next_post() : null;

// Order these templates per slug priority.
// Build map of template slugs to their priority in the current hierarchy.
$slug_priorities = array_flip( $slugs );

// See if there is a theme block template with higher priority than the resolved template post.
$higher_priority_block_template_path = null;
$higher_priority_block_template_priority = PHP_INT_MAX;
$block_template_files = gutenberg_get_template_paths();
foreach ( $block_template_files as $path ) {
if ( ! isset( $slug_priorities[ basename( $path, '.html' ) ] ) ) {
continue;
usort(
$templates,
function ( $template_a, $template_b ) use ( $slug_priorities ) {
$priority_a = $slug_priorities[ $template_a->post_name ] * 2 + ( 'publish' === $template_a->post_status ? 1 : 0 );
$priority_b = $slug_priorities[ $template_b->post_name ] * 2 + ( 'publish' === $template_b->post_status ? 1 : 0 );
return $priority_b - $priority_a;
}
$theme_block_template_priority = $slug_priorities[ basename( $path, '.html' ) ];
if (
$theme_block_template_priority < $higher_priority_block_template_priority &&
( empty( $current_template_post ) || $theme_block_template_priority < $slug_priorities[ $current_template_post->post_name ] )
) {
$higher_priority_block_template_path = $path;
$higher_priority_block_template_priority = $theme_block_template_priority;
}
}

// If there is, use it instead.
if ( isset( $higher_priority_block_template_path ) ) {
$post_name = basename( $higher_priority_block_template_path, '.html' );
$file_contents = file_get_contents( $higher_priority_block_template_path );
$current_template_post = array(
'post_content' => $file_contents,
'post_title' => $post_name,
'post_status' => 'auto-draft',
'post_type' => 'wp_template',
'post_name' => $post_name,
);
if ( is_admin() || defined( 'REST_REQUEST' ) ) {
$template_query = new WP_Query(
array(
'post_type' => 'wp_template',
'post_status' => 'auto-draft',
'name' => $post_name,
'posts_per_page' => 1,
'no_found_rows' => true,
)
);
$current_template_post = $template_query->have_posts() ? $template_query->next_post() : $current_template_post;

// Only create auto-draft of block template for editing
// in admin screens, when necessary, because the underlying
// file has changed.
if ( is_array( $current_template_post ) || $current_template_post->post_content !== $file_contents ) {
if ( ! is_array( $current_template_post ) ) {
$current_template_post->post_content = $file_contents;
}
$current_template_post = get_post(
wp_insert_post( $current_template_post )
);
}
} else {
$current_template_post = new WP_Post(
(object) $current_template_post
);
}
}

// If we haven't found any template post by here, it means that this theme doesn't even come with a fallback
// `index.html` block template. We create one so that people that are trying to access the editor are greeted
// with a blank page rather than an error.
if ( ! $current_template_post && ( is_admin() || defined( 'REST_REQUEST' ) ) ) {
$current_template_post = array(
'post_title' => 'index',
'post_status' => 'auto-draft',
'post_type' => 'wp_template',
'post_name' => 'index',
);
$current_template_post = get_post(
wp_insert_post( $current_template_post )
);
}
);

if ( $current_template_post ) {
$template_part_ids = array();
if ( is_admin() || defined( 'REST_REQUEST' ) ) {
foreach ( parse_blocks( $current_template_post->post_content ) as $block ) {
$template_part_ids = array_merge( $template_part_ids, create_auto_draft_for_template_part_block( $block ) );
}
}
return array(
'template_post' => $current_template_post,
'template_part_ids' => $template_part_ids,
);
}
return null;
return count( $templates ) ? $templates[0] : null;
}

/**
Expand Down Expand Up @@ -396,37 +247,6 @@ function gutenberg_strip_php_suffix( $template_file ) {
return preg_replace( '/\.php$/', '', $template_file );
}

/**
* Extends default editor settings to enable template and template part editing.
*
* @param array $settings Default editor settings.
*
* @return array Filtered editor settings.
*/
function gutenberg_template_loader_filter_block_editor_settings( $settings ) {
global $post;

if ( ! $post ) {
return $settings;
}

// If this is the Site Editor, auto-drafts for template parts have already been generated
// through `filter_rest_wp_template_part_query`, when called via the REST API.
if ( isset( $settings['editSiteInitialState'] ) ) {
return $settings;
}

// Otherwise, create template part auto-drafts for the edited post.
$post = get_post();
foreach ( parse_blocks( $post->post_content ) as $block ) {
create_auto_draft_for_template_part_block( $block );
}

// TODO: Set editing mode and current template ID for editing modes support.
return $settings;
}
add_filter( 'block_editor_settings', 'gutenberg_template_loader_filter_block_editor_settings' );
youknowriad marked this conversation as resolved.
Show resolved Hide resolved

/**
* Removes post details from block context when rendering a block template.
*
Expand Down
Loading