Changeset 2688373
- Timestamp:
- 03/03/2022 05:42:02 PM (2 years ago)
- Location:
- full-site-editing/trunk
- Files:
-
- 30 deleted
- 5 edited
Legend:
- Unmodified
- Added
- Removed
-
full-site-editing/trunk/dotcom-fse/class-full-site-editing.php
r2671684 r2688373 20 20 21 21 /** 22 * Custom post types.23 *24 * @var array25 */26 private $template_post_types = array( 'wp_template_part' );27 28 /**29 22 * Current theme slug. 30 23 * … … 46 39 add_action( 'init', array( $this, 'register_blocks' ), 100 ); 47 40 add_action( 'init', array( $this, 'register_template_post_types' ) ); 48 add_action( 'enqueue_block_editor_assets', array( $this, 'enqueue_script_and_style' ), 100 );49 add_action( 'the_post', array( $this, 'merge_template_and_post' ) );50 add_filter( 'wp_insert_post_data', array( $this, 'remove_template_components' ), 10, 2 );51 add_filter( 'admin_body_class', array( $this, 'toggle_editor_post_title_visibility' ) );52 add_filter( 'block_editor_settings', array( $this, 'set_block_template' ) );53 add_filter( 'body_class', array( $this, 'add_fse_body_class' ) );54 55 add_filter( 'post_row_actions', array( $this, 'remove_trash_row_action_for_template_post_types' ), 10, 2 );56 add_filter( 'bulk_actions-edit-wp_template', array( $this, 'remove_trash_bulk_action_for_template_post_type' ) );57 add_action( 'wp_trash_post', array( $this, 'restrict_template_deletion' ) );58 add_action( 'before_delete_post', array( $this, 'restrict_template_deletion' ) );59 add_filter( 'wp_template_type_row_actions', array( $this, 'remove_delete_row_action_for_template_taxonomy' ), 10, 2 );60 add_filter( 'bulk_actions-edit-wp_template_type', array( $this, 'remove_delete_bulk_action_for_template_taxonomy' ) );61 add_action( 'pre_delete_term', array( $this, 'restrict_template_taxonomy_deletion' ), 10, 2 );62 add_action( 'transition_post_status', array( $this, 'restrict_template_drafting' ), 10, 3 );63 add_action( 'admin_menu', array( $this, 'remove_wp_admin_menu_items' ) );64 add_filter( 'block_editor_rest_api_preload_paths', array( $this, 'preload_template_parts' ), 10, 2 );65 41 66 42 $this->theme_slug = normalize_theme_slug( get_stylesheet() ); … … 82 58 83 59 /** 84 * Inserts template data for the theme we are currently switching to.85 *86 * This insertion will only happen if theme supports dotcom FSE.87 * It is hooked into after_switch_theme action.88 */89 public function insert_default_data() {90 // Bail if current theme doesn't support dotcom FSE.91 if ( ! is_theme_supported() ) {92 return;93 }94 95 $this->wp_template_inserter->insert_default_template_data();96 $this->wp_template_inserter->insert_default_pages();97 }98 99 /**100 60 * Register post types. 101 61 */ 102 62 public function register_template_post_types() { 103 63 $this->wp_template_inserter->register_template_post_types(); 104 }105 106 /**107 * Auth callback.108 *109 * @return mixed110 */111 public function meta_template_id_auth_callback() {112 return current_user_can( 'edit_theme_options' );113 }114 115 /**116 * Enqueue assets.117 */118 public function enqueue_script_and_style() {119 $asset_file = include plugin_dir_path( __FILE__ ) . 'dist/dotcom-fse.asset.php';120 $script_dependencies = $asset_file['dependencies'];121 wp_enqueue_script(122 'a8c-full-site-editing-script',123 plugins_url( 'dist/dotcom-fse.js', __FILE__ ),124 is_array( $script_dependencies ) ? $script_dependencies : array(),125 filemtime( plugin_dir_path( __FILE__ ) . 'dist/dotcom-fse.js' ),126 true127 );128 129 wp_localize_script(130 'a8c-full-site-editing-script',131 'fullSiteEditing',132 array(133 'editorPostType' => get_current_screen()->post_type,134 'closeButtonLabel' => $this->get_close_button_label(),135 'closeButtonUrl' => esc_url( $this->get_close_button_url() ),136 'editTemplateBaseUrl' => esc_url( $this->get_edit_template_base_url() ),137 )138 );139 140 $style_file = is_rtl()141 ? 'dotcom-fse.rtl.css'142 : 'dotcom-fse.css';143 wp_enqueue_style(144 'a8c-full-site-editing-style',145 plugins_url( 'dist/' . $style_file, __FILE__ ),146 'wp-edit-post',147 filemtime( plugin_dir_path( __FILE__ ) . 'dist/' . $style_file )148 );149 64 } 150 65 … … 222 137 } 223 138 224 /**225 * Returns the parent post ID if sent as query param when editing a Template from a226 * Post/Page or a Template.227 *228 * @return null|string The parent post ID, or null if not set.229 */230 public function get_parent_post_id() {231 // phpcs:disable WordPress.Security.NonceVerification232 if ( ! isset( $_GET['fse_parent_post'] ) ) {233 return null;234 }235 236 $parent_post_id = absint( $_GET['fse_parent_post'] );237 // phpcs:enable WordPress.Security.NonceVerification238 239 if ( empty( $parent_post_id ) ) {240 return null;241 }242 243 return $parent_post_id;244 }245 246 /**247 * Returns the label for the Gutenberg close button.248 *249 * When we edit a Template from a Post/Page or a Template, we want to replace250 * the close icon with a "Back to" button, to clarify that it will take us251 * back to the previous editing view, and not the Template CPT list.252 *253 * @return null|string Override label string if it should be inserted, or null otherwise.254 */255 public function get_close_button_label() {256 $parent_post_id = $this->get_parent_post_id();257 258 if ( ! $parent_post_id ) {259 return null;260 }261 262 $parent_post_type = get_post_type( $parent_post_id );263 264 // See https://github.com/Automattic/wp-calypso/issues/38075#issuecomment-559900054.265 if ( 'page' === $parent_post_type ) {266 return __( 'Page Layout', 'full-site-editing' );267 }268 269 $parent_post_type_object = get_post_type_object( $parent_post_type );270 271 /* translators: %s: "Back to Post", "Back to Page", "Back to Template", etc. */272 return sprintf( __( 'Back to %s', 'full-site-editing' ), $parent_post_type_object->labels->singular_name );273 }274 275 /**276 * Returns the URL for the Gutenberg close button.277 *278 * In some cases we want to override the default value which would take us to post listing279 * for a given post type. For example, when navigating back from Header, we want to show the280 * parent page editing view, and not the Template CPT list.281 *282 * @return null|string Override URL string if it should be inserted, or null otherwise.283 */284 public function get_close_button_url() {285 $parent_post_id = $this->get_parent_post_id();286 287 if ( ! $parent_post_id ) {288 return null;289 }290 291 $close_button_url = get_edit_post_link( $parent_post_id );292 293 /**294 * Filter the Gutenberg's close button URL when editing Template CPTs.295 *296 * @since 0.1297 *298 * @param string Current close button URL.299 */300 return apply_filters( 'a8c_fse_close_button_link', $close_button_url );301 }302 303 /**304 * Returns the base URL for the Edit Template button. The URL does not contain neither305 * the post ID nor the template ID. Those query arguments should be provided by306 * the Template on the Block.307 *308 * @return string edit link without post ID309 */310 public function get_edit_template_base_url() {311 $edit_post_link = remove_query_arg( 'post', get_edit_post_link( 0, 'edit' ) );312 313 /**314 * Filter the Gutenberg's edit template button base URL315 * when editing pages or posts.316 *317 * @since 0.2318 *319 * @param string Current edit button URL.320 */321 return apply_filters( 'a8c_fse_edit_template_base_url', $edit_post_link );322 }323 324 /** This will merge the post content with the post template, modifiying the $post parameter.325 *326 * @param \WP_Post $post Post instance.327 */328 public function merge_template_and_post( $post ) {329 // Bail if not a REST API Request and not in the editor.330 if ( ! $this->should_merge_template_and_post( $post ) ) {331 return;332 }333 334 $template = new WP_Template();335 $template_content = $template->get_page_template_content();336 337 // Bail if the template has no post content block.338 if ( ! has_block( 'a8c/post-content', $template_content ) ) {339 return;340 }341 342 $post->post_content = preg_replace( '@(<!-- wp:a8c/post-content)(.*?)(/-->)@', "$1$2-->$post->post_content<!-- /wp:a8c/post-content -->", $template_content );343 }344 345 /**346 * Detects if we are in a context where the template and post should be merged.347 *348 * Conditions:349 * 1. Current theme supports it350 * 2. AND in a REST API request (either flavour)351 * 3. OR on a block editor screen (inlined requests using `rest_preload_api_request` )352 * 4. AND editing a post_type that supports full site editing353 *354 * @param \WP_Post $post object for the check.355 * @return bool356 */357 private function should_merge_template_and_post( $post ) {358 $is_rest_api_wpcom = ( defined( 'REST_API_REQUEST' ) && REST_API_REQUEST );359 $is_rest_api_core = ( defined( 'REST_REQUEST' ) && REST_REQUEST );360 $is_block_editor_screen = ( function_exists( 'get_current_screen' ) && get_current_screen() && get_current_screen()->is_block_editor() );361 362 if ( ! ( $is_block_editor_screen || $is_rest_api_core || $is_rest_api_wpcom ) ) {363 return false;364 }365 return $this->is_full_site_page( $post );366 }367 368 /**369 * This will extract the inner blocks of the post content and370 * serialize them back to HTML for saving.371 *372 * @param array $data An array of slashed post data.373 * @param array $postarr An array of sanitized, but otherwise unmodified post data.374 * @return array375 */376 public function remove_template_components( $data, $postarr ) {377 // Bail if the post type is one of the template post types.378 if ( in_array( $postarr['post_type'], $this->template_post_types, true ) ) {379 return $data;380 }381 382 $post_content = wp_unslash( $data['post_content'] );383 384 // Bail if post content has no blocks.385 if ( ! has_blocks( $post_content ) ) {386 return $data;387 }388 389 $post_content_blocks = parse_blocks( $post_content );390 $post_content_key = array_search( 'a8c/post-content', array_column( $post_content_blocks, 'blockName' ), true );391 392 // Bail if no post content block found.393 if ( ! $post_content_key ) {394 return $data;395 }396 397 $data['post_content'] = wp_slash( serialize_blocks( $post_content_blocks[ $post_content_key ]['innerBlocks'] ) );398 return $data;399 }400 401 /**402 * Return an extra class that will be assigned to the body element if a full site page is being edited.403 *404 * That class hides the default post title of the editor and displays a new post title rendered by the post content405 * block in order to have it just before the content of the post.406 *407 * @param string $classes Space-separated list of CSS classes.408 * @return string409 */410 public function toggle_editor_post_title_visibility( $classes ) {411 if ( get_current_screen()->is_block_editor() && $this->is_full_site_page() ) {412 $classes .= ' show-post-title-before-content ';413 }414 return $classes;415 }416 417 /**418 * Sets the block template to be loaded by the editor when creating a new full site page.419 *420 * @param array $editor_settings Default editor settings.421 * @return array Editor settings with the updated template setting.422 */423 public function set_block_template( $editor_settings ) {424 if ( $this->is_full_site_page() ) {425 $fse_template = new WP_Template();426 $template_blocks = $fse_template->get_template_blocks();427 428 $template = array();429 foreach ( $template_blocks as $block ) {430 $template[] = $this->fse_map_block_to_editor_template_setting( $block );431 }432 $editor_settings['template'] = $template;433 $editor_settings['templateLock'] = 'all';434 }435 return $editor_settings;436 }437 438 /**439 * Determine if the current edited post is a full site page.440 * So far we only support static pages.441 *442 * @param object $post optional post object, if not passed in then current post is checked.443 * @return boolean444 */445 public function is_full_site_page( $post = null ) {446 $post_type = get_post_type( $post );447 return 'page' === $post_type || ( 'revision' === $post_type && 'page' === get_post_type( $post->post_parent ) );448 }449 450 /**451 * Determines whether given post belongs to FSE template CPTs.452 *453 * @param WP_Post $post Check if this post belongs to templates.454 *455 * @return boolean456 */457 public function is_template_post_type( $post ) {458 return in_array( $post->post_type, $this->template_post_types, true );459 }460 461 /**462 * Add fse-enabled class to body so we can target css only if plugin enabled.463 *464 * @param array $classes classes to be applied to body.465 * @return array classes to be applied to body.466 */467 public function add_fse_body_class( $classes ) {468 $classes[] = 'fse-enabled';469 return $classes;470 }471 472 /**473 * Returns an array with the expected format of the block template setting syntax.474 *475 * @see https://github.com/WordPress/gutenberg/blob/1414cf0ad1ec3d0f3e86a40815513c15938bb522/docs/designers-developers/developers/block-api/block-templates.md476 *477 * @param array $block Block to convert.478 * @return array479 */480 private function fse_map_block_to_editor_template_setting( $block ) {481 $block_name = $block['blockName'];482 $attrs = $block['attrs'];483 $inner_blocks = $block['innerBlocks'];484 485 $inner_blocks_template = array();486 foreach ( $inner_blocks as $inner_block ) {487 $inner_blocks[] = fse_map_block_to_editor_template_setting( $inner_block );488 }489 return array( $block_name, $attrs, $inner_blocks_template );490 }491 492 /**493 * Removes the Trash action from the quick actions on the Templates list494 *495 * @param array $actions Array of row action links.496 * @param WP_Post $post The post object.497 * @return array498 */499 public function remove_trash_row_action_for_template_post_types( $actions, $post ) {500 if ( $this->is_template_post_type( $post ) ) {501 unset( $actions['trash'] );502 }503 return $actions;504 }505 506 /**507 * Removes the Trash bulk action from the Template List page.508 *509 * @param array $bulk_actions Array of bulk actions.510 * @return array;511 */512 public function remove_trash_bulk_action_for_template_post_type( $bulk_actions ) {513 unset( $bulk_actions['trash'] );514 return $bulk_actions;515 }516 517 /**518 * Prevents posts for the template post types to be deleted.519 *520 * @param integer $post_id The post id.521 */522 public function restrict_template_deletion( $post_id ) {523 if ( $this->is_template_post_type( get_post( $post_id ) ) ) {524 wp_die( esc_html__( 'Templates cannot be deleted.', 'full-site-editing' ) );525 }526 }527 528 // phpcs:disable Generic.CodeAnalysis.UnusedFunctionParameter.FoundBeforeLastUsed529 /**530 * Prevents draftinig of template post types.531 *532 * @param string $new_status New post status.533 * @param string $old_status Old post status.534 * @param object $post Post object for which the status change is attempted.535 */536 public function restrict_template_drafting( $new_status, $old_status, $post ) {537 if ( 'draft' === $new_status && $this->is_template_post_type( $post ) ) {538 wp_die( esc_html__( 'Templates cannot be moved to drafts.', 'full-site-editing' ) );539 }540 }541 // phpcs:enable Generic.CodeAnalysis.UnusedFunctionParameter.FoundBeforeLastUsed542 543 /**544 * Removes the Delete action from the quick actions for the template taxonomy.545 *546 * @param array $actions Array of row action links.547 * @param WP_Term $term The Term object.548 * @return array549 */550 public function remove_delete_row_action_for_template_taxonomy( $actions, $term ) {551 if ( 'wp_template_part_type' === $term->taxonomy ) {552 unset( $actions['delete'] );553 }554 return $actions;555 }556 557 /**558 * Removes the Delete bulk action from the Template Taxonomy list.559 *560 * @param array $bulk_actions Array of bulk actions.561 * @return array562 */563 public function remove_delete_bulk_action_for_template_taxonomy( $bulk_actions ) {564 unset( $bulk_actions['delete'] );565 return $bulk_actions;566 }567 568 /**569 * Prevents template types to be deleted.570 *571 * @param integer $term The Term Id.572 * @param string $taxonomy Taxonomy name.573 */574 public function restrict_template_taxonomy_deletion( $term, $taxonomy ) { // phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundBeforeLastUsed575 if ( 'wp_template_part_type' === $taxonomy ) {576 wp_die( esc_html__( 'Template Types cannon be deleted.', 'full-site-editing' ) );577 }578 }579 580 /**581 * Removes wp admin menu items we don't want like Customize and Widgets.582 */583 public function remove_wp_admin_menu_items() {584 global $submenu;585 586 // For safety.587 if ( ! \A8C\FSE\is_full_site_editing_active() ) {588 return;589 }590 591 // Remove widget submenu.592 remove_submenu_page( 'themes.php', 'widgets.php' );593 594 /*595 * This position is hardcoded in `wp-admin/menu.php` and we can't use `remove_submenu_page`596 * because the customize URL varies depending on the current screen.597 *598 * We also want to access the customizer through the URL, so we shouldn't deny URL access.599 */600 if ( isset( $submenu['themes.php'][6] ) ) {601 unset( $submenu['themes.php'][6] );602 }603 }604 605 /**606 * Registers the footer credit option for API use.607 */608 public function register_footer_credit_setting() {609 /**610 * Note: We do not want to create the option if it doesn't exist. This611 * way, the default option can theoretically change if the user switches612 * site types without changing an option in the list at all. As soon as613 * they make a change to the selection, however, it will be persisted.614 */615 616 // Registers the footercredit option for API use.617 register_setting(618 'general',619 'footercredit',620 array(621 'show_in_rest' => array(622 'name' => 'footer_credit',623 ),624 'type' => 'string',625 'description' => __( 'WordPress Footer Credit', 'full-site-editing' ),626 )627 );628 }629 630 /**631 * Preload the path used to request a template part post when editing it in the block editor.632 *633 * @param array $paths Preload paths.634 * @param WP_Block_Editor_Context $context Current editor context.635 * @return $paths Filtered preload paths.636 */637 public function preload_template_parts( $paths, $context ) {638 $post = $context->post;639 640 if ( $post && 'wp_template_part' === $post->post_type ) {641 $paths[] = sprintf( '/wp/v2/template_parts?wp_id=%s&context=edit', $post->ID );642 }643 return $paths;644 }645 139 } -
full-site-editing/trunk/dotcom-fse/helpers.php
r2574005 r2688373 25 25 require_once __DIR__ . '/blocks/template/index.php'; 26 26 require_once __DIR__ . '/class-full-site-editing.php'; 27 require_once __DIR__ . '/templates/class-rest-templates-controller.php';28 27 require_once __DIR__ . '/templates/class-wp-template.php'; 29 28 require_once __DIR__ . '/templates/class-wp-template-inserter.php'; 30 require_once __DIR__ . '/templates/class-template-image-inserter.php';31 require_once __DIR__ . '/serialize-block-fallback.php';32 29 } 33 30 … … 39 36 */ 40 37 function is_full_site_editing_active() { 41 /** 42 * There are times when this function is called from the WordPress.com public 43 * API context. In this case, we need to switch to the correct blog so that 44 * the functions reference the correct blog context. 45 */ 46 $multisite_id = apply_filters( 'a8c_fse_get_multisite_id', false ); 47 $should_switch = is_multisite() && $multisite_id; 48 if ( $should_switch ) { 49 switch_to_blog( $multisite_id ); 38 // We will always return false in admin and REST API contexts as we work towards getting rid of this. 39 if ( defined( 'REST_API_REQUEST' ) && REST_API_REQUEST ) { 40 return false; 50 41 } 51 42 52 $is_active = is_site_eligible_for_full_site_editing() && is_theme_supported() && did_insert_template_parts(); 53 54 if ( $should_switch ) { 55 restore_current_blog(); 56 } 57 return $is_active; 43 return is_site_eligible_for_full_site_editing() && is_theme_supported() && did_insert_template_parts(); 58 44 } 59 45 … … 197 183 return; 198 184 } 199 200 require_once __DIR__ . '/templates/class-template-image-inserter.php';201 185 require_once __DIR__ . '/templates/class-wp-template-inserter.php'; 202 186 -
full-site-editing/trunk/dotcom-fse/templates/class-wp-template-inserter.php
r2471048 r2688373 432 432 'wp_template_part', // phpcs:ignore WordPress.NamingConventions.ValidPostTypeSlug.ReservedPrefix 433 433 array( 434 'labels' 434 'labels' => array( 435 435 'name' => _x( 'Template Parts', 'post type general name', 'full-site-editing' ), 436 436 'singular_name' => _x( 'Template Part', 'post type singular name', 'full-site-editing' ), … … 455 455 'item_updated' => __( 'Template part updated.', 'full-site-editing' ), 456 456 ), 457 'menu_icon' => 'dashicons-layout', 458 'public' => false, 459 'show_ui' => true, // Otherwise we'd get permission error when trying to edit them. 460 'show_in_menu' => false, 461 'rewrite' => false, 462 'show_in_rest' => true, // Otherwise previews won't be generated in full page view. 463 'rest_base' => 'template_parts', 464 'rest_controller_class' => __NAMESPACE__ . '\REST_Templates_Controller', 465 'capability_type' => 'template_part', 466 'capabilities' => array( 457 'menu_icon' => 'dashicons-layout', 458 'public' => false, 459 'show_ui' => true, // Otherwise we'd get permission error when trying to edit them. 460 'show_in_menu' => false, 461 'rewrite' => false, 462 'capability_type' => 'template_part', 463 'capabilities' => array( 467 464 // You need to be able to edit posts, in order to read templates in their raw form. 468 465 'read' => 'edit_posts', … … 477 474 'publish_posts' => 'edit_theme_options', 478 475 ), 479 'map_meta_cap' 480 'supports' 476 'map_meta_cap' => true, 477 'supports' => array( 481 478 'title', 482 479 'editor', -
full-site-editing/trunk/full-site-editing-plugin.php
r2683894 r2688373 3 3 * Plugin Name: WordPress.com Editing Toolkit 4 4 * Description: Enhances your page creation workflow within the Block Editor. 5 * Version: 3.2 78125 * Version: 3.2 6 6 * Author: Automattic 7 7 * Author URI: https://automattic.com/wordpress-plugins/ … … 43 43 * @var string 44 44 */ 45 define( 'A8C_ETK_PLUGIN_VERSION', '3.2 7812' );45 define( 'A8C_ETK_PLUGIN_VERSION', '3.2' ); 46 46 47 47 // Always include these helper files for dotcom FSE. -
full-site-editing/trunk/readme.txt
r2683894 r2688373 4 4 Requires at least: 5.5 5 5 Tested up to: 5.6 6 Stable tag: 3.2 78126 Stable tag: 3.2 7 7 Requires PHP: 5.6.20 8 8 License: GPLv2 or later
Note: See TracChangeset
for help on using the changeset viewer.