diff --git a/lib/client-assets.php b/lib/client-assets.php
index 9757e4b7ff24a..99aa7f147ecbf 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 0000000000000..91c9e74bb74ed
--- /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 0000000000000..bc49ea80a9942
--- /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 eaa314946ab9a..15d964e2deec7 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 a2df41908aa25..de1d3dbce71a1 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 fbba502d2f5aa..6d3638bf96913 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 2b4a0f2fb95f2..911bd0c37451e 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 1ef143a639ed0..f7aef453b5447 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 */