From 44a7b815f46e1c5eebf73991792ff81120fa6c18 Mon Sep 17 00:00:00 2001 From: Riad Benguella Date: Mon, 28 Dec 2020 12:53:42 +0100 Subject: [PATCH 01/49] Avoid using auto-drafts for theme templates and template parts --- lib/full-site-editing/block-templates.php | 222 +++++++++++++ ...lass-wp-rest-template-parts-controller.php | 290 +++++++++++++++++ .../class-wp-rest-templates-controller.php | 291 ++++++++++++++++++ .../edit-site-export.php | 53 +--- .../edit-site-page.php | 0 .../full-site-editing.php | 0 .../template-loader.php | 25 +- .../template-parts.php | 63 +--- lib/full-site-editing/templates-utils.php | 6 +- lib/{ => full-site-editing}/templates.php | 128 +------- lib/load.php | 20 +- lib/templates-sync.php | 188 ----------- .../src/template-part/block.json | 3 - .../src/template-part/edit/index.js | 78 +---- .../src/template-part/edit/name-panel.js | 25 +- .../template-part/edit/placeholder/index.js | 1 - .../edit/selection/template-part-previews.js | 16 +- .../edit/use-template-part-post.js | 47 --- packages/core-data/src/entities.js | 56 +--- packages/core-data/src/resolvers.js | 4 +- 20 files changed, 901 insertions(+), 615 deletions(-) create mode 100644 lib/full-site-editing/block-templates.php create mode 100644 lib/full-site-editing/class-wp-rest-template-parts-controller.php create mode 100644 lib/full-site-editing/class-wp-rest-templates-controller.php rename lib/{ => full-site-editing}/edit-site-export.php (62%) rename lib/{ => full-site-editing}/edit-site-page.php (100%) rename lib/{ => full-site-editing}/full-site-editing.php (100%) rename lib/{ => full-site-editing}/template-loader.php (91%) rename lib/{ => full-site-editing}/template-parts.php (69%) rename lib/{ => full-site-editing}/templates.php (62%) delete mode 100644 lib/templates-sync.php delete mode 100644 packages/block-library/src/template-part/edit/use-template-part-post.js diff --git a/lib/full-site-editing/block-templates.php b/lib/full-site-editing/block-templates.php new file mode 100644 index 00000000000000..0768aa8de84dd4 --- /dev/null +++ b/lib/full-site-editing/block-templates.php @@ -0,0 +1,222 @@ + $file ) { + $path_list[] = $path; + } + } + return $path_list; +} + +/** + * Retrieves the template files from the theme. + * + * @access private + * @internal + * + * @param array $template_type wp_template or wp_template_part. + * + * @return array Template. + */ +function _gutenberg_get_template_files( $template_type ) { + $template_base_paths = array( + 'wp_template' => 'block-templates', + 'wp_template_part' => 'block-template-parts', + ); + $themes = array( + get_stylesheet() => get_stylesheet_directory(), + get_template() => get_template_directory(), + ); + + $template_files = array(); + foreach ( $themes as $theme_slug => $theme_dir ) { + $theme_template_files = _gutenberg_get_template_paths( $theme_dir . '/' . $template_base_paths[ $template_type ] ); + foreach ( $theme_template_files as $template_file ) { + $template_base_path = $template_base_paths[ $template_type ]; + $template_slug = substr( + $template_file, + // Starting position of slug. + strpos( $template_file, $template_base_path . DIRECTORY_SEPARATOR ) + 1 + strlen( $template_base_path ), + // Subtract ending '.html'. + -5 + ); + $template_files[] = array( + 'slug' => $template_slug, + 'path' => $template_file, + 'theme' => $theme_slug, + 'type' => $template_type, + ); + } + } + + return $template_files; +} + +/** + * Build a unified template object based a theme file. + * + * @param array $template_file Theme file. + * @param array $template_type wp_template or wp_template_part. + * + * @return stdClass Template. + */ +function _gutenberg_build_template_result_from_file( $template_file, $template_type ) { + $default_template_types = gutenberg_get_default_template_types(); + + $theme = wp_get_theme()->get_stylesheet(); + $template = new stdClass(); + $template->id = $theme . '|' . $template_file['slug']; + $template->theme = $theme; + $template->content = file_get_contents( $template_file['path'] ); + $template->slug = $template_file['slug']; + $template->is_custom = false; + $template->type = $template_type; + $template->description = ''; + $template->title = $template_file['slug']; + + if ( 'wp_template' === $template_type && isset( $default_template_types[ $template_file['slug'] ] ) ) { + $template->description = $default_template_types[ $template_file['slug'] ]['description']; + $template->title = $default_template_types[ $template_file['slug'] ]['title']; + } + + return $template; +} + +/** + * Build a unified template object based a post Object. + * + * @param WP_Post $post Template post. + * + * @return stdClass Template. + */ +function _gutenberg_build_template_result_from_post( $post ) { + // Is this the best way to get the theme of a template post? + $theme = get_the_terms( $post, 'wp_theme' )[0]->slug; + + $template = new stdClass(); + $template->wp_id = $post->ID; + $template->id = $theme . '|' . $post->post_name; + $template->theme = $theme; + $template->content = $post->post_content; + $template->slug = $post->post_name; + $template->is_custom = true; + $template->type = $post->post_type; + $template->description = $post->post_exerpt; + $template->title = $post->post_title; + + return $template; +} + +/** + * Retrieves a list of unified template objects based on a query. + * + * @param array $query Query. + * @param array $template_type wp_template or wp_template_part. + * + * @return array Templates. + */ +function gutenberg_get_block_templates( $query = array(), $template_type = 'wp_template' ) { + $wp_query_args = array( + 'post_type' => $template_type, + 'post_status' => 'publish', + 'posts_per_page' => -1, + 'no_found_rows' => true, + ); + + if ( isset( $query['theme'] ) ) { + $wp_query_args['tax_query'] = array( + array( + 'taxonomy' => 'wp_theme', + 'field' => 'slug', + 'terms' => $query['theme'], + ), + ); + } + + if ( isset( $query['slug__in'] ) ) { + $wp_query_args['post_name__in'] = $query['slug__in']; + } + + $template_query = new WP_Query( $wp_query_args ); + $query_result = array(); + foreach ( $template_query->get_posts() as $post ) { + $query_result[] = _gutenberg_build_template_result_from_post( $post ); + } + + if ( isset( $query['theme'] ) && wp_get_theme()->get_stylesheet() === $query['theme'] ) { + $template_files = _gutenberg_get_template_files( $template_type ); + foreach ( $template_files as $template_file ) { + $is_custom = array_search( + wp_get_theme()->get_stylesheet() . '/' . $template_file['slug'], + array_column( $query_result, 'id' ), + true + ); + $should_include = false === $is_custom && ( + ! isset( $query['slug__in'] ) || in_array( $template_file['slug'], $query['slug__in'], true ) + ); + if ( $should_include ) { + $query_result[] = _gutenberg_build_template_result_from_file( $template_file, $template_type ); + } + } + } + + return $query_result; +} + +/** + * Retrieves a single unified template object using its id. + * + * @param array $id Template id. + * @param array $template_type wp_template or wp_template_part. + * + * @return stdClass|null Template. + */ +function gutenberg_get_block_template( $id, $template_type = 'wp_template' ) { + list( $theme, $slug ) = explode( '|', $id ); + $wp_query_args = array( + 'post_name' => $slug, + 'post_type' => $template_type, + 'post_status' => 'publish', + 'posts_per_page' => 1, + 'no_found_rows' => true, + array( + 'taxonomy' => 'wp_theme', + 'field' => 'slug', + 'terms' => $theme, + ), + ); + $template_query = new WP_Query( $wp_query_args ); + $posts = $template_query->get_posts(); + if ( count( $posts ) > 0 ) { + return _gutenberg_build_template_result_from_post( $posts[0] ); + } + + if ( wp_get_theme()->get_stylesheet() === $theme ) { + $template_files = _gutenberg_get_template_files( $template_type ); + foreach ( $template_files as $template_file ) { + if ( $template_file['slug'] === $slug ) { + return _gutenberg_build_template_result_from_file( $template_file, $template_type ); + } + } + } + + return null; +} diff --git a/lib/full-site-editing/class-wp-rest-template-parts-controller.php b/lib/full-site-editing/class-wp-rest-template-parts-controller.php new file mode 100644 index 00000000000000..1c151ed22cbb28 --- /dev/null +++ b/lib/full-site-editing/class-wp-rest-template-parts-controller.php @@ -0,0 +1,290 @@ +namespace = 'wp/v2'; + $this->rest_base = 'template-parts'; + } + + /** + * Registers the controllers routes. + * + * @return void + */ + public function register_routes() { + // Lists all template parts. + register_rest_route( + $this->namespace, + '/' . $this->rest_base, + array( + array( + 'methods' => WP_REST_Server::READABLE, + 'callback' => array( $this, 'get_items' ), + 'permission_callback' => array( $this, 'permissions_check' ), + 'args' => array( + 'context' => $this->get_context_param( array( 'default' => 'view' ) ), + ), + ), + 'schema' => array( $this, 'get_public_item_schema' ), + ) + ); + + // Lists/updates a single template part based on the given id. + register_rest_route( + $this->namespace, + '/' . $this->rest_base . '/(?P[|\w-]+)', + array( + array( + 'methods' => WP_REST_Server::READABLE, + 'callback' => array( $this, 'get_item' ), + 'permission_callback' => array( $this, 'permissions_check' ), + 'args' => array( + 'id' => array( + 'description' => __( 'The id of a template part', 'gutenberg' ), + 'type' => 'string', + ), + ), + ), + array( + 'methods' => WP_REST_Server::EDITABLE, + 'callback' => array( $this, 'update_item' ), + 'permission_callback' => array( $this, 'permissions_check' ), + 'args' => $this->get_endpoint_args_for_item_schema( WP_REST_Server::EDITABLE ), + ), + 'schema' => array( $this, 'get_public_item_schema' ), + ) + ); + } + + /** + * Checks if the user has permissions to make the request. + * + * @return true|WP_Error True if the request has read access, WP_Error object otherwise. + * @since 5.6.0 + * @access public + */ + public function permissions_check() { + // Verify if the current user has edit_theme_options capability. + // This capability is required to access the widgets screen. + if ( ! current_user_can( 'edit_theme_options' ) ) { + return new WP_Error( + 'template_parts_cannot_access', + __( 'Sorry, you are not allowed to access the template parts on this site.', 'gutenberg' ), + array( + 'status' => rest_authorization_required_code(), + ) + ); + } + + return true; + } + + /** + * Returns a list of template parts. + * + * @param WP_REST_Request $request The request instance. + * + * @return WP_REST_Response + */ + public function get_items( $request ) { + $query = array(); + if ( isset( $request['theme'] ) ) { + $query['theme'] = $request['theme']; + } + $template_parts = array(); + foreach ( gutenberg_get_block_templates( $query, 'wp_template_part' ) as $template_part ) { + $data = $this->prepare_item_for_response( $template_part, $request ); + $template_parts[] = $data; + } + + return rest_ensure_response( $template_parts ); + } + + /** + * Returns the given template part. + * + * @param WP_REST_Request $request The request instance. + * + * @return WP_REST_Response|WP_Error + */ + public function get_item( $request ) { + $template_part = gutenberg_get_block_template( $request['id'], 'wp_template_part' ); + + if ( ! $template_part ) { + return new WP_Error( 'rest_template_not_found', __( 'No template parts exists with that id.', 'gutenberg' ), array( 'status' => 404 ) ); + } + + $response = $this->prepare_item_for_response( $template_part, $request ); + return rest_ensure_response( $response ); + } + + /** + * Updates a single template part. + * + * @param WP_REST_Request $request Full details about the request. + * @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure. + */ + public function update_item( $request ) { + $template_part = gutenberg_get_block_template( $request['id'], 'wp_template_part' ); + if ( ! $template_part ) { + return new WP_Error( 'rest_template_part_not_found', __( 'No template parts exists with that id.', 'gutenberg' ), array( 'status' => 404 ) ); + } + $changes = $this->prepare_item_for_database( $request ); + if ( $template_part->is_custom ) { + $result = wp_update_post( $changes, true ); + } else { + $result = wp_insert_post( $changes, true ); + } + + if ( is_wp_error( $result ) ) { + return $result; + } + + $response = $this->prepare_item_for_response( gutenberg_get_block_template( $request['id'], 'wp_template_part' ), $request ); + + return rest_ensure_response( $response ); + } + + /** + * Prepares a single template part for create or update. + * + * @param WP_REST_Request $request Request object. + * @return array Changes to pass to wp_update_post. + */ + protected function prepare_item_for_database( $request ) { + $template_part = gutenberg_get_block_template( $request['id'], 'wp_template_part' ); + $changes = array( 'post_name' => $template_part->slug ); + if ( ! $template_part->is_custom ) { + $changes['post_type'] = 'wp_template_part'; + $changes['post_status'] = 'publish'; + $changes['tax_input'] = array( + 'wp_theme' => array( $template_part->theme ), + ); + } else { + $changes['ID'] = $template_part->wp_id; + } + if ( isset( $request['content'] ) ) { + $changes['post_content'] = $request['content']; + } elseif ( ! $template_part->is_custom ) { + $changes['post_content'] = $template_part->content; + } + if ( isset( $request['title'] ) ) { + $changes['post_title'] = $request['title']; + } elseif ( ! $template_part->is_custom ) { + $changes['post_title'] = $template_part->title; + } + if ( isset( $request['description'] ) ) { + $changes['post_excerpt'] = $request['description']; + } elseif ( ! $template_part->is_custom ) { + $changes['post_excerpt'] = $template_part->description; + } + + return $changes; + } + + /** + * Prepare a single template part output for response + * + * @param stdClass $template_part Temlate part instance. + * @param WP_REST_Request $request Request object. + * + * @return WP_REST_Response $data + */ + public function prepare_item_for_response( $template_part, $request ) { + $result = array( + 'id' => $template_part->id, + 'theme' => $template_part->theme, + 'content' => array( 'raw' => $template_part->content ), + 'slug' => $template_part->slug, + 'is_custom' => $template_part->is_custom, + 'type' => $template_part->type, + 'description' => $template_part->description, + 'title' => array( + 'raw' => $template_part->title, + 'rendered' => $template_part->title, + ), + ); + + return $result; + } + + /** + * Retrieves the block type' schema, conforming to JSON Schema. + * + * @return array Item schema data. + */ + public function get_item_schema() { + if ( $this->schema ) { + return $this->add_additional_fields_schema( $this->schema ); + } + + $schema = array( + '$schema' => 'http://json-schema.org/draft-04/schema#', + 'title' => 'template part', + 'type' => 'object', + 'properties' => array( + 'id' => array( + 'description' => __( 'ID of template part.', 'gutenberg' ), + 'type' => 'string', + 'context' => array( 'embed', 'view', 'edit' ), + 'readonly' => true, + ), + 'slug' => array( + 'description' => __( 'Unique slug identifying the template part.', 'gutenberg' ), + 'type' => 'string', + 'context' => array( 'embed', 'view', 'edit' ), + 'readonly' => true, + ), + 'theme' => array( + 'description' => __( 'Theme identifier for the template part.', 'gutenberg' ), + 'type' => 'string', + 'context' => array( 'embed', 'view', 'edit' ), + 'readonly' => true, + ), + 'is_custom' => array( + 'description' => __( 'Whether the template part is customized.', 'gutenberg' ), + 'type' => 'bool', + 'context' => array( 'embed', 'view', 'edit' ), + 'readonly' => true, + ), + 'cntent' => array( + 'description' => __( 'Content of template part.', 'gutenberg' ), + 'type' => 'string', + 'default' => '', + 'context' => array( 'embed', 'view', 'edit' ), + ), + 'title' => array( + 'description' => __( 'Title of template part.', 'gutenberg' ), + 'type' => 'string', + 'default' => '', + 'context' => array( 'embed', 'view', 'edit' ), + ), + 'description' => array( + 'description' => __( 'Description of template part.', 'gutenberg' ), + 'type' => 'string', + 'default' => '', + 'context' => array( 'embed', 'view', 'edit' ), + ), + ), + ); + + $this->schema = $schema; + + return $this->add_additional_fields_schema( $this->schema ); + } +} diff --git a/lib/full-site-editing/class-wp-rest-templates-controller.php b/lib/full-site-editing/class-wp-rest-templates-controller.php new file mode 100644 index 00000000000000..6079cfffd95336 --- /dev/null +++ b/lib/full-site-editing/class-wp-rest-templates-controller.php @@ -0,0 +1,291 @@ +namespace = 'wp/v2'; + $this->rest_base = 'templates'; + } + + /** + * Registers the controllers routes. + * + * @return void + */ + public function register_routes() { + // Lists all templates. + register_rest_route( + $this->namespace, + '/' . $this->rest_base, + array( + array( + 'methods' => WP_REST_Server::READABLE, + 'callback' => array( $this, 'get_items' ), + 'permission_callback' => array( $this, 'permissions_check' ), + 'args' => array( + 'context' => $this->get_context_param( array( 'default' => 'view' ) ), + ), + ), + 'schema' => array( $this, 'get_public_item_schema' ), + ) + ); + + // Lists/updates a single template based on the given id. + register_rest_route( + $this->namespace, + '/' . $this->rest_base . '/(?P[|\w-]+)', + array( + array( + 'methods' => WP_REST_Server::READABLE, + 'callback' => array( $this, 'get_item' ), + 'permission_callback' => array( $this, 'permissions_check' ), + 'args' => array( + 'id' => array( + 'description' => __( 'The id of a template', 'gutenberg' ), + 'type' => 'string', + ), + ), + ), + array( + 'methods' => WP_REST_Server::EDITABLE, + 'callback' => array( $this, 'update_item' ), + 'permission_callback' => array( $this, 'permissions_check' ), + 'args' => $this->get_endpoint_args_for_item_schema( WP_REST_Server::EDITABLE ), + ), + 'schema' => array( $this, 'get_public_item_schema' ), + ) + ); + } + + /** + * Checks if the user has permissions to make the request. + * + * @return true|WP_Error True if the request has read access, WP_Error object otherwise. + * @since 5.6.0 + * @access public + */ + public function permissions_check() { + // Verify if the current user has edit_theme_options capability. + // This capability is required to access the widgets screen. + if ( ! current_user_can( 'edit_theme_options' ) ) { + return new WP_Error( + 'templates_cannot_access', + __( 'Sorry, you are not allowed to access the templates on this site.', 'gutenberg' ), + array( + 'status' => rest_authorization_required_code(), + ) + ); + } + + return true; + } + + /** + * Returns a list of templates. + * + * @param WP_REST_Request $request The request instance. + * + * @return WP_REST_Response + */ + public function get_items( $request ) { + $query = array(); + if ( isset( $request['theme'] ) ) { + $query['theme'] = $request['theme']; + } + $templates = array(); + foreach ( gutenberg_get_block_templates( $query ) as $template ) { + $data = $this->prepare_item_for_response( $template, $request ); + $templates[] = $data; + } + + return rest_ensure_response( $templates ); + } + + /** + * Returns the given template + * + * @param WP_REST_Request $request The request instance. + * + * @return WP_REST_Response|WP_Error + */ + public function get_item( $request ) { + $template = gutenberg_get_block_template( $request['id'] ); + + if ( ! $template ) { + return new WP_Error( 'rest_template_not_found', __( 'No templates exists with that id.', 'gutenberg' ), array( 'status' => 404 ) ); + } + + $response = $this->prepare_item_for_response( $template, $request ); + return rest_ensure_response( $response ); + } + + /** + * Updates a single template. + * + * @param WP_REST_Request $request Full details about the request. + * @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure. + */ + public function update_item( $request ) { + $template = gutenberg_get_block_template( $request['id'] ); + if ( ! $template ) { + return new WP_Error( 'rest_template_not_found', __( 'No templates exists with that id.', 'gutenberg' ), array( 'status' => 404 ) ); + } + + $changes = $this->prepare_item_for_database( $request ); + if ( $template->is_custom ) { + $result = wp_update_post( $changes, true ); + } else { + $result = wp_insert_post( $changes, true ); + } + + if ( is_wp_error( $result ) ) { + return $result; + } + + $response = $this->prepare_item_for_response( gutenberg_get_block_template( $request['id'] ), $request ); + + return rest_ensure_response( $response ); + } + + /** + * Prepares a single template for create or update. + * + * @param WP_REST_Request $request Request object. + * @return array Changes to pass to wp_update_post. + */ + protected function prepare_item_for_database( $request ) { + $template = gutenberg_get_block_template( $request['id'] ); + $changes = array( 'post_name' => $template->slug ); + if ( ! $template->is_custom ) { + $changes['post_type'] = 'wp_template'; + $changes['post_status'] = 'publish'; + $changes['tax_input'] = array( + 'wp_theme' => array( $template->theme ), + ); + } else { + $changes['ID'] = $template->wp_id; + } + if ( isset( $request['content'] ) ) { + $changes['post_content'] = $request['content']; + } elseif ( ! $template->is_custom ) { + $changes['post_content'] = $template->content; + } + if ( isset( $request['title'] ) ) { + $changes['post_title'] = $request['title']; + } elseif ( ! $template->is_custom ) { + $changes['post_title'] = $template->title; + } + if ( isset( $request['description'] ) ) { + $changes['post_excerpt'] = $request['description']; + } elseif ( ! $template->is_custom ) { + $changes['post_excerpt'] = $template->description; + } + + return $changes; + } + + /** + * Prepare a single template output for response + * + * @param stdClass $template Temlate instance. + * @param WP_REST_Request $request Request object. + * + * @return WP_REST_Response $data + */ + public function prepare_item_for_response( $template, $request ) { + $result = array( + 'id' => $template->id, + 'theme' => $template->theme, + 'content' => array( 'raw' => $template->content ), + 'slug' => $template->slug, + 'is_custom' => $template->is_custom, + 'type' => $template->type, + 'description' => $template->description, + 'title' => array( + 'raw' => $template->title, + 'rendered' => $template->title, + ), + ); + + return $result; + } + + /** + * Retrieves the block type' schema, conforming to JSON Schema. + * + * @return array Item schema data. + */ + public function get_item_schema() { + if ( $this->schema ) { + return $this->add_additional_fields_schema( $this->schema ); + } + + $schema = array( + '$schema' => 'http://json-schema.org/draft-04/schema#', + 'title' => 'template', + 'type' => 'object', + 'properties' => array( + 'id' => array( + 'description' => __( 'ID of template.', 'gutenberg' ), + 'type' => 'string', + 'context' => array( 'embed', 'view', 'edit' ), + 'readonly' => true, + ), + 'slug' => array( + 'description' => __( 'Unique slug identifying the template.', 'gutenberg' ), + 'type' => 'string', + 'context' => array( 'embed', 'view', 'edit' ), + 'readonly' => true, + ), + 'theme' => array( + 'description' => __( 'Theme identifier for the template.', 'gutenberg' ), + 'type' => 'string', + 'context' => array( 'embed', 'view', 'edit' ), + 'readonly' => true, + ), + 'is_custom' => array( + 'description' => __( 'Whether the template is customized.', 'gutenberg' ), + 'type' => 'bool', + 'context' => array( 'embed', 'view', 'edit' ), + 'readonly' => true, + ), + 'cntent' => array( + 'description' => __( 'Content of template.', 'gutenberg' ), + 'type' => 'string', + 'default' => '', + 'context' => array( 'embed', 'view', 'edit' ), + ), + 'title' => array( + 'description' => __( 'Title of template.', 'gutenberg' ), + 'type' => 'string', + 'default' => '', + 'context' => array( 'embed', 'view', 'edit' ), + ), + 'description' => array( + 'description' => __( 'Description of template.', 'gutenberg' ), + 'type' => 'string', + 'default' => '', + 'context' => array( 'embed', 'view', 'edit' ), + ), + ), + ); + + $this->schema = $schema; + + return $this->add_additional_fields_schema( $this->schema ); + } +} diff --git a/lib/edit-site-export.php b/lib/full-site-editing/edit-site-export.php similarity index 62% rename from lib/edit-site-export.php rename to lib/full-site-editing/edit-site-export.php index 14c0a756c57b3a..e419479a2008a8 100644 --- a/lib/edit-site-export.php +++ b/lib/full-site-editing/edit-site-export.php @@ -10,11 +10,6 @@ * 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(); @@ -23,53 +18,21 @@ function gutenberg_edit_site_export() { $zip->addEmptyDir( 'theme/block-templates' ); $zip->addEmptyDir( 'theme/block-template-parts' ); - $theme = wp_get_theme()->get_stylesheet(); - // Load templates into the zip file. - $template_query = new WP_Query( - array( - 'post_type' => 'wp_template', - 'post_status' => array( 'publish', 'auto-draft' ), - 'tax_query' => array( - array( - 'taxonomy' => 'wp_theme', - 'field' => 'slug', - 'terms' => $theme, - ), - ), - 'posts_per_page' => -1, - 'no_found_rows' => true, - ) - ); - while ( $template_query->have_posts() ) { - $template = $template_query->next_post(); + $templates = gutenberg_get_block_templates( array( 'theme' => wp_get_theme()->get_stylesheet() ) ); + foreach ( $templates as $template ) { $zip->addFromString( - 'theme/block-templates/' . $template->post_name . '.html', - gutenberg_strip_post_ids_from_template_part_blocks( $template->post_content ) + 'theme/block-templates/' . $template->slug . '.html', + gutenberg_strip_post_ids_from_template_part_blocks( $template->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' ), - 'tax_query' => array( - array( - 'taxonomy' => 'wp_theme', - 'field' => 'slug', - 'terms' => $theme, - ), - ), - 'posts_per_page' => -1, - 'no_found_rows' => true, - ) - ); - while ( $template_part_query->have_posts() ) { - $template_part = $template_part_query->next_post(); + $template_parts = gutenberg_get_block_templates( array( 'theme' => wp_get_theme()->get_stylesheet() ), 'wp_template_part' ); + foreach ( $template_parts as $template_part ) { $zip->addFromString( - 'theme/block-template-parts/' . $template_part->post_name . '.html', - gutenberg_strip_post_ids_from_template_part_blocks( $template_part->post_content ) + 'theme/block-template-parts/' . $template_part->slug . '.html', + gutenberg_strip_post_ids_from_template_part_blocks( $template_part->content ) ); } diff --git a/lib/edit-site-page.php b/lib/full-site-editing/edit-site-page.php similarity index 100% rename from lib/edit-site-page.php rename to lib/full-site-editing/edit-site-page.php diff --git a/lib/full-site-editing.php b/lib/full-site-editing/full-site-editing.php similarity index 100% rename from lib/full-site-editing.php rename to lib/full-site-editing/full-site-editing.php diff --git a/lib/template-loader.php b/lib/full-site-editing/template-loader.php similarity index 91% rename from lib/template-loader.php rename to lib/full-site-editing/template-loader.php index 5dc55ec88cb8f1..1ffdec27a3040a 100644 --- a/lib/template-loader.php +++ b/lib/full-site-editing/template-loader.php @@ -66,7 +66,7 @@ function gutenberg_override_query_template( $template, $type, array $templates = $current_template = gutenberg_resolve_template( $type, $templates ); if ( $current_template ) { - $_wp_current_template_content = empty( $current_template->post_content ) ? __( 'Empty template.', 'gutenberg' ) : $current_template->post_content; + $_wp_current_template_content = empty( $current_template->content ) ? __( 'Empty template.', 'gutenberg' ) : $current_template->content; if ( isset( $_GET['_wp-find-template'] ) ) { wp_send_json_success( $current_template ); @@ -124,24 +124,11 @@ function gutenberg_resolve_template( $template_type, $template_hierarchy = array ); // Find all potential templates 'wp_template' post matching the hierarchy. - $template_query = new WP_Query( - array( - 'post_type' => 'wp_template', - 'post_status' => array( 'publish', 'auto-draft' ), - 'post_name__in' => $slugs, - 'orderby' => 'post_name__in', - 'posts_per_page' => -1, - 'no_found_rows' => true, - 'tax_query' => array( - array( - 'taxonomy' => 'wp_theme', - 'field' => 'slug', - 'terms' => wp_get_theme()->get_stylesheet(), - ), - ), - ) + $query = array( + 'theme' => wp_get_theme()->get_stylesheet(), + 'slug__in' => $slugs, ); - $templates = $template_query->get_posts(); + $templates = gutenberg_get_block_templates( $query ); // Order these templates per slug priority. // Build map of template slugs to their priority in the current hierarchy. @@ -150,7 +137,7 @@ function gutenberg_resolve_template( $template_type, $template_hierarchy = array usort( $templates, function ( $template_a, $template_b ) use ( $slug_priorities ) { - return $slug_priorities[ $template_a->post_name ] - $slug_priorities[ $template_b->post_name ]; + return $slug_priorities[ $template_a->slug ] - $slug_priorities[ $template_b->slug ]; } ); diff --git a/lib/template-parts.php b/lib/full-site-editing/template-parts.php similarity index 69% rename from lib/template-parts.php rename to lib/full-site-editing/template-parts.php index 464200f66d000a..da02fdc8db6973 100644 --- a/lib/template-parts.php +++ b/lib/full-site-editing/template-parts.php @@ -36,17 +36,18 @@ function gutenberg_register_template_part_post_type() { ); $args = array( - 'labels' => $labels, - 'description' => __( 'Template parts to include in your templates.', 'gutenberg' ), - 'public' => false, - 'has_archive' => false, - 'show_ui' => true, - 'show_in_menu' => 'themes.php', - 'show_in_admin_bar' => false, - 'show_in_rest' => true, - 'rest_base' => 'template-parts', - 'map_meta_cap' => true, - 'supports' => array( + 'labels' => $labels, + 'description' => __( 'Template parts to include in your templates.', 'gutenberg' ), + 'public' => false, + 'has_archive' => false, + 'show_ui' => true, + 'show_in_menu' => 'themes.php', + 'show_in_admin_bar' => false, + 'show_in_rest' => true, + 'rest_base' => 'template-parts', + 'rest_controller_class' => 'WP_REST_Template_Parts_Controller', + 'map_meta_cap' => true, + 'supports' => array( 'title', 'slug', 'excerpt', @@ -108,43 +109,3 @@ function gutenberg_fix_template_part_admin_menu_entry() { add_filter( 'manage_wp_template_part_posts_columns', 'gutenberg_templates_lists_custom_columns' ); add_action( 'manage_wp_template_part_posts_custom_column', 'gutenberg_render_templates_lists_custom_column', 10, 2 ); add_filter( 'views_edit-wp_template_part', 'gutenberg_filter_templates_edit_views' ); - -/** - * Filter for adding and a `theme` parameter to `wp_template_part` queries. - * - * @param array $query_params The query parameters. - * @return array Filtered $query_params. - */ -function filter_rest_wp_template_part_collection_params( $query_params ) { - $query_params += array( - 'theme' => array( - 'description' => __( 'The theme slug for the theme that created the template part.', 'gutenberg' ), - 'type' => 'string', - ), - ); - return $query_params; -} -add_filter( 'rest_wp_template_part_collection_params', 'filter_rest_wp_template_part_collection_params', 99, 1 ); - -/** - * Filter for supporting the `resolved`, `template`, and `theme` parameters in `wp_template_part` queries. - * - * @param array $args The query arguments. - * @param WP_REST_Request $request The request object. - * @return array Filtered $args. - */ -function filter_rest_wp_template_part_query( $args, $request ) { - if ( $request['theme'] ) { - $tax_query = isset( $args['tax_query'] ) ? $args['tax_query'] : array(); - $tax_query[] = array( - 'taxonomy' => 'wp_theme', - 'field' => 'slug', - 'terms' => $request['theme'], - ); - - $args['tax_query'] = $tax_query; - } - - return $args; -} -add_filter( 'rest_wp_template_part_query', 'filter_rest_wp_template_part_query', 99, 2 ); diff --git a/lib/full-site-editing/templates-utils.php b/lib/full-site-editing/templates-utils.php index 5430470cfa640a..ce622fd113152c 100644 --- a/lib/full-site-editing/templates-utils.php +++ b/lib/full-site-editing/templates-utils.php @@ -60,11 +60,7 @@ function gutenberg_render_templates_lists_custom_column( $column_name, $post_id $themes = array(); $is_file_based = false; foreach ( $terms as $term ) { - if ( '_wp_file_based' === $term->slug ) { - $is_file_based = true; - } else { - $themes[] = esc_html( wp_get_theme( $term->slug ) ); - } + $themes[] = esc_html( wp_get_theme( $term->slug ) ); } echo implode( '
', $themes ); if ( $is_file_based ) { diff --git a/lib/templates.php b/lib/full-site-editing/templates.php similarity index 62% rename from lib/templates.php rename to lib/full-site-editing/templates.php index aa085c0d54d125..1fc5725c859db6 100644 --- a/lib/templates.php +++ b/lib/full-site-editing/templates.php @@ -55,18 +55,19 @@ function gutenberg_register_template_post_type() { ); $args = array( - 'labels' => $labels, - 'description' => __( 'Templates to include in your theme.', 'gutenberg' ), - 'public' => false, - 'has_archive' => false, - 'show_ui' => true, - 'show_in_menu' => 'themes.php', - 'show_in_admin_bar' => false, - 'show_in_rest' => true, - 'rest_base' => 'templates', - 'capability_type' => array( 'template', 'templates' ), - 'map_meta_cap' => true, - 'supports' => array( + 'labels' => $labels, + 'description' => __( 'Templates to include in your theme.', 'gutenberg' ), + 'public' => false, + 'has_archive' => false, + 'show_ui' => true, + 'show_in_menu' => 'themes.php', + 'show_in_admin_bar' => false, + 'show_in_rest' => true, + 'rest_base' => 'templates', + 'rest_controller_class' => 'WP_REST_Templates_Controller', + 'capability_type' => array( 'template', 'templates' ), + 'map_meta_cap' => true, + 'supports' => array( 'title', 'slug', 'excerpt', @@ -102,26 +103,12 @@ function gutenberg_register_wp_theme_taxonomy() { 'show_ui' => false, '_builtin' => true, 'show_in_nav_menus' => false, - 'show_in_rest' => true, + 'show_in_rest' => false, ) ); } add_action( 'init', 'gutenberg_register_wp_theme_taxonomy' ); -/** - * Automatically set the theme meta for templates. - * - * @param array $post_id Template ID. - */ -function gutenberg_set_template_and_template_part_post_theme( $post_id ) { - $themes = wp_get_post_terms( $post_id, 'wp_theme' ); - if ( ! $themes ) { - wp_set_post_terms( $post_id, array( wp_get_theme()->get_stylesheet() ), 'wp_theme', true ); - } -} -add_action( 'save_post_wp_template', 'gutenberg_set_template_and_template_part_post_theme', 10, 3 ); -add_action( 'save_post_wp_template_part', 'gutenberg_set_template_and_template_part_post_theme', 10, 3 ); - /** * Filters the capabilities of a user to conditionally grant them capabilities for managing 'wp_template' posts. * @@ -196,90 +183,3 @@ function gutenberg_fix_template_admin_menu_entry() { add_filter( 'manage_wp_template_posts_columns', 'gutenberg_templates_lists_custom_columns' ); add_action( 'manage_wp_template_posts_custom_column', 'gutenberg_render_templates_lists_custom_column', 10, 2 ); add_filter( 'views_edit-wp_template', 'gutenberg_filter_templates_edit_views' ); - -/** - * Filter for adding a `resolved` parameter to `wp_template` queries. - * - * @param array $query_params The query parameters. - * @return array Filtered $query_params. - */ -function filter_rest_wp_template_collection_params( $query_params ) { - $query_params += array( - 'resolved' => array( - 'description' => __( 'Whether to filter for resolved templates', 'gutenberg' ), - 'type' => 'boolean', - ), - ); - return $query_params; -} -apply_filters( 'rest_wp_template_collection_params', 'filter_rest_wp_template_collection_params', 99, 1 ); - -/** - * Filter for supporting the `resolved` parameter in `wp_template` queries. - * - * @param array $args The query arguments. - * @param WP_REST_Request $request The request object. - * @return array Filtered $args. - */ -function filter_rest_wp_template_query( $args, $request ) { - if ( $request['resolved'] ) { - $template_ids = array( 0 ); // Return nothing by default (the 0 is needed for `post__in`). - $template_types = $request['slug'] ? $request['slug'] : gutenberg_get_template_type_slugs(); - - foreach ( $template_types as $template_type ) { - // Skip 'embed' for now because it is not a regular template type. - if ( in_array( $template_type, array( 'embed' ), true ) ) { - continue; - } - - $current_template = gutenberg_resolve_template( $template_type ); - if ( $current_template ) { - $template_ids[] = $current_template->ID; - } - } - $args['post__in'] = $template_ids; - $args['post_status'] = array( 'publish', 'auto-draft' ); - } - - return $args; -} -add_filter( 'rest_wp_template_query', 'filter_rest_wp_template_query', 99, 2 ); - -/** - * Filters the post data for a response. - * - * @param WP_REST_Response $response The response object. - * @return WP_REST_Response - */ -function filter_rest_prepare_add_wp_theme_slug_and_file_based( $response ) { - if ( isset( $response->data ) && is_array( $response->data ) && isset( $response->data['id'] ) ) { - $response->data['wp_theme_slug'] = false; - - // Get the wp_theme terms. - $wp_themes = wp_get_post_terms( $response->data['id'], 'wp_theme' ); - - // If a theme is assigned, add it to the REST response. - if ( $wp_themes && is_array( $wp_themes ) ) { - $wp_theme_slugs = array_column( $wp_themes, 'slug' ); - - $file_based = in_array( '_wp_file_based', $wp_theme_slugs, true ); - $response->data['file_based'] = $file_based; - - $theme_slug = array_values( - array_filter( - $wp_theme_slugs, - function( $slug ) { - return '_wp_file_based' !== $slug; - } - ) - ); - if ( $theme_slug ) { - $response->data['wp_theme_slug'] = $theme_slug[0]; - } - } - } - - return $response; -} -add_filter( 'rest_prepare_wp_template', 'filter_rest_prepare_add_wp_theme_slug_and_file_based' ); -add_filter( 'rest_prepare_wp_template_part', 'filter_rest_prepare_add_wp_theme_slug_and_file_based' ); diff --git a/lib/load.php b/lib/load.php index 02b54459609dd6..44608eb050933b 100644 --- a/lib/load.php +++ b/lib/load.php @@ -59,6 +59,12 @@ function gutenberg_is_experiment_enabled( $name ) { if ( ! class_exists( 'WP_REST_Batch_Controller' ) ) { require_once __DIR__ . '/class-wp-rest-batch-controller.php'; } + if ( ! class_exists( 'WP_REST_Templates_Controller' ) ) { + require_once __DIR__ . '/full-site-editing/class-wp-rest-templates-controller.php'; + } + if ( ! class_exists( 'WP_REST_Template_Parts_Controller' ) ) { + require_once __DIR__ . '/full-site-editing/class-wp-rest-template-parts-controller.php'; + } /** * End: Include for phase 2 */ @@ -76,16 +82,16 @@ function gutenberg_is_experiment_enabled( $name ) { require __DIR__ . '/utils.php'; require __DIR__ . '/editor-settings.php'; -require __DIR__ . '/full-site-editing.php'; +require __DIR__ . '/full-site-editing/full-site-editing.php'; +require __DIR__ . '/full-site-editing/block-templates.php'; require __DIR__ . '/full-site-editing/default-template-types.php'; require __DIR__ . '/full-site-editing/templates-utils.php'; require __DIR__ . '/full-site-editing/page-templates.php'; -require __DIR__ . '/templates-sync.php'; -require __DIR__ . '/templates.php'; -require __DIR__ . '/template-parts.php'; -require __DIR__ . '/template-loader.php'; -require __DIR__ . '/edit-site-page.php'; -require __DIR__ . '/edit-site-export.php'; +require __DIR__ . '/full-site-editing/templates.php'; +require __DIR__ . '/full-site-editing/template-parts.php'; +require __DIR__ . '/full-site-editing/template-loader.php'; +require __DIR__ . '/full-site-editing/edit-site-page.php'; +require __DIR__ . '/full-site-editing/edit-site-export.php'; require __DIR__ . '/blocks.php'; require __DIR__ . '/client-assets.php'; diff --git a/lib/templates-sync.php b/lib/templates-sync.php deleted file mode 100644 index b5c1076f49ce8c..00000000000000 --- a/lib/templates-sync.php +++ /dev/null @@ -1,188 +0,0 @@ - $post_type, - 'post_status' => array( 'publish', 'auto-draft' ), - 'post_name__in' => array( $slug ), - 'tax_query' => array( - array( - 'taxonomy' => 'wp_theme', - 'field' => 'slug', - 'terms' => $theme, - ), - ), - 'posts_per_page' => 1, - 'no_found_rows' => true, - ) - ); - $post = $template_query->have_posts() ? $template_query->next_post() : null; - if ( ! $post ) { - $template_post = array( - 'post_content' => $content, - 'post_title' => $slug, - 'post_status' => 'auto-draft', - 'post_type' => $post_type, - 'post_name' => $slug, - 'tax_input' => array( 'wp_theme' => array( $theme, '_wp_file_based' ) ), - ); - - if ( 'wp_template' === $post_type ) { - $default_template_types = gutenberg_get_default_template_types(); - if ( isset( $default_template_types[ $slug ] ) ) { - $template_post['post_title'] = $default_template_types[ $slug ]['title']; - $template_post['post_excerpt'] = $default_template_types[ $slug ]['description']; - } - } - - wp_insert_post( $template_post ); - } 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 ); - } -} - -/** - * Finds all nested template part file paths in a theme's directory. - * - * @access private - * - * @param string $base_directory The theme's file path. - * @return array $path_list A list of paths to all template part files. - */ -function _gutenberg_get_template_paths( $base_directory ) { - $path_list = array(); - if ( file_exists( $base_directory ) ) { - $nested_files = new RecursiveIteratorIterator( new RecursiveDirectoryIterator( $base_directory ) ); - $nested_html_files = new RegexIterator( $nested_files, '/^.+\.html$/i', RecursiveRegexIterator::GET_MATCH ); - foreach ( $nested_html_files as $path => $file ) { - $path_list[] = $path; - } - } - return $path_list; -} - -/** - * Create the template parts auto-drafts for the current theme. - * - * @access private - * @internal - * - * @param string $template_type The template type (template or template-part). - */ -function _gutenberg_synchronize_theme_templates( $template_type ) { - $template_post_types = array( - 'template' => 'wp_template', - 'template-part' => 'wp_template_part', - ); - $template_base_paths = array( - '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, - ); - } - } - - $last_checks[ $theme_slug ] = $current_time; - } - - // Build and save each template part. - foreach ( $template_files as $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( - $path, - // Starting position of slug. - strpos( $path, $template_base_path . DIRECTORY_SEPARATOR ) + 1 + strlen( $template_base_path ), - // Subtract ending '.html'. - -5 - ); - _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( $post_id, $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/packages/block-library/src/template-part/block.json b/packages/block-library/src/template-part/block.json index 3d9e5dfa0476e9..9b11cc651917d8 100644 --- a/packages/block-library/src/template-part/block.json +++ b/packages/block-library/src/template-part/block.json @@ -3,9 +3,6 @@ "name": "core/template-part", "category": "design", "attributes": { - "postId": { - "type": "number" - }, "slug": { "type": "string" }, diff --git a/packages/block-library/src/template-part/edit/index.js b/packages/block-library/src/template-part/edit/index.js index e96f85d7ecadbc..cb81e1617dd58c 100644 --- a/packages/block-library/src/template-part/edit/index.js +++ b/packages/block-library/src/template-part/edit/index.js @@ -1,8 +1,7 @@ /** * WordPress dependencies */ -import { useRef, useEffect } from '@wordpress/element'; -import { useSelect, useDispatch } from '@wordpress/data'; +import { useSelect } from '@wordpress/data'; import { BlockControls, InspectorAdvancedControls, @@ -17,89 +16,45 @@ import { } from '@wordpress/components'; import { __ } from '@wordpress/i18n'; import { chevronUp, chevronDown } from '@wordpress/icons'; -import { serialize } from '@wordpress/blocks'; + /** * Internal dependencies */ -import useTemplatePartPost from './use-template-part-post'; import TemplatePartNamePanel from './name-panel'; import TemplatePartInnerBlocks from './inner-blocks'; import TemplatePartPlaceholder from './placeholder'; import TemplatePartSelection from './selection'; export default function TemplatePartEdit( { - attributes: { postId: _postId, slug, theme, tagName: TagName = 'div' }, + attributes: { slug, theme, tagName: TagName = 'div' }, setAttributes, clientId, } ) { - const initialSlug = useRef( slug ); - const initialTheme = useRef( theme ); - const initialContent = useRef(); - - // Resolve the post ID if not set, and load its post. - const postId = useTemplatePartPost( _postId, slug, theme ); + const templatePartId = theme + '|' + slug; // Set the postId block attribute if it did not exist, // but wait until the inner blocks have loaded to allow // new edits to trigger this. - const { innerBlocks, expectedContent } = useSelect( + const { isResolved, innerBlocks } = useSelect( ( select ) => { const { getBlocks } = select( 'core/block-editor' ); const entityRecord = select( 'core' ).getEntityRecord( 'postType', 'wp_template_part', - postId + theme + '|' + slug ); return { innerBlocks: getBlocks( clientId ), - expectedContent: entityRecord?.content.raw, + isResolved: !! entityRecord, }; }, - [ clientId, postId ] + [ templatePartId, clientId ] ); - const { editEntityRecord } = useDispatch( 'core' ); - useEffect( () => { - // If postId (entity) has not resolved or _postId (block attr) is set, - // then we have no need for this effect. - if ( ! postId || _postId ) { - return; - } - - const innerContent = serialize( innerBlocks ); - - // If we havent set initialContent, check if innerBlocks are loaded. - if ( ! initialContent.current ) { - // If the content of innerBlocks and the content from entity match, - // then we can consider innerBlocks as loaded and set initialContent. - if ( innerContent === expectedContent ) { - initialContent.current = innerContent; - } - // Continue to return early until this effect is triggered - // with innerBlocks already loaded (as denoted by initialContent being set). - return; - } - - // After initialContent is set and the content is updated, we can set the - // postId block attribute and set the post status to 'publish'. - // After this is done the hook will no longer run due to the first return above. - if ( initialContent.current !== innerContent ) { - setAttributes( { postId } ); - editEntityRecord( 'postType', 'wp_template_part', postId, { - status: 'publish', - } ); - } - }, [ innerBlocks, expectedContent ] ); const blockProps = useBlockProps(); - - // Part of a template file, post ID already resolved. - const isTemplateFile = !! postId; - // Fresh new block. - const isPlaceholder = - ! postId && ! initialSlug.current && ! initialTheme.current; - // Part of a template file, post ID not resolved yet. - const isUnresolvedTemplateFile = ! isPlaceholder && ! postId; + const isPlaceholder = ! slug; + const isUnresolvedTemplateFile = ! isPlaceholder && ! isResolved; const inspectorAdvancedControls = ( @@ -130,13 +85,10 @@ export default function TemplatePartEdit( { innerBlocks={ innerBlocks } /> ) } - { isTemplateFile && ( + { ! isPlaceholder && ! isUnresolvedTemplateFile && ( - + ) } - { isTemplateFile && ( + { ! isPlaceholder && ! isUnresolvedTemplateFile && ( 0 } /> ) } - { isUnresolvedTemplateFile && } + { ! isPlaceholder && isUnresolvedTemplateFile && } ); diff --git a/packages/block-library/src/template-part/edit/name-panel.js b/packages/block-library/src/template-part/edit/name-panel.js index 7f8ad5dec4a98e..e2f5ac90b322e0 100644 --- a/packages/block-library/src/template-part/edit/name-panel.js +++ b/packages/block-library/src/template-part/edit/name-panel.js @@ -4,41 +4,22 @@ import { useEntityProp } from '@wordpress/core-data'; import { TextControl } from '@wordpress/components'; import { __ } from '@wordpress/i18n'; -import { cleanForSlug } from '@wordpress/url'; -export default function TemplatePartNamePanel( { postId, setAttributes } ) { +export default function TemplatePartNamePanel( { postId } ) { const [ title, setTitle ] = useEntityProp( 'postType', 'wp_template_part', 'title', postId ); - const [ slug, setSlug ] = useEntityProp( - 'postType', - 'wp_template_part', - 'slug', - postId - ); - const [ status, setStatus ] = useEntityProp( - 'postType', - 'wp_template_part', - 'status', - postId - ); return (
{ setTitle( value ); - const newSlug = cleanForSlug( value ); - setSlug( newSlug ); - if ( status !== 'publish' ) { - setStatus( 'publish' ); - } - setAttributes( { slug: newSlug, postId } ); } } onFocus={ ( event ) => event.target.select() } /> diff --git a/packages/block-library/src/template-part/edit/placeholder/index.js b/packages/block-library/src/template-part/edit/placeholder/index.js index f1181149c86521..9c6bc6e8c26a87 100644 --- a/packages/block-library/src/template-part/edit/placeholder/index.js +++ b/packages/block-library/src/template-part/edit/placeholder/index.js @@ -33,7 +33,6 @@ export default function TemplatePartPlaceholder( { } ); setAttributes( { - postId: templatePart.id, slug: templatePart.slug, theme: templatePart.wp_theme_slug, } ); diff --git a/packages/block-library/src/template-part/edit/selection/template-part-previews.js b/packages/block-library/src/template-part/edit/selection/template-part-previews.js index b47dfaa92970e5..d42419c87dbb2b 100644 --- a/packages/block-library/src/template-part/edit/selection/template-part-previews.js +++ b/packages/block-library/src/template-part/edit/selection/template-part-previews.js @@ -32,7 +32,7 @@ function TemplatePartItem( { onClose, composite, } ) { - const { id, slug, wp_theme_slug: theme } = templatePart; + const { slug, wp_theme_slug: theme } = templatePart; // The 'raw' property is not defined for a brief period in the save cycle. // The fallback prevents an error in the parse function while saving. const content = templatePart.content.raw || ''; @@ -40,7 +40,7 @@ function TemplatePartItem( { const { createSuccessNotice } = useDispatch( noticesStore ); const onClick = useCallback( () => { - setAttributes( { postId: id, slug, theme } ); + setAttributes( { slug, theme } ); createSuccessNotice( sprintf( /* translators: %s: template part title. */ @@ -52,7 +52,7 @@ function TemplatePartItem( { } ); onClose(); - }, [ id, slug, theme ] ); + }, [ slug, theme ] ); return ( { const publishedTemplateParts = - select( 'core' ).getEntityRecords( 'postType', 'wp_template_part', { - status: [ 'publish' ], - per_page: -1, - } ) || []; + select( 'core' ).getEntityRecords( + 'postType', + 'wp_template_part' + ) || []; const currentTheme = select( 'core' ).getCurrentTheme()?.stylesheet; const themeTemplateParts = select( 'core' ).getEntityRecords( 'postType', 'wp_template_part', { theme: currentTheme, - status: [ 'auto-draft' ], - per_page: -1, } ) || []; return [ ...themeTemplateParts, ...publishedTemplateParts ]; }, [] ); diff --git a/packages/block-library/src/template-part/edit/use-template-part-post.js b/packages/block-library/src/template-part/edit/use-template-part-post.js deleted file mode 100644 index fed17e7a4cd77a..00000000000000 --- a/packages/block-library/src/template-part/edit/use-template-part-post.js +++ /dev/null @@ -1,47 +0,0 @@ -/** - * WordPress dependencies - */ -import { useSelect } from '@wordpress/data'; -import { cleanForSlug } from '@wordpress/url'; - -export default function useTemplatePartPost( postId, slug, theme ) { - return useSelect( - ( select ) => { - if ( postId ) { - // This is already a custom template part, - // use its CPT post. - return ( - select( 'core' ).getEntityRecord( - 'postType', - 'wp_template_part', - postId - ) && postId - ); - } - - // This is not a custom template part, - // load the auto-draft created from the - // relevant file. - if ( slug && theme ) { - const cleanedSlug = cleanForSlug( slug ); - const posts = select( 'core' ).getEntityRecords( - 'postType', - 'wp_template_part', - { - status: [ 'publish', 'auto-draft' ], - slug: cleanedSlug, - theme, - } - ); - - // A published post might already exist if this template part was customized elsewhere - // or if it's part of a customized template. - const foundPost = - posts?.find( ( post ) => post.status === 'publish' ) || - posts?.find( ( post ) => post.status === 'auto-draft' ); - return foundPost?.id; - } - }, - [ postId, slug, theme ] - ); -} diff --git a/packages/core-data/src/entities.js b/packages/core-data/src/entities.js index c3c81b8dbfee03..abd1195b8aec44 100644 --- a/packages/core-data/src/entities.js +++ b/packages/core-data/src/entities.js @@ -114,49 +114,16 @@ export const kinds = [ { name: 'taxonomy', loadEntities: loadTaxonomyEntities }, ]; -/** - * Returns a function to be used to retrieve the title of a given post type record. - * - * @param {string} postTypeName PostType name. - * @return {Function} getTitle. - */ -export const getPostTypeTitle = ( postTypeName ) => ( record ) => { - if ( [ 'wp_template_part', 'wp_template' ].includes( postTypeName ) ) { - return ( - record?.title?.rendered || record?.title || startCase( record.slug ) - ); - } - return record?.title?.rendered || record?.title || String( record.id ); -}; - /** * Returns a function to be used to retrieve extra edits to apply before persisting a post type. * - * @param {string} postTypeName PostType name. - * @return {Function} prePersistHandler. + * @param {Object} persistedRecord Already persisted Post + * @param {Object} edits Edits. + * @return {Object} Updated edits. */ -export const getPostTypePrePersistHandler = ( postTypeName ) => ( - persistedRecord, - edits -) => { +export const prePersistPostType = ( persistedRecord, edits ) => { const newEdits = {}; - // Fix template titles. - if ( - [ 'wp_template', 'wp_template_part' ].includes( postTypeName ) && - ! edits.title && - ! persistedRecord.title - ) { - newEdits.title = persistedRecord - ? getPostTypeTitle( postTypeName )( persistedRecord ) - : edits.slug; - } - - // Templates and template parts can only be published. - if ( [ 'wp_template', 'wp_template_part' ].includes( postTypeName ) ) { - newEdits.status = 'publish'; - } - if ( persistedRecord?.status === 'auto-draft' ) { // Saving an auto-draft should create a draft by default. if ( ! edits.status && ! newEdits.status ) { @@ -185,6 +152,9 @@ export const getPostTypePrePersistHandler = ( postTypeName ) => ( function* loadPostTypeEntities() { const postTypes = yield apiFetch( { path: '/wp/v2/types?context=edit' } ); return map( postTypes, ( postType, name ) => { + const isTemplate = [ 'wp_template', 'wp_template_part' ].includes( + name + ); return { kind: 'postType', baseURL: '/wp/v2/' + postType.rest_base, @@ -196,8 +166,16 @@ function* loadPostTypeEntities() { selectionEnd: true, }, mergedEdits: { meta: true }, - getTitle: getPostTypeTitle( name ), - __unstablePrePersist: getPostTypePrePersistHandler( name ), + getTitle: isTemplate + ? ( record ) => + record?.title?.rendered || + record?.title || + startCase( record.slug ) + : ( record ) => + record?.title?.rendered || + record?.title || + String( record.id ), + __unstablePrePersist: isTemplate ? undefined : prePersistPostType, }; } ); } diff --git a/packages/core-data/src/resolvers.js b/packages/core-data/src/resolvers.js index 3458c260d3f603..ce09f4439d8c7e 100644 --- a/packages/core-data/src/resolvers.js +++ b/packages/core-data/src/resolvers.js @@ -406,13 +406,13 @@ export function* __experimentalGetTemplateForLink( link ) { return; } - yield getEntityRecord( 'postType', 'wp_template', template.ID ); + yield getEntityRecord( 'postType', 'wp_template', template.id ); const record = yield controls.select( 'core', 'getEntityRecord', 'postType', 'wp_template', - template.ID + template.id ); if ( record ) { From a33cdb82ecd6ecd2794fc217fe1b25420d5af341 Mon Sep 17 00:00:00 2001 From: Riad Benguella Date: Mon, 28 Dec 2020 15:00:30 +0100 Subject: [PATCH 02/49] Fix site editor --- lib/full-site-editing/block-templates.php | 3 +- .../src/components/navigate-to-link/index.js | 37 ++++---------- packages/edit-site/src/index.js | 2 - packages/edit-site/src/store/actions.js | 12 ++--- packages/edit-site/src/store/controls.js | 34 ------------- packages/edit-site/src/store/index.js | 5 +- packages/edit-site/src/store/test/controls.js | 44 ----------------- packages/edit-site/src/utils/find-template.js | 39 --------------- packages/edit-site/src/utils/index.js | 1 - .../edit-site/src/utils/test/find-template.js | 49 ------------------- 10 files changed, 20 insertions(+), 206 deletions(-) delete mode 100644 packages/edit-site/src/store/controls.js delete mode 100644 packages/edit-site/src/store/test/controls.js delete mode 100644 packages/edit-site/src/utils/find-template.js delete mode 100644 packages/edit-site/src/utils/index.js delete mode 100644 packages/edit-site/src/utils/test/find-template.js diff --git a/lib/full-site-editing/block-templates.php b/lib/full-site-editing/block-templates.php index 0768aa8de84dd4..3ab2ff50ad852a 100644 --- a/lib/full-site-editing/block-templates.php +++ b/lib/full-site-editing/block-templates.php @@ -192,7 +192,7 @@ function gutenberg_get_block_templates( $query = array(), $template_type = 'wp_t function gutenberg_get_block_template( $id, $template_type = 'wp_template' ) { list( $theme, $slug ) = explode( '|', $id ); $wp_query_args = array( - 'post_name' => $slug, + 'name' => $slug, 'post_type' => $template_type, 'post_status' => 'publish', 'posts_per_page' => 1, @@ -205,6 +205,7 @@ function gutenberg_get_block_template( $id, $template_type = 'wp_template' ) { ); $template_query = new WP_Query( $wp_query_args ); $posts = $template_query->get_posts(); + if ( count( $posts ) > 0 ) { return _gutenberg_build_template_result_from_post( $posts[0] ); } diff --git a/packages/edit-site/src/components/navigate-to-link/index.js b/packages/edit-site/src/components/navigate-to-link/index.js index 379f4078fce96d..d6af6a65833cdf 100644 --- a/packages/edit-site/src/components/navigate-to-link/index.js +++ b/packages/edit-site/src/components/navigate-to-link/index.js @@ -2,24 +2,19 @@ * WordPress dependencies */ import { getPathAndQueryString } from '@wordpress/url'; -import { useState, useEffect, useMemo } from '@wordpress/element'; -import { useSelect, useRegistry } from '@wordpress/data'; +import { useMemo } from '@wordpress/element'; +import { useSelect } from '@wordpress/data'; import { Button } from '@wordpress/components'; import { edit } from '@wordpress/icons'; import { __ } from '@wordpress/i18n'; -/** - * Internal dependencies - */ -import { findTemplate } from '../../utils'; - export default function NavigateToLink( { type, id, activePage, onActivePageChange, } ) { - const pageEntity = useSelect( + const post = useSelect( ( select ) => type && id && @@ -28,34 +23,22 @@ export default function NavigateToLink( { [ type, id ] ); - const registry = useRegistry(); - const [ templateId, setTemplateId ] = useState(); - useEffect( () => { - if ( pageEntity ) - findTemplate( - pageEntity.link, - registry.__experimentalResolveSelect( 'core' ).getEntityRecords - ).then( - ( newTemplateId ) => setTemplateId( newTemplateId ), - () => setTemplateId( null ) - ); - }, [ pageEntity?.link, registry ] ); - const onClick = useMemo( () => { - if ( ! pageEntity || ! templateId ) return null; - const path = getPathAndQueryString( pageEntity.link ); + if ( ! post?.link ) return null; + const path = getPathAndQueryString( post.link ); if ( path === activePage.path ) return null; return () => onActivePageChange( { type, - slug: pageEntity.slug, + slug: post.slug, path, context: { - postType: pageEntity.type, - postId: pageEntity.id, + postType: post.type, + postId: post.id, }, } ); - }, [ pageEntity, templateId, activePage.path, onActivePageChange ] ); + }, [ post, activePage.path, onActivePageChange ] ); + return ( onClick && (