diff --git a/editor/assets/stylesheets/main.scss b/editor/assets/stylesheets/main.scss index d1ed54d2ab7e6..ab4b4855a68b3 100644 --- a/editor/assets/stylesheets/main.scss +++ b/editor/assets/stylesheets/main.scss @@ -1,31 +1,30 @@ -body.toplevel_page_gutenberg, -body.gutenberg_page_gutenberg-demo { +body { background: $white; +} - #update-nag, .update-nag { - display: none; - } +#update-nag, .update-nag { + display: none; +} - #wpcontent { - padding-left: 0; - } +#wpcontent { + padding-left: 0; +} - #wpbody-content { - padding-bottom: 0; - } +#wpbody-content { + padding-bottom: 0; +} - #wpfooter { - display: none; - } +#wpfooter { + display: none; +} - .a11y-speak-region { - left: -1px; - top: -1px; - } +.a11y-speak-region { + left: -1px; + top: -1px; +} - svg { - fill: currentColor; - } +svg { + fill: currentColor; } .gutenberg { diff --git a/gutenberg.php b/gutenberg.php index 95f71a1182dd1..720e34a63907a 100644 --- a/gutenberg.php +++ b/gutenberg.php @@ -13,22 +13,367 @@ define( 'GUTENBERG_DEVELOPMENT_MODE', true ); ### END AUTO-GENERATED DEFINES -require_once dirname( __FILE__ ) . '/lib/init-checks.php'; -if ( gutenberg_can_init() ) { - // Load API functions, register scripts and actions, etc. - require_once dirname( __FILE__ ) . '/lib/class-wp-block-type.php'; - require_once dirname( __FILE__ ) . '/lib/class-wp-block-type-registry.php'; - require_once dirname( __FILE__ ) . '/lib/class-wp-rest-reusable-blocks-controller.php'; - require_once dirname( __FILE__ ) . '/lib/blocks.php'; - require_once dirname( __FILE__ ) . '/lib/client-assets.php'; - require_once dirname( __FILE__ ) . '/lib/compat.php'; - require_once dirname( __FILE__ ) . '/lib/i18n.php'; - require_once dirname( __FILE__ ) . '/lib/parser.php'; - require_once dirname( __FILE__ ) . '/lib/register.php'; - require_once dirname( __FILE__ ) . '/lib/plugin-compat.php'; - - // Register server-side code for individual blocks. - foreach ( glob( dirname( __FILE__ ) . '/blocks/library/*/index.php' ) as $block_logic ) { - require_once $block_logic; +gutenberg_pre_init(); + +/** + * Project. + * + * The main entry point for the Gutenberg editor. Renders the editor on the + * wp-admin page for the plugin. + * + * @todo Remove the temporary fix for the NVDA screen reader and use meaningful + * content instead. See pull #2380 and issues #467 and #503. + * + * @since 0.1.0 + */ +function the_gutenberg_project() { + ?> +
 
+
+
+
+

'; + echo __( 'Gutenberg requires WordPress 4.8 or later to function properly. Please upgrade WordPress before activating Gutenberg.', 'gutenberg' ); + echo '

'; + + deactivate_plugins( array( 'gutenberg/gutenberg.php' ) ); +} + +/** + * Verify that we can initialize the Gutenberg editor , then load it. + * + * @since 1.5.0 + */ +function gutenberg_pre_init() { + // Get unmodified $wp_version. + include( ABSPATH . WPINC . '/version.php' ); + + // Strip '-src' from the version string. Messes up version_compare(). + $version = str_replace( '-src', '', $wp_version ); + + if ( version_compare( $version, '4.8', '<' ) ) { + add_action( 'admin_notices', 'gutenberg_wordpress_version_notice' ); + return; + } + + require_once dirname( __FILE__ ) . '/lib/load.php'; + + if ( version_compare( $version, '4.9-beta1-41829', '>=' ) ) { + add_filter( 'replace_editor', 'gutenberg_init', 10, 2 ); + } else { + add_action( 'load-post.php', 'gutenberg_intercept_edit_post' ); + add_action( 'load-post-new.php', 'gutenberg_intercept_post_new' ); + } +} + +/** + * Initialize Gutenberg. + * + * Load API functions, register scripts and actions, etc. + * + * @param bool $return Whether to replace the editor. Used in the `replace_editor` filter. + * @param object $post The post to edit or an auto-draft. + * @return bool Whether Gutenberg was initialized. + */ +function gutenberg_init( $return, $post ) { + if ( true === $return && current_filter() === 'replace_editor' ) { + return $return; + } + + if ( isset( $_GET['classic-editor'] ) ) { + return false; + } + + $post_type = $post->post_type; + $post_type_object = get_post_type_object( $post_type ); + + if ( 'attachment' === $post_type ) { + return false; + } + + if ( ! $post_type_object->show_in_rest ) { + return false; + } + + if ( ! post_type_supports( $post_type, 'editor' ) ) { + return false; + } + + require_once( ABSPATH . 'wp-admin/admin-header.php' ); + the_gutenberg_project(); + + return true; +} + +/** + * Emulate post.php + */ +function gutenberg_intercept_edit_post() { + global $post_type, $post_type_object, $post, $post_id, $post_ID, $title, $editing, + $typenow, $parent_file, $submenu_file, $post_new_file; + + wp_reset_vars( array( 'action' ) ); + + // Other actions are handled in post.php. + if ( 'edit' !== $GLOBALS['action'] ) { + return; + } + + if ( isset( $_GET['post'] ) ) { + $post_ID = (int) $_GET['post']; + $post_id = $post_ID; } + + if ( empty( $post_id ) ) { + return; + } + + $post = get_post( $post_id ); + + // Errors and invalid requests are handled in post.php, do not intercept. + if ( $post ) { + $post_type = $post->post_type; + $post_type_object = get_post_type_object( $post_type ); + } else { + return; + } + + if ( ! $post_type_object ) { + return; + } + + if ( ! in_array( $typenow, get_post_types( array( 'show_ui' => true ) ) ) ) { + return; + } + + if ( ! current_user_can( 'edit_post', $post_id ) ) { + return; + } + + if ( 'trash' == $post->post_status ) { + return; + } + + if ( ! empty( $_GET['get-post-lock'] ) ) { + check_admin_referer( 'lock-post_' . $post_id ); + wp_set_post_lock( $post_id ); + wp_redirect( get_edit_post_link( $post_id, 'url' ) ); + exit(); + } + + $editing = true; + $title = $post_type_object->labels->edit_item; + + $post_type = $post->post_type; + if ( 'post' == $post_type ) { + $parent_file = 'edit.php'; + $submenu_file = 'edit.php'; + $post_new_file = 'post-new.php'; + } elseif ( 'attachment' == $post_type ) { + $parent_file = 'upload.php'; + $submenu_file = 'upload.php'; + $post_new_file = 'media-new.php'; + } else { + if ( isset( $post_type_object ) && $post_type_object->show_in_menu && true !== $post_type_object->show_in_menu ) { + $parent_file = $post_type_object->show_in_menu; + } else { + $parent_file = "edit.php?post_type=$post_type"; + } + + $submenu_file = "edit.php?post_type=$post_type"; + $post_new_file = "post-new.php?post_type=$post_type"; + } + + if ( gutenberg_init( false, $post ) ) { + include( ABSPATH . 'wp-admin/admin-footer.php' ); + exit; + } +} + +/** + * Emulate post-new.php + */ +function gutenberg_intercept_post_new() { + global $post_type, $post_type_object, $post, $post_ID, $title, $editing, + $parent_file, $submenu_file, $_registered_pages; + + if ( ! isset( $_GET['post_type'] ) ) { + $post_type = 'post'; + } elseif ( in_array( $_GET['post_type'], get_post_types( array( 'show_ui' => true ) ) ) ) { + $post_type = $_GET['post_type']; + } else { + return; + } + $post_type_object = get_post_type_object( $post_type ); + + if ( 'post' == $post_type ) { + $parent_file = 'edit.php'; + $submenu_file = 'post-new.php'; + } elseif ( 'attachment' == $post_type ) { + if ( wp_redirect( admin_url( 'media-new.php' ) ) ) { + exit; + } + } else { + $submenu_file = "post-new.php?post_type=$post_type"; + if ( isset( $post_type_object ) && $post_type_object->show_in_menu && true !== $post_type_object->show_in_menu ) { + $parent_file = $post_type_object->show_in_menu; + // What if there isn't a post-new.php item for this post type? + if ( ! isset( $_registered_pages[ get_plugin_page_hookname( "post-new.php?post_type=$post_type", $post_type_object->show_in_menu ) ] ) ) { + if ( isset( $_registered_pages[ get_plugin_page_hookname( "edit.php?post_type=$post_type", $post_type_object->show_in_menu ) ] ) ) { + // Fall back to edit.php for that post type, if it exists. + $submenu_file = "edit.php?post_type=$post_type"; + } else { + // Otherwise, give up and highlight the parent. + $submenu_file = $parent_file; + } + } + } else { + $parent_file = "edit.php?post_type=$post_type"; + } + } + + $title = $post_type_object->labels->add_new_item; + $editing = true; + + // Errors and invalid requests are handled in post-new.php, do not intercept. + if ( ! current_user_can( $post_type_object->cap->edit_posts ) || ! current_user_can( $post_type_object->cap->create_posts ) ) { + return; + } + + // Schedule auto-draft cleanup. + if ( ! wp_next_scheduled( 'wp_scheduled_auto_draft_delete' ) ) { + wp_schedule_event( time(), 'daily', 'wp_scheduled_auto_draft_delete' ); + } + + $post = get_default_post_to_edit( $post_type, true ); + $post_ID = $post->ID; + + if ( gutenberg_init( false, $post ) ) { + include( ABSPATH . 'wp-admin/admin-footer.php' ); + exit; + } +} + +/** + * Redirects the demo page to edit a new post. + */ +function gutenberg_redirect_demo() { + global $pagenow; + + if ( 'admin.php' === $pagenow && isset( $_GET['page'] ) && 'gutenberg' === $_GET['page'] ) { + wp_safe_redirect( admin_url( 'post-new.php?gutenberg-demo' ) ); + exit; + } +} +add_action( 'admin_init', 'gutenberg_redirect_demo' ); + +/** + * Adds the filters to register additional links for the Gutenberg editor in + * the post/page screens. + * + * @since 1.5.0 + */ +function gutenberg_add_edit_link_filters() { + // For hierarchical post types. + add_filter( 'page_row_actions', 'gutenberg_add_edit_link', 10, 2 ); + // For non-hierarchical post types. + add_filter( 'post_row_actions', 'gutenberg_add_edit_link', 10, 2 ); +} +add_action( 'admin_init', 'gutenberg_add_edit_link_filters' ); + +/** + * Registers an additional link in the post/page screens to edit any post/page in + * the Classic editor. + * + * @since 1.5.0 + * + * @param array $actions Post actions. + * @param WP_Post $post Edited post. + * + * @return array Updated post actions. + */ +function gutenberg_add_edit_link( $actions, $post ) { + if ( 'trash' === $post->post_status || ! post_type_supports( $post->post_type, 'editor' ) ) { + return $actions; + } + + $edit_url = get_edit_post_link( $post->ID, 'raw' ); + $edit_url = add_query_arg( 'classic-editor', '', $edit_url ); + + // Build the classic edit action. See also: WP_Posts_List_Table::handle_row_actions(). + $title = _draft_or_post_title( $post->ID ); + $edit_action = array( + 'classic' => sprintf( + '%s', + esc_url( $edit_url ), + esc_attr( sprintf( + /* translators: %s: post title */ + __( 'Edit “%s” in the classic editor', 'gutenberg' ), + $title + ) ), + __( 'Classic Editor', 'gutenberg' ) + ), + ); + + // Insert the Classic Edit action after the Edit action. + $edit_offset = array_search( 'edit', array_keys( $actions ), true ); + $actions = array_merge( + array_slice( $actions, 0, $edit_offset + 1 ), + $edit_action, + array_slice( $actions, $edit_offset + 1 ) + ); + + return $actions; } diff --git a/lib/client-assets.php b/lib/client-assets.php index 832b7706ccc67..8ee0dde83a19b 100644 --- a/lib/client-assets.php +++ b/lib/client-assets.php @@ -167,7 +167,8 @@ function gutenberg_register_scripts_and_styles() { filemtime( gutenberg_dir_path() . 'blocks/build/edit-blocks.css' ) ); } -add_action( 'init', 'gutenberg_register_scripts_and_styles' ); +add_action( 'wp_enqueue_scripts', 'gutenberg_register_scripts_and_styles', 5 ); +add_action( 'admin_enqueue_scripts', 'gutenberg_register_scripts_and_styles', 5 ); /** * Append result of internal request to REST API for purpose of preloading @@ -596,11 +597,7 @@ function gutenberg_color_palette() { * @param string $hook Screen name. */ function gutenberg_editor_scripts_and_styles( $hook ) { - if ( ! preg_match( '/(toplevel|gutenberg)_page_gutenberg(-demo)?/', $hook, $page_match ) ) { - return; - } - - $is_demo = isset( $page_match[2] ); + $is_demo = isset( $_GET['gutenberg-demo'] ); wp_add_inline_script( 'editor', 'window.wp.oldEditor = window.wp.editor;', 'after' @@ -683,36 +680,9 @@ function gutenberg_editor_scripts_and_styles( $hook ) { $word_count_script = wp_scripts()->query( 'word-count' ); array_push( $word_count_script->deps, 'wp-utils' ); - // Parse post type from parameters. - $post_type = null; - if ( ! isset( $_GET['post_type'] ) ) { - $post_type = 'post'; - } else { - $post_types = get_post_types( array( - 'show_ui' => true, - ) ); - - if ( in_array( $_GET['post_type'], $post_types ) ) { - $post_type = $_GET['post_type']; - } else { - wp_die( __( 'Invalid post type.', 'gutenberg' ) ); - } - } - - // Parse post ID from parameters. - $post_id = null; - if ( isset( $_GET['post_id'] ) && (int) $_GET['post_id'] > 0 ) { - $post_id = (int) $_GET['post_id']; - } - - // Create an auto-draft if new post. - if ( ! $post_id ) { - $default_post_to_edit = get_default_post_to_edit( $post_type, true ); - $post_id = $default_post_to_edit->ID; - } - - // Generate API-prepared post from post ID. - $post_to_edit = gutenberg_get_post_to_edit( $post_id ); + global $post; + // Generate API-prepared post. + $post_to_edit = gutenberg_get_post_to_edit( $post ); if ( is_wp_error( $post_to_edit ) ) { wp_die( $post_to_edit->get_error_message() ); } @@ -720,10 +690,9 @@ function gutenberg_editor_scripts_and_styles( $hook ) { // Set initial title to empty string for auto draft for duration of edit. $is_new_post = 'auto-draft' === $post_to_edit['status']; if ( $is_new_post ) { - $default_title = apply_filters( 'default_title', '' ); $post_to_edit['title'] = array( - 'raw' => $default_title, - 'rendered' => apply_filters( 'the_title', $default_title, $post_id ), + 'raw' => '', + 'rendered' => apply_filters( 'the_title', '', $post->ID ), ); } @@ -733,14 +702,17 @@ function gutenberg_editor_scripts_and_styles( $hook ) { '/wp/v2/taxonomies?context=edit', gutenberg_get_rest_link( $post_to_edit, 'about', 'edit' ), ); + if ( ! $is_new_post ) { $preload_paths[] = gutenberg_get_rest_link( $post_to_edit, 'version-history' ); } + $preload_data = array_reduce( $preload_paths, 'gutenberg_preload_api_request', array() ); + wp_add_inline_script( 'wp-components', sprintf( 'window._wpAPIDataPreload = %s', wp_json_encode( $preload_data ) ), @@ -772,11 +744,13 @@ function gutenberg_editor_scripts_and_styles( $hook ) { // Preload server-registered block schemas. $block_registry = WP_Block_Type_Registry::get_instance(); $schemas = array(); + foreach ( $block_registry->get_all_registered() as $block_name => $block_type ) { if ( isset( $block_type->attributes ) ) { $schemas[ $block_name ] = $block_type->attributes; } } + wp_localize_script( 'wp-blocks', '_wpBlocksAttributes', $schemas ); // Initialize the editor. diff --git a/lib/compat.php b/lib/compat.php index efa2912c3022a..4f1c317f048ab 100644 --- a/lib/compat.php +++ b/lib/compat.php @@ -132,7 +132,7 @@ function gutenberg_ensure_wp_api_request() { */ function gutenberg_disable_editor_settings_wpautop( $settings ) { $post = get_post(); - if ( is_object( $post ) && gutenberg_post_has_blocks( $post->ID ) ) { + if ( is_object( $post ) && gutenberg_post_has_blocks( $post ) ) { $settings['wpautop'] = false; } diff --git a/lib/init-checks.php b/lib/init-checks.php deleted file mode 100644 index 4f3849282185e..0000000000000 --- a/lib/init-checks.php +++ /dev/null @@ -1,42 +0,0 @@ -

'; - echo __( 'Gutenberg requires WordPress 4.8 or later to function properly. Please upgrade WordPress before activating Gutenberg.', 'gutenberg' ); - echo '

'; - - deactivate_plugins( array( 'gutenberg/gutenberg.php' ) ); -} - -/** - * Verify that we can initialize the Gutenberg editor plugin. - * - * @since 0.1.0 - * @return bool Whether we can initialize the plugin. - */ -function gutenberg_can_init() { - global $wp_version; - - // Strip `-src` and `-beta` suffixes from $wp_version before comparing. - // Otherwise the comparison returns incorrect results (`4.8-src` < `4.8`). - if ( version_compare( strtok( $wp_version, '-' ), '4.8', '<' ) ) { - add_action( 'admin_notices', 'gutenberg_wordpress_version_notice' ); - return false; - } - - return true; -} diff --git a/lib/load.php b/lib/load.php new file mode 100644 index 0000000000000..668cac81c525b --- /dev/null +++ b/lib/load.php @@ -0,0 +1,25 @@ + -
 
-
-
-
- get_node( 'edit' ); - if ( ! $edit_node ) { - return; - } - - $queried_object = get_queried_object(); - if ( empty( $queried_object ) || empty( $queried_object->post_type ) || ! post_type_exists( $queried_object->post_type ) || ! gutenberg_can_edit_post( $queried_object->ID ) ) { - return; - } - $post = $queried_object; - - if ( ! get_post_type_object( $post->post_type )->show_in_admin_bar ) { - return; - } - - $classic_text = __( 'Edit in Classic Editor', 'gutenberg' ); - remove_filter( 'get_edit_post_link', 'gutenberg_filter_edit_post_link', 10 ); - $classic_url = get_edit_post_link( $post->ID, 'raw' ); - add_filter( 'get_edit_post_link', 'gutenberg_filter_edit_post_link', 10, 3 ); - - if ( empty( $classic_url ) || ! post_type_supports( $post->post_type, 'editor' ) ) { - return; - } - - $gutenberg_text = __( 'Edit in Gutenberg', 'gutenberg' ); - $gutenberg_url = gutenberg_get_edit_post_url( $post->ID ); - - $is_gutenberg_default = gutenberg_post_has_blocks( $post->ID ); - - // Update title for edit link to indicate default editor. - $wp_admin_bar->add_node( array_merge( - (array) $edit_node, - array( - 'title' => $is_gutenberg_default ? $gutenberg_text : $classic_text, - ) - ) ); - - // Add submenu item under link to go to Gutenberg editor or classic editor. - $wp_admin_bar->add_node( array( - 'id' => 'edit_alt', - 'parent' => 'edit', - 'href' => $is_gutenberg_default ? $classic_url : $gutenberg_url, - 'title' => $is_gutenberg_default ? $classic_text : $gutenberg_text, - ) ); - -} -add_action( 'admin_bar_menu', 'gutenberg_add_admin_bar_edit_link', 81 ); - -/** - * Adds the filters to register additional links for the Gutenberg editor in - * the post/page screens. - * - * @since 0.1.0 - */ -function gutenberg_add_edit_links_filters() { - // For hierarchical post types. - add_filter( 'page_row_actions', 'gutenberg_add_edit_links', 10, 2 ); - // For non-hierarchical post types. - add_filter( 'post_row_actions', 'gutenberg_add_edit_links', 10, 2 ); -} -add_action( 'admin_init', 'gutenberg_add_edit_links_filters' ); - -/** - * Registers additional links in the post/page screens to edit any post/page in - * the Gutenberg editor. - * - * @since 0.1.0 - * - * @param array $actions Post actions. - * @param WP_Post $post Edited post. - * - * @return array Updated post actions. - */ -function gutenberg_add_edit_links( $actions, $post ) { - if ( ! gutenberg_can_edit_post( $post->ID ) || - 'trash' === $post->post_status || - ! post_type_supports( $post->post_type, 'editor' ) || - ! apply_filters( 'gutenberg_add_edit_link_for_post_type', true, $post->post_type, $post ) ) { - return $actions; - } - - remove_filter( 'get_edit_post_link', 'gutenberg_filter_edit_post_link', 10 ); - $classic_url = get_edit_post_link( $post->ID, 'raw' ); - add_filter( 'get_edit_post_link', 'gutenberg_filter_edit_post_link', 10, 3 ); - - // Build the new edit actions. See also: WP_Posts_List_Table::handle_row_actions(). - $title = _draft_or_post_title( $post->ID ); - $edit_actions = array( - 'classic hide-if-no-js' => sprintf( - '%s', - esc_url( $classic_url ), - esc_attr( sprintf( - /* translators: %s: post title */ - __( 'Edit “%s” in the classic editor', 'gutenberg' ), - $title - ) ), - __( 'Classic Editor', 'gutenberg' ) - ), - 'gutenberg hide-if-no-js' => sprintf( - '%s', - esc_url( gutenberg_get_edit_post_url( $post->ID ) ), - esc_attr( sprintf( - /* translators: %s: post title */ - __( 'Edit “%s” in the Gutenberg editor', 'gutenberg' ), - $title - ) ), - __( 'Gutenberg', 'gutenberg' ) - ), - ); - - // Insert the new actions in place of the Edit action. - $edit_offset = array_search( 'edit', array_keys( $actions ), true ); - $actions = array_merge( - array_slice( $actions, 0, $edit_offset ), - $edit_actions, - array_slice( $actions, $edit_offset + 1 ) - ); - - return $actions; -} - -/** - * Get the edit post URL for Gutenberg. - * - * @since 0.5.0 - * - * @param int $post_id Post ID. - * @return string|false URL or false if not available. - */ -function gutenberg_get_edit_post_url( $post_id ) { - // Note that menu_page_url() cannot be used because it does not work on the frontend. - $gutenberg_url = admin_url( 'admin.php?page=gutenberg' ); - $gutenberg_url = add_query_arg( 'post_id', $post_id, $gutenberg_url ); - return $gutenberg_url; -} - -/** - * Filters the post edit link to default to the Gutenberg editor when the post content contains a block. - * - * @since 0.5.0 - * - * @param string $url The edit link URL. - * @param int $post_id Post ID. - * @param string $context The link context. If set to 'display' then ampersands are encoded. - * @return string Edit post link. - */ -function gutenberg_filter_edit_post_link( $url, $post_id, $context ) { - // Avoid redirect to Gutenberg after saving a block post in Classic editor. - $sendback = wp_get_referer(); - if ( $sendback && ( - 0 === strpos( $sendback, parse_url( admin_url( 'post.php' ), PHP_URL_PATH ) ) || - 0 === strpos( $sendback, parse_url( admin_url( 'post-new.php' ), PHP_URL_PATH ) ) ) ) { - return $url; - } - - $post = get_post( $post_id ); - if ( gutenberg_can_edit_post( $post_id ) && gutenberg_post_has_blocks( $post_id ) && post_type_supports( get_post_type( $post_id ), 'editor' ) ) { - $gutenberg_url = gutenberg_get_edit_post_url( $post->ID ); - if ( 'display' === $context ) { - $gutenberg_url = esc_url( $gutenberg_url ); - } - $url = $gutenberg_url; - } - return $url; -} -add_filter( 'get_edit_post_link', 'gutenberg_filter_edit_post_link', 10, 3 ); - /** * Return whether the post can be edited in Gutenberg and by the current user. * @@ -279,24 +37,12 @@ function gutenberg_can_edit_post( $post_id ) { * * @since 0.5.0 * - * @param int $post_id Post ID. - * @return bool Whether the post has blocks. - */ -function gutenberg_post_has_blocks( $post_id ) { - $post = get_post( $post_id ); - return $post && content_has_blocks( $post->post_content ); -} - -/** - * Determine whether a content string contains blocks. - * - * @since 1.3.0 - * - * @param string $content Content to test. - * @return bool Whether the content contains blocks. + * @param object $post Post. + * @return bool Whether the post has blocks. */ -function content_has_blocks( $content ) { - return false !== strpos( $content, '