How to modify VisualComposer’s TinyMCE editor only for a specific shortcode

There are a number of problems:

  1. The tiny_mce_before_init filter does not affect the TinyMCE editor that VC instantiates for parameters with name == 'content' and type == 'content_html'. VC gets the TinyMCE HTML markup in a separate AJAX request. The editor’s instantiation does not benefit from the tinyMCEPreInit inline JavaScript array that the filter affects. The solution is to introduce a TinyMCE plugin that modifies the editor’s settings on the client side.
  2. Any TinyMCE plugin must be loaded after the editor itself (see here). Even though according to the wp_enqueue_script() documentation you would expect to be able to enqueue your plugin by setting a dependency to the built-in tiny_mce script, this does not work because TinyMCE is loaded differently. See here for a discussion of the problem and a solution.

I first bind to tiny_mce_before_init, but not to modify the buttons. I only add a plugin which all editors will run.

add_filter( 'tiny_mce_before_init', array( &$this, 'filter_tiny_mce_before_init' ) );

    public function filter_tiny_mce_before_init( $mceInit ) {
        $mceInit['plugins'] .= ',myplugin';
        return $mceInit;
    }

Then I created the following short TinyMCE plugin. It first checks to see if the current TinyMCE editor is a Visual Composer editor, and then uses jQuery to determine if the shortcode to which it corresponds starts with my plugin’s name.

'use strict';
tinymce.PluginManager
            .add( 'myplugin',
                function(editor, url) {

                    // skip editors that don't belong to VC
                    if ( editor.id != 'wpb_tinymce_content' )
                        return;

                    // skip editors that don't belong to my shortcode
                    if ( !jQuery( '#wpb_tinymce_content' ).parents(
                        '[data-vc-shortcode^="myplugin"]' ).length )
                        return;

                    // these are the only buttons I want
                    editor.settings.toolbar1 = 'bold,italic,underline,strikethrough,link,unlink,spellchecker,wp_fullscreen';
                    editor.settings.toolbar2 = 'pastetext,removeformat,charmap,undo,redo,wp_help';

                } );

Finally I make sure this plugin is loaded after TinyMCE. It is ugly but again see here for why this is done.

add_action( 'after_wp_tiny_mce', array( &$this, 'action_after_wp_tiny_mce' ) );

public function action_after_wp_tiny_mce() {
    printf( 
        '<script type="text/javascript" src="https://wordpress.stackexchange.com/questions/220409/%s"></script>', 
        plugins_url( 'assets/scripts/myplugin-tinymce.js', __FILE__ ) );
}

It works 🙂