: str_replace(): Passing null to parameter #2 ($replace) of type array|string is deprecated in
<label for="classic-editor-allow-sites"><?php _e( 'Allow site admins to change settings', 'classic-editor' ); ?></label>
<p class="description"><?php _e( 'By default the block editor is replaced with the classic editor and users cannot switch editors.', 'classic-editor' ); ?></p>
public static function save_network_settings() {
isset( $_POST['classic-editor-network-settings'] ) &&
current_user_can( 'manage_network_options' ) &&
wp_verify_nonce( $_POST['classic-editor-network-settings'], 'allow-site-admin-settings' ) // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
if ( isset( $_POST['classic-editor-replace'] ) && $_POST['classic-editor-replace'] === 'block' ) {
update_network_option( null, 'classic-editor-replace', 'block' );
update_network_option( null, 'classic-editor-replace', 'classic' );
if ( isset( $_POST['classic-editor-allow-sites'] ) && $_POST['classic-editor-allow-sites'] === 'allow' ) {
update_network_option( null, 'classic-editor-allow-sites', 'allow' );
update_network_option( null, 'classic-editor-allow-sites', 'disallow' );
* Add a hidden field in edit-form-advanced.php
* to help redirect back to the classic editor on saving.
public static function add_redirect_helper() {
<input type="hidden" name="classic-editor" value="" />
* Remember when the classic editor was used to edit a post.
public static function remember_classic_editor( $post ) {
$post_type = get_post_type( $post );
if ( $post_type && post_type_supports( $post_type, 'editor' ) ) {
self::remember( $post->ID, 'classic-editor' );
* Remember when the block editor was used to edit a post.
public static function remember_block_editor( $editor_settings, $context ) {
if ( is_a( $context, 'WP_Post' ) ) {
} elseif ( ! empty( $context->post ) ) {
$post_type = get_post_type( $post );
if ( $post_type && self::can_edit_post_type( $post_type ) ) {
self::remember( $post->ID, 'block-editor' );
private static function remember( $post_id, $editor ) {
if ( get_post_meta( $post_id, 'classic-editor-remember', true ) !== $editor ) {
update_post_meta( $post_id, 'classic-editor-remember', $editor );
* Choose which editor to use for a post.
* Passes through `$which_editor` for block editor (it's sets to `true` but may be changed by another plugin).
* @uses `use_block_editor_for_post` filter.
* @param boolean $use_block_editor True for block editor, false for classic editor.
* @param WP_Post $post The post being edited.
* @return boolean True for block editor, false for classic editor.
public static function choose_editor( $use_block_editor, $post ) {
$settings = self::get_settings();
$editors = self::get_enabled_editors_for_post( $post );
// If no editor is supported, pass through `$use_block_editor`.
if ( ! $editors['block_editor'] && ! $editors['classic_editor'] ) {
return $use_block_editor;
// Open the default editor when no $post and for "Add New" links,
// or the alternate editor when the user is switching editors.
// phpcs:disable WordPress.Security.NonceVerification.Recommended
if ( empty( $post->ID ) || $post->post_status === 'auto-draft' ) {
( $settings['editor'] === 'classic' && ! isset( $_GET['classic-editor__forget'] ) ) || // Add New
( isset( $_GET['classic-editor'] ) && isset( $_GET['classic-editor__forget'] ) ) // Switch to classic editor when no draft post.
$use_block_editor = false;
} elseif ( self::is_classic( $post->ID ) ) {
$use_block_editor = false;
// phpcs:enable WordPress.Security.NonceVerification.Recommended
// Enforce the editor if set by plugins.
if ( $use_block_editor && ! $editors['block_editor'] ) {
$use_block_editor = false;
} elseif ( ! $use_block_editor && ! $editors['classic_editor'] && $editors['block_editor'] ) {
$use_block_editor = true;
return $use_block_editor;
* Keep the `classic-editor` query arg through redirects when saving posts.
public static function redirect_location( $location ) {
isset( $_REQUEST['classic-editor'] ) || // phpcs:ignore WordPress.Security.NonceVerification.Recommended
( isset( $_POST['_wp_http_referer'] ) && strpos( $_POST['_wp_http_referer'], '&classic-editor' ) !== false ) // phpcs:ignore WordPress.Security.NonceVerification.Recommended, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized, WordPress.Security.NonceVerification.Missing
$location = add_query_arg( 'classic-editor', '', $location );
* Keep the `classic-editor` query arg when looking at revisions.
public static function get_edit_post_link( $url ) {
$settings = self::get_settings();
if ( isset( $_REQUEST['classic-editor'] ) || $settings['editor'] === 'classic' ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
$url = add_query_arg( 'classic-editor', '', $url );
public static function add_meta_box( $post_type, $post ) {
$editors = self::get_enabled_editors_for_post( $post );
if ( ! $editors['block_editor'] || ! $editors['classic_editor'] ) {
// Editors cannot be switched.
$id = 'classic-editor-switch-editor';
$title = __( 'Editor', 'classic-editor' );
$callback = array( __CLASS__, 'do_meta_box' );
'__back_compat_meta_box' => true,
add_meta_box( $id, $title, $callback, null, 'side', 'default', $args );
public static function do_meta_box( $post ) {
$edit_url = get_edit_post_link( $post->ID, 'raw' );
// Switching to block editor.
$edit_url = remove_query_arg( 'classic-editor', $edit_url );
// Forget the previous value when going to a specific editor.
$edit_url = add_query_arg( 'classic-editor__forget', '', $edit_url );
<p style="margin: 1em 0;">
<a href="<?php echo esc_url( $edit_url ); ?>"><?php _e( 'Switch to block editor', 'classic-editor' ); ?></a>
public static function enqueue_block_editor_scripts() {
// get_enabled_editors_for_post() needs a WP_Post or post_ID.
if ( empty( $GLOBALS['post'] ) ) {
$editors = self::get_enabled_editors_for_post( $GLOBALS['post'] );
if ( ! $editors['classic_editor'] ) {
// Editor cannot be switched.
plugins_url( 'js/block-editor-plugin.js', __FILE__ ),
array( 'wp-element', 'wp-components', 'lodash' ),
'classicEditorPluginL10n',
array( 'linkText' => __( 'Switch to classic editor', 'classic-editor' ) )
* Add a link to the settings on the Plugins screen.
public static function add_settings_link( $links, $file ) {
$settings = self::get_settings();
if ( $file === 'classic-editor/classic-editor.php' && ! $settings['hide-settings-ui'] && current_user_can( 'manage_options' ) ) {
if ( current_filter() === 'plugin_action_links' ) {
$url = admin_url( 'options-writing.php#classic-editor-options' );
$url = admin_url( '/network/settings.php#classic-editor-options' );
// Prevent warnings in PHP 7.0+ when a plugin uses this filter incorrectly.
$links[] = sprintf( '<a href="%s">%s</a>', $url, __( 'Settings', 'classic-editor' ) );
private static function can_edit_post_type( $post_type ) {
if ( function_exists( 'gutenberg_can_edit_post_type' ) ) {
$can_edit = gutenberg_can_edit_post_type( $post_type );
} elseif ( function_exists( 'use_block_editor_for_post_type' ) ) {
$can_edit = use_block_editor_for_post_type( $post_type );
* Checks which editors are enabled for the post type.
* @param string $post_type The post type.
* @return array Associative array of the editors and whether they are enabled for the post type.
private static function get_enabled_editors_for_post_type( $post_type ) {
if ( isset( self::$supported_post_types[ $post_type ] ) ) {
return self::$supported_post_types[ $post_type ];
$classic_editor = post_type_supports( $post_type, 'editor' );
$block_editor = self::can_edit_post_type( $post_type );
'classic_editor' => $classic_editor,
'block_editor' => $block_editor,
* Filters the editors that are enabled for the post type.
* @param array $editors Associative array of the editors and whether they are enabled for the post type.
* @param string $post_type The post type.
$editors = apply_filters( 'classic_editor_enabled_editors_for_post_type', $editors, $post_type );
self::$supported_post_types[ $post_type ] = $editors;
* Checks which editors are enabled for the post.
* @param WP_Post $post The post object.
* @return array Associative array of the editors and whether they are enabled for the post.
private static function get_enabled_editors_for_post( $post ) {
$post_type = get_post_type( $post );
'classic_editor' => false,
$editors = self::get_enabled_editors_for_post_type( $post_type );
* Filters the editors that are enabled for the post.
* @param array $editors Associative array of the editors and whether they are enabled for the post.
* @param WP_Post $post The post object.
return apply_filters( 'classic_editor_enabled_editors_for_post', $editors, $post );
* Adds links to the post/page screens to edit any post or page in
* the classic editor or block editor.
* @param array $actions Post actions.
* @param WP_Post $post Edited post.
* @return array Updated post actions.
public static function add_edit_links( $actions, $post ) {
// This is in Gutenberg, don't duplicate it.
if ( array_key_exists( 'classic', $actions ) ) {
unset( $actions['classic'] );
if ( ! array_key_exists( 'edit', $actions ) ) {
$edit_url = get_edit_post_link( $post->ID, 'raw' );
$editors = self::get_enabled_editors_for_post( $post );
// Do not show the links if only one editor is available.
if ( ! $editors['classic_editor'] || ! $editors['block_editor'] ) {
// Forget the previous value when going to a specific editor.
$edit_url = add_query_arg( 'classic-editor__forget', '', $edit_url );
// Build the edit actions. See also: WP_Posts_List_Table::handle_row_actions().
$title = _draft_or_post_title( $post->ID );
// Link to the block editor.
$url = remove_query_arg( 'classic-editor', $edit_url );
$text = _x( 'Edit (block editor)', 'Editor Name', 'classic-editor' );
/* translators: %s: post title */
$label = sprintf( __( 'Edit “%s” in the block editor', 'classic-editor' ), $title );
$edit_block = sprintf( '<a href="%s" aria-label="%s">%s</a>', esc_url( $url ), esc_attr( $label ), $text );
// Link to the classic editor.
$url = add_query_arg( 'classic-editor', '', $edit_url );
$text = _x( 'Edit (classic editor)', 'Editor Name', 'classic-editor' );
/* translators: %s: post title */
$label = sprintf( __( 'Edit “%s” in the classic editor', 'classic-editor' ), $title );
$edit_classic = sprintf( '<a href="%s" aria-label="%s">%s</a>', esc_url( $url ), esc_attr( $label ), $text );
'classic-editor-block' => $edit_block,
'classic-editor-classic' => $edit_classic,
// Insert the new Edit actions instead of the Edit action.
$edit_offset = array_search( 'edit', array_keys( $actions ), true );
array_splice( $actions, $edit_offset, 1, $edit_actions );
* Show the editor that will be used in a "post state" in the Posts list table.
public static function add_post_state( $post_states, $post ) {
if ( get_post_status( $post ) === 'trash' ) {
$editors = self::get_enabled_editors_for_post( $post );
if ( ! $editors['classic_editor'] && ! $editors['block_editor'] ) {
} elseif ( $editors['classic_editor'] && ! $editors['block_editor'] ) {
// Forced to classic editor.
$state = '<span class="classic-editor-forced-state">' . _x( 'classic editor', 'Editor Name', 'classic-editor' ) . '</span>';
} elseif ( ! $editors['classic_editor'] && $editors['block_editor'] ) {
// Forced to block editor.
$state = '<span class="classic-editor-forced-state">' . _x( 'block editor', 'Editor Name', 'classic-editor' ) . '</span>';
$last_editor = get_post_meta( $post->ID, 'classic-editor-remember', true );
$is_classic = ( $last_editor === 'classic-editor' );
} elseif ( ! empty( $post->post_content ) ) {
$is_classic = ! self::has_blocks( $post->post_content );
$settings = self::get_settings();
$is_classic = ( $settings['editor'] === 'classic' );
$state = $is_classic ? _x( 'Classic editor', 'Editor Name', 'classic-editor' ) : _x( 'Block editor', 'Editor Name', 'classic-editor' );
// Fix PHP 7+ warnings if another plugin returns unexpected type.
$post_states = (array) $post_states;
$post_states['classic-editor-plugin'] = $state;
public static function add_edit_php_inline_style() {
.classic-editor-forced-state {
public static function on_admin_init() {
if ( $pagenow !== 'post.php' ) {
$settings = self::get_settings();
$post_id = self::get_edited_post_id();
if ( $post_id && ( $settings['editor'] === 'classic' || self::is_classic( $post_id ) ) ) {
// Move the Privacy Policy help notice back under the title field.
remove_action( 'admin_notices', array( 'WP_Privacy_Policy_Content', 'notice' ) );
add_action( 'edit_form_after_title', array( 'WP_Privacy_Policy_Content', 'notice' ) );
// Need to support WP < 5.0
private static function has_blocks( $post = null ) {
if ( ! is_string( $post ) ) {
$wp_post = get_post( $post );
if ( $wp_post instanceof WP_Post ) {
$post = $wp_post->post_content;
return false !== strpos( (string) $post, '<!-- wp:' );
* Set defaults on activation.
public static function activate() {
register_uninstall_hook( __FILE__, array( __CLASS__, 'uninstall' ) );
add_network_option( null, 'classic-editor-replace', 'classic' );
add_network_option( null, 'classic-editor-allow-sites', 'disallow' );
add_option( 'classic-editor-replace', 'classic' );
add_option( 'classic-editor-allow-users', 'disallow' );
* Delete the options on uninstall.
public static function uninstall() {
delete_network_option( null, 'classic-editor-replace' );
delete_network_option( null, 'classic-editor-allow-sites' );
delete_option( 'classic-editor-replace' );
delete_option( 'classic-editor-allow-users' );
add_action( 'plugins_loaded', array( 'Classic_Editor', 'init_actions' ) );