: str_replace(): Passing null to parameter #2 ($replace) of type array|string is deprecated in
foreach ( $mce_external_plugins as $name => $url ) {
if ( in_array( $name, $plugins, true ) ) {
unset( $mce_external_plugins[ $name ] );
$url = set_url_scheme( $url );
$mce_external_plugins[ $name ] = $url;
$plugurl = dirname( $url );
// Try to load langs/[locale].js and langs/[locale]_dlg.js.
if ( ! in_array( $name, $loaded_langs, true ) ) {
$path = str_replace( content_url(), '', $plugurl );
$path = realpath( WP_CONTENT_DIR . $path . '/langs/' );
$path = trailingslashit( $path );
if ( @is_file( $path . $mce_locale . '.js' ) ) {
$strings .= @file_get_contents( $path . $mce_locale . '.js' ) . "\n";
if ( @is_file( $path . $mce_locale . '_dlg.js' ) ) {
$strings .= @file_get_contents( $path . $mce_locale . '_dlg.js' ) . "\n";
if ( 'en' !== $mce_locale && empty( $strings ) ) {
if ( @is_file( $path . 'en.js' ) ) {
$str1 = @file_get_contents( $path . 'en.js' );
$strings .= preg_replace( '/([\'"])en\./', '$1' . $mce_locale . '.', $str1, 1 ) . "\n";
if ( @is_file( $path . 'en_dlg.js' ) ) {
$str2 = @file_get_contents( $path . 'en_dlg.js' );
$strings .= preg_replace( '/([\'"])en\./', '$1' . $mce_locale . '.', $str2, 1 ) . "\n";
if ( ! empty( $strings ) ) {
$ext_plugins .= "\n" . $strings . "\n";
$ext_plugins .= 'tinyMCEPreInit.load_ext("' . $plugurl . '", "' . $mce_locale . '");' . "\n";
self::$plugins = $plugins;
self::$ext_plugins = $ext_plugins;
$settings = self::default_settings();
$settings['plugins'] = implode( ',', $plugins );
if ( ! empty( $mce_external_plugins ) ) {
$settings['external_plugins'] = wp_json_encode( $mce_external_plugins );
/** This filter is documented in wp-admin/includes/media.php */
if ( apply_filters( 'disable_captions', '' ) ) {
$settings['wpeditimage_disable_captions'] = true;
$mce_css = $settings['content_css'];
* The `editor-style.css` added by the theme is generally intended for the editor instance on the Edit Post screen.
* Plugins that use wp_editor() on the front-end can decide whether to add the theme stylesheet
* by using `get_editor_stylesheets()` and the `mce_css` or `tiny_mce_before_init` filters, see below.
$editor_styles = get_editor_stylesheets();
if ( ! empty( $editor_styles ) ) {
// Force urlencoding of commas.
foreach ( $editor_styles as $key => $url ) {
if ( str_contains( $url, ',' ) ) {
$editor_styles[ $key ] = str_replace( ',', '%2C', $url );
$mce_css .= ',' . implode( ',', $editor_styles );
* Filters the comma-delimited list of stylesheets to load in TinyMCE.
* @param string $stylesheets Comma-delimited list of stylesheets.
$mce_css = trim( apply_filters( 'mce_css', $mce_css ), ' ,' );
if ( ! empty( $mce_css ) ) {
$settings['content_css'] = $mce_css;
unset( $settings['content_css'] );
self::$first_init = $settings;
* Filters the list of teenyMCE buttons (Text tab).
* @since 3.3.0 The `$editor_id` parameter was added.
* @param array $mce_buttons An array of teenyMCE buttons.
* @param string $editor_id Unique editor identifier, e.g. 'content'.
$mce_buttons = apply_filters( 'teeny_mce_buttons', $mce_buttons, $editor_id );
$mce_buttons_2 = array();
$mce_buttons_3 = array();
$mce_buttons_4 = array();
if ( ! wp_is_mobile() ) {
if ( $set['_content_editor_dfw'] ) {
$mce_buttons[] = 'wp_adv';
$mce_buttons[] = 'fullscreen';
$mce_buttons[] = 'wp_adv';
$mce_buttons[] = 'wp_adv';
* Filters the first-row list of TinyMCE buttons (Visual tab).
* @since 3.3.0 The `$editor_id` parameter was added.
* @param array $mce_buttons First-row list of buttons.
* @param string $editor_id Unique editor identifier, e.g. 'content'. Accepts 'classic-block'
* when called from block editor's Classic block.
$mce_buttons = apply_filters( 'mce_buttons', $mce_buttons, $editor_id );
if ( ! wp_is_mobile() ) {
$mce_buttons_2[] = 'wp_help';
* Filters the second-row list of TinyMCE buttons (Visual tab).
* @since 3.3.0 The `$editor_id` parameter was added.
* @param array $mce_buttons_2 Second-row list of buttons.
* @param string $editor_id Unique editor identifier, e.g. 'content'. Accepts 'classic-block'
* when called from block editor's Classic block.
$mce_buttons_2 = apply_filters( 'mce_buttons_2', $mce_buttons_2, $editor_id );
* Filters the third-row list of TinyMCE buttons (Visual tab).
* @since 3.3.0 The `$editor_id` parameter was added.
* @param array $mce_buttons_3 Third-row list of buttons.
* @param string $editor_id Unique editor identifier, e.g. 'content'. Accepts 'classic-block'
* when called from block editor's Classic block.
$mce_buttons_3 = apply_filters( 'mce_buttons_3', array(), $editor_id );
* Filters the fourth-row list of TinyMCE buttons (Visual tab).
* @since 3.3.0 The `$editor_id` parameter was added.
* @param array $mce_buttons_4 Fourth-row list of buttons.
* @param string $editor_id Unique editor identifier, e.g. 'content'. Accepts 'classic-block'
* when called from block editor's Classic block.
$mce_buttons_4 = apply_filters( 'mce_buttons_4', array(), $editor_id );
$body_class = $editor_id;
$body_class .= ' post-type-' . sanitize_html_class( $post->post_type ) . ' post-status-' . sanitize_html_class( $post->post_status );
if ( post_type_supports( $post->post_type, 'post-formats' ) ) {
$post_format = get_post_format( $post );
if ( $post_format && ! is_wp_error( $post_format ) ) {
$body_class .= ' post-format-' . sanitize_html_class( $post_format );
$body_class .= ' post-format-standard';
$page_template = get_page_template_slug( $post );
if ( false !== $page_template ) {
$page_template = empty( $page_template ) ? 'default' : str_replace( '.', '-', basename( $page_template, '.php' ) );
$body_class .= ' page-template-' . sanitize_html_class( $page_template );
$body_class .= ' locale-' . sanitize_html_class( strtolower( str_replace( '_', '-', get_user_locale() ) ) );
if ( ! empty( $set['tinymce']['body_class'] ) ) {
$body_class .= ' ' . $set['tinymce']['body_class'];
unset( $set['tinymce']['body_class'] );
'selector' => "#$editor_id",
'wpautop' => (bool) $set['wpautop'],
'indent' => ! $set['wpautop'],
'toolbar1' => implode( ',', $mce_buttons ),
'toolbar2' => implode( ',', $mce_buttons_2 ),
'toolbar3' => implode( ',', $mce_buttons_3 ),
'toolbar4' => implode( ',', $mce_buttons_4 ),
'tabfocus_elements' => $set['tabfocus_elements'],
'body_class' => $body_class,
// Merge with the first part of the init array.
$mce_init = array_merge( self::$first_init, $mce_init );
if ( is_array( $set['tinymce'] ) ) {
$mce_init = array_merge( $mce_init, $set['tinymce'] );
* For people who really REALLY know what they're doing with TinyMCE
* You can modify $mceInit to add, remove, change elements of the config
* before tinyMCE.init. Setting "valid_elements", "invalid_elements"
* and "extended_valid_elements" can be done through this filter. Best
* is to use the default cleanup by not specifying valid_elements,
* as TinyMCE checks against the full set of HTML 5.0 elements and attributes.
* Filters the teenyMCE config before init.
* @since 3.3.0 The `$editor_id` parameter was added.
* @param array $mce_init An array with teenyMCE config.
* @param string $editor_id Unique editor identifier, e.g. 'content'.
$mce_init = apply_filters( 'teeny_mce_before_init', $mce_init, $editor_id );
* Filters the TinyMCE config before init.
* @since 3.3.0 The `$editor_id` parameter was added.
* @param array $mce_init An array with TinyMCE config.
* @param string $editor_id Unique editor identifier, e.g. 'content'. Accepts 'classic-block'
* when called from block editor's Classic block.
$mce_init = apply_filters( 'tiny_mce_before_init', $mce_init, $editor_id );
if ( empty( $mce_init['toolbar3'] ) && ! empty( $mce_init['toolbar4'] ) ) {
$mce_init['toolbar3'] = $mce_init['toolbar4'];
$mce_init['toolbar4'] = '';
self::$mce_settings[ $editor_id ] = $mce_init;
} // End if self::$this_tinymce.
private static function _parse_init( $init ) {
foreach ( $init as $key => $value ) {
if ( is_bool( $value ) ) {
$val = $value ? 'true' : 'false';
$options .= $key . ':' . $val . ',';
} elseif ( ! empty( $value ) && is_string( $value ) && (
( '{' === $value[0] && '}' === $value[ strlen( $value ) - 1 ] ) ||
( '[' === $value[0] && ']' === $value[ strlen( $value ) - 1 ] ) ||
preg_match( '/^\(?function ?\(/', $value ) ) ) {
$options .= $key . ':' . $value . ',';
$options .= $key . ':"' . $value . '",';
return '{' . trim( $options, ' ,' ) . '}';
* @param bool $default_scripts Optional. Whether default scripts should be enqueued. Default false.
public static function enqueue_scripts( $default_scripts = false ) {
if ( $default_scripts || self::$has_tinymce ) {
wp_enqueue_script( 'editor' );
if ( $default_scripts || self::$has_quicktags ) {
wp_enqueue_script( 'quicktags' );
wp_enqueue_style( 'buttons' );
if ( $default_scripts || in_array( 'wplink', self::$plugins, true ) || in_array( 'link', self::$qt_buttons, true ) ) {
wp_enqueue_script( 'wplink' );
wp_enqueue_script( 'jquery-ui-autocomplete' );
if ( self::$has_medialib ) {
wp_enqueue_script( 'media-upload' );
wp_enqueue_script( 'wp-embed' );
} elseif ( $default_scripts ) {
wp_enqueue_script( 'media-upload' );
* Fires when scripts and styles are enqueued for the editor.
* @param array $to_load An array containing boolean values whether TinyMCE
* and Quicktags are being loaded.
'tinymce' => ( $default_scripts || self::$has_tinymce ),
'quicktags' => ( $default_scripts || self::$has_quicktags ),
* Enqueue all editor scripts.
* For use when the editor is going to be initialized after page load.
public static function enqueue_default_editor() {
// We are past the point where scripts can be enqueued properly.
if ( did_action( 'wp_enqueue_editor' ) ) {
self::enqueue_scripts( true );
// Also add wp-includes/css/editor.css.
wp_enqueue_style( 'editor-buttons' );
add_action( 'admin_print_footer_scripts', array( __CLASS__, 'force_uncompressed_tinymce' ), 1 );
add_action( 'admin_print_footer_scripts', array( __CLASS__, 'print_default_editor_scripts' ), 45 );
add_action( 'wp_print_footer_scripts', array( __CLASS__, 'force_uncompressed_tinymce' ), 1 );
add_action( 'wp_print_footer_scripts', array( __CLASS__, 'print_default_editor_scripts' ), 45 );
* Print (output) all editor scripts and default settings.
* For use when the editor is going to be initialized after page load.
public static function print_default_editor_scripts() {
$user_can_richedit = user_can_richedit();
if ( $user_can_richedit ) {
$settings = self::default_settings();
$settings['toolbar1'] = 'bold,italic,bullist,numlist,link';
$settings['wpautop'] = false;
$settings['indent'] = true;
$settings['elementpath'] = false;
$settings['directionality'] = 'rtl';
* In production all plugins are loaded (they are in wp-editor.js.gz).
* The 'wpview', 'wpdialogs', and 'media' TinyMCE plugins are not initialized by default.
* Can be added from js by using the 'wp-before-tinymce-init' event.
$settings['plugins'] = implode(
$settings = self::_parse_init( $settings );
<script type="text/javascript">
window.wp = window.wp || {};
window.wp.editor = window.wp.editor || {};
window.wp.editor.getDefaultSettings = function() {
tinymce: <?php echo $settings; ?>,
buttons: 'strong,em,link,ul,ol,li,code'
if ( $user_can_richedit ) {
$suffix = SCRIPT_DEBUG ? '' : '.min';
$baseurl = self::get_baseurl();
baseURL: "<?php echo $baseurl; ?>",
suffix: "<?php echo $suffix; ?>",