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 @@ + -