diff --git a/lib/client-assets.php b/lib/client-assets.php index 9757e4b7ff24a8..99aa7f147ecbfc 100644 --- a/lib/client-assets.php +++ b/lib/client-assets.php @@ -203,7 +203,13 @@ function gutenberg_register_packages_scripts( $scripts ) { // Add dependencies that cannot be detected and generated by build tools. switch ( $handle ) { case 'wp-block-library': - array_push( $dependencies, 'editor' ); + if ( + ! gutenberg_is_experiment_enabled( 'gutenberg-no-tinymce' ) || + ! empty( $_GET['requiresTinymce'] ) || + gutenberg_post_being_edited_requires_classic_block() + ) { + array_push( $dependencies, 'editor' ); + } break; case 'wp-edit-post': diff --git a/lib/experimental/assets/tinymce-proxy.js b/lib/experimental/assets/tinymce-proxy.js new file mode 100644 index 00000000000000..91c9e74bb74ed8 --- /dev/null +++ b/lib/experimental/assets/tinymce-proxy.js @@ -0,0 +1,14 @@ +async function reloadWithTinymce() { + const currentUrl = new URL( window.location.href ); + currentUrl.searchParams.set( 'requiresTinymce', '1' ); + window.location.href = currentUrl; +} + +window.tinymce = new Proxy( + {}, + { + get: reloadWithTinymce, + set: reloadWithTinymce, + apply: reloadWithTinymce, + } +); diff --git a/lib/experimental/class--wp-editors.php b/lib/experimental/class--wp-editors.php new file mode 100644 index 00000000000000..bc49ea80a99421 --- /dev/null +++ b/lib/experimental/class--wp-editors.php @@ -0,0 +1,27 @@ +window.wp.needsClassicBlock = true;'; +} +add_action( 'admin_footer', 'gutenberg_declare_classic_block_necessary' ); + +// If user has already requested TinyMCE, we're ending the experiment. +if ( ! empty( $_GET['requiresTinymce'] ) || gutenberg_post_being_edited_requires_classic_block() ) { + return; +} + + +/** + * Disable TinyMCE by introducing a placeholder `_WP_Editors` class. + */ +function gutenberg_disable_tinymce() { + require __DIR__ . '/class--wp-editors.php'; +} + +add_action( 'admin_init', 'gutenberg_disable_tinymce' ); + +/** + * Enqueue TinyMCE proxy script. + * Detects TinyMCE usage and sets the `requiresTinymce` query argument to stop disabling TinyMCE loading. + */ +function gutenberg_enqueue_tinymce_proxy() { + wp_enqueue_script( 'gutenberg-tinymce-proxy', plugins_url( 'assets/tinymce-proxy.js', __FILE__ ) ); +} + +add_action( 'admin_enqueue_scripts', 'gutenberg_enqueue_tinymce_proxy' ); + +/** + * Example TinyMCE usage used for testing. + * Uncomment line 8 in this file to enable. + */ +function gutenberg_test_tinymce_access() { + echo ''; +} + +/** + * Whether the current editor contains a classic block instance. + * + * @return bool True if the editor contains a classic block, false otherwse. + */ +function gutenberg_post_being_edited_requires_classic_block() { + if ( ! is_admin() ) { + return false; + } + + // Handle the post editor. + if ( ! empty( $_GET['post'] ) && ! empty( $_GET['action'] ) && 'edit' === $_GET['action'] ) { + $current_post = get_post( intval( $_GET['post'] ) ); + if ( ! $current_post || is_wp_error( $current_post ) ) { + return false; + } + + $content = $current_post->post_content; + } + + if ( empty( $content ) ) { + return false; + } + + $parsed_blocks = parse_blocks( $content ); + foreach ( $parsed_blocks as $block ) { + if ( empty( $block['blockName'] ) && strlen( trim( $block['innerHTML'] ) ) > 0 ) { + return true; + } + } + + return false; +} diff --git a/lib/experimental/editor-settings.php b/lib/experimental/editor-settings.php index eaa314946ab9a3..15d964e2deec72 100644 --- a/lib/experimental/editor-settings.php +++ b/lib/experimental/editor-settings.php @@ -90,6 +90,9 @@ function gutenberg_enable_experiments() { wp_add_inline_script( 'wp-block-editor', 'window.__experimentalInteractivityAPI = true', 'before' ); } + if ( gutenberg_is_experiment_enabled( 'gutenberg-no-tinymce' ) ) { + wp_add_inline_script( 'wp-block-library', 'window.__experimentalDisableTinymce = true', 'before' ); + } } add_action( 'admin_init', 'gutenberg_enable_experiments' ); diff --git a/lib/experiments-page.php b/lib/experiments-page.php index a2df41908aa257..de1d3dbce71a14 100644 --- a/lib/experiments-page.php +++ b/lib/experiments-page.php @@ -89,6 +89,18 @@ function gutenberg_initialize_experiments_settings() { ) ); + add_settings_field( + 'gutenberg-no-tinymce', + __( 'Disable TinyMCE and Classic block', 'gutenberg' ), + 'gutenberg_display_experiment_field', + 'gutenberg-experiments', + 'gutenberg_experiments_section', + array( + 'label' => __( 'Disable TinyMCE and Classic block', 'gutenberg' ), + 'id' => 'gutenberg-no-tinymce', + ) + ); + register_setting( 'gutenberg-experiments', 'gutenberg-experiments' diff --git a/lib/load.php b/lib/load.php index fbba502d2f5aae..6d3638bf969134 100644 --- a/lib/load.php +++ b/lib/load.php @@ -108,6 +108,11 @@ function gutenberg_is_experiment_enabled( $name ) { require __DIR__ . '/experimental/navigation-theme-opt-in.php'; require __DIR__ . '/experimental/kses.php'; require __DIR__ . '/experimental/l10n.php'; + +if ( gutenberg_is_experiment_enabled( 'gutenberg-no-tinymce' ) ) { + require __DIR__ . '/experimental/disable-tinymce.php'; +} + if ( gutenberg_is_experiment_enabled( 'gutenberg-interactivity-api-core-blocks' ) ) { require __DIR__ . '/experimental/interactivity-api/blocks.php'; } @@ -123,7 +128,6 @@ function gutenberg_is_experiment_enabled( $name ) { require __DIR__ . '/experimental/interactivity-api/directives/wp-style.php'; require __DIR__ . '/experimental/interactivity-api/directives/wp-text.php'; - // Fonts API. if ( ! class_exists( 'WP_Fonts' ) ) { // Fonts API files. diff --git a/packages/block-library/src/index.js b/packages/block-library/src/index.js index 2b4a0f2fb95f2a..911bd0c37451ee 100644 --- a/packages/block-library/src/index.js +++ b/packages/block-library/src/index.js @@ -142,7 +142,6 @@ const getAllBlocks = () => { buttons, calendar, categories, - ...( window.wp && window.wp.oldEditor ? [ classic ] : [] ), // Only add the classic block in WP Context. code, column, columns, @@ -229,6 +228,24 @@ const getAllBlocks = () => { queryTitle, postAuthorBiography, ]; + + // When in a WordPress context, conditionally + // add the classic block and TinyMCE editor + // under any of the following conditions: + // - the current post contains a classic block + // - the experiment to disable TinyMCE isn't active. + // - a query argument specifies that TinyMCE should be loaded + if ( + window?.wp?.oldEditor && + ( window?.wp?.needsClassicBlock || + ! window?.__experimentalDisableTinymce || + !! new URLSearchParams( window?.location?.search ).get( + 'requiresTinymce' + ) ) + ) { + blocks.push( classic ); + } + return blocks.filter( Boolean ); }; diff --git a/packages/block-library/src/missing/edit.js b/packages/block-library/src/missing/edit.js index 1ef143a639ed06..f7aef453b5447c 100644 --- a/packages/block-library/src/missing/edit.js +++ b/packages/block-library/src/missing/edit.js @@ -16,22 +16,46 @@ import { safeHTML } from '@wordpress/dom'; function MissingBlockWarning( { attributes, convertToHTML, clientId } ) { const { originalName, originalUndelimitedContent } = attributes; const hasContent = !! originalUndelimitedContent; - const hasHTMLBlock = useSelect( + const { hasFreeformBlock, hasHTMLBlock } = useSelect( ( select ) => { const { canInsertBlockType, getBlockRootClientId } = select( blockEditorStore ); - return canInsertBlockType( - 'core/html', - getBlockRootClientId( clientId ) - ); + return { + hasFreeformBlock: canInsertBlockType( + 'core/freeform', + getBlockRootClientId( clientId ) + ), + hasHTMLBlock: canInsertBlockType( + 'core/html', + getBlockRootClientId( clientId ) + ), + }; }, [ clientId ] ); const actions = []; let messageHTML; - if ( hasContent && hasHTMLBlock ) { + + const convertToHtmlButton = ( + + ); + + if ( hasContent && ! hasFreeformBlock && ! originalName ) { + if ( hasHTMLBlock ) { + messageHTML = __( + 'It appears you are trying to use the deprecated Classic block. You can leave this block intact, convert its content to a Custom HTML block, or remove it entirely. Alternatively, you can refresh the page to use the Classic block.' + ); + actions.push( convertToHtmlButton ); + } else { + messageHTML = __( + 'It appears you are trying to use the deprecated Classic block. You can leave this block intact, or remove it entirely. Alternatively, you can refresh the page to use the Classic block.' + ); + } + } else if ( hasContent && hasHTMLBlock ) { messageHTML = sprintf( /* translators: %s: block name */ __( @@ -39,11 +63,7 @@ function MissingBlockWarning( { attributes, convertToHTML, clientId } ) { ), originalName ); - actions.push( - - ); + actions.push( convertToHtmlButton ); } else { messageHTML = sprintf( /* translators: %s: block name */