Adding shortcode button to TinyMCE editor

Image displaying a window screen with a brick inside

Continuing on the TinyMCE post I made earlier, I want to show you how to add a button on the TinyMCE editor for your custom shortcode. The idea for this came when I created a team member shortcode for a client.

Notice: This article was written in 2016. WordPress has changed a lot since then, so take this article with a grain of salt…

They needed a small and simple shortcode that will show image, title, some content and a link to a page or to some outside content. This is a trivial thing to do, but later on I had to explain to him how to use it, and I realized that adding shortcode via shortcode tags

[shortcode-name link="" bla=""]Code language: JSON / JSON with Comments (json)

is not the most user-friendly way for a beginner to do. Or for non-beginner for that matter. With the rise of the page builders like Visual Composer, Divi builder, The Creator, and others, people got used to simple ‘just click’ way of doing things. I knew that I could add buttons to TinyMCE, and I remembered that I saw plugins adding their own buttons to the TinyMCE with modal windows, so I knew there’s got to be a way of achieving that.
There indeed was a way, and you can google a bunch of tutorials on how to add a button for shortcode. But I wanted to go a step further – add media from WordPress to your shortcode. You could even modify the code I’ll show you, to add links to pages (modifying the code from my first article here) and posts you made on WordPress. Endless possibilities indeed.

Paving the way

The first thing we’ll do is add our button in functions.php, include the necessary JavaScript file where our control will be located and add a localization – which is important if you want to use this in a plugin and want it to be translatable. The whole code is as follows

add_action( 'after_setup_theme', 'mytheme_theme_setup' );

if ( ! function_exists( 'mytheme_theme_setup' ) ) {
    function mytheme_theme_setup() {

        add_action( 'init', 'mytheme_buttons' );

    }
}

/********* TinyMCE Buttons ***********/
if ( ! function_exists( 'mytheme_buttons' ) ) {
    function mytheme_buttons() {
        if ( ! current_user_can( 'edit_posts' ) && ! current_user_can( 'edit_pages' ) ) {
            return;
        }

        if ( get_user_option( 'rich_editing' ) !== 'true' ) {
            return;
        }

        add_filter( 'mce_external_plugins', 'mytheme_add_buttons' );
        add_filter( 'mce_buttons', 'mytheme_register_buttons' );
    }
}

if ( ! function_exists( 'mytheme_add_buttons' ) ) {
    function mytheme_add_buttons( $plugin_array ) {
        $plugin_array['mybutton'] = get_template_directory_uri().'/js/tinymce_buttons.js';
        return $plugin_array;
    }
}

if ( ! function_exists( 'mytheme_register_buttons' ) ) {
    function mytheme_register_buttons( $buttons ) {
        array_push( $buttons, 'mybutton' );
        return $buttons;
    }
}

add_action ( 'after_wp_tiny_mce', 'mytheme_tinymce_extra_vars' );

if ( !function_exists( 'mytheme_tinymce_extra_vars' ) ) {
 function mytheme_tinymce_extra_vars() { ?>
 <script type="text/javascript">
 var tinyMCE_object = <?php echo json_encode(
 array(
 'button_name' => esc_html__('My button name', 'mythemeslug'),
 'button_title' => esc_html__('The title of the pop up box', 'mythemeslug'),
 'image_title' => esc_html__('Image', 'mythemeslug'),
 'image_button_title' => esc_html__('Upload image', 'mythemeslug'),
 )
 );
 ?>;
 </script><?php
 }
}Code language: PHP (php)

The first part is just adding our button (registering it with TinyMCE), the second part, with the function mytheme_tinymce_extra_vars(), is adding the object in which we’ll add our translatable strings that we can use in the tinymce_buttons.js file. Now you can skip this part out, but in that case, you will have names of your button, labels, etc. all fixed – not translatable. So I would go with this approach. We just created a PHP array, and encoded it to JSON, so that we can read it in our .js file later on.

Adding the button

The next thing we want to do is to add our button, that will open a modal window on click, with our options for our imaginary shortcode. On closing the modal the shortcode will be added to our content so that we won’t have to type anything manually. Remember, when adding a button, we need to keep the previously defined name the same – in our case, it’s ‘mybutton’. For every button you add, you add a separate code with that name. You can add image, like in the column button I showed you, but now we’ll just add a simple text with the button name. In tinymce_buttons.js file add

(function() {
    tinymce.PluginManager.add('mybutton', function( editor, url ) {
        editor.addButton( 'mybutton', {
            text: tinyMCE_object.button_name,
            icon: false,
            onclick: function() {
                editor.windowManager.open( {
                    title: tinyMCE_object.button_title,
                    body: [
                        {
                            type: 'textbox',
                            name: 'img',
                            label: tinyMCE_object.image_title,
                            value: '',
                            classes: 'my_input_image',
                        },
                        {
                            type: 'button',
                            name: 'my_upload_button',
                            label: '',
                            text: tinyMCE_object.image_button_title,
                            classes: 'my_upload_button',
                        },//new stuff!
                        {
                            type   : 'listbox',
                            name   : 'listbox',
                            label  : 'listbox',
                            values : [
                                { text: 'Test1', value: 'test1' },
                                { text: 'Test2', value: 'test2' },
                                { text: 'Test3', value: 'test3' }
                            ],
                            value : 'test2' // Sets the default
                        },
                        {
                            type   : 'combobox',
                            name   : 'combobox',
                            label  : 'combobox',
                            values : [
                                { text: 'Test', value: 'test' },
                                { text: 'Test2', value: 'test2' }
                            ]
                        },
                        {
                            type   : 'textbox',
                            name   : 'textbox',
                            label  : 'textbox',
                            tooltip: 'Some nice tooltip to use',
                            value  : 'default value'
                        },
                        {
                            type   : 'container',
                            name   : 'container',
                            label  : 'container',
                            html   : '<h1>container<h1> is <i>ANY</i> html i guess...<br/><br/><pre>but needs some styling?!?</pre>'
                        },
                        {
                            type   : 'tooltip',
                            name   : 'tooltip',
                            label  : 'tooltip ( you dont use it like this check textbox params )'
                        },
                        {
                            type   : 'button',
                            name   : 'button',
                            label  : 'button ( i dont know the other params )',
                            text   : 'My Button'
                        },
                        {
                            type   : 'buttongroup',
                            name   : 'buttongroup',
                            label  : 'buttongroup ( i dont know the other params )',
                            items  : [
                                { text: 'Button 1', value: 'button1' },
                                { text: 'Button 2', value: 'button2' }
                            ]
                        },
                        {
                            type   : 'checkbox',
                            name   : 'checkbox',
                            label  : 'checkbox ( it doesn`t seem to accept more than 1 )',
                            text   : 'My Checkbox',
                            checked : true
                        },
                        {
                            type   : 'colorbox',
                            name   : 'colorbox',
                            label  : 'colorbox ( i have no idea how it works )',
                            // text   : '#fff',
                            values : [
                                { text: 'White', value: '#fff' },
                                { text: 'Black', value: '#000' }
                            ]
                        },
                        {
                            type   : 'colorpicker',
                            name   : 'colorpicker',
                            label  : 'colorpicker'
                        },
                        {
                            type   : 'radio',
                            name   : 'radio',
                            label  : 'radio ( defaults to checkbox, or i`m missing something )',
                            text   : 'My Radio Button'
                        }
                    ],
                    onsubmit: function( e ) {
                        editor.insertContent( '[shortcode-name img="' + e.data.img + '" list="' + e.data.listbox + '" combo="' + e.data.combobox + '" text="' + e.data.textbox + '" check="' + e.data.checkbox + '" color="' + e.data.colorbox + '" color_2="' + e.data.colorpicker + '" radio="' + e.data.radio + '"]');
                    }
                });
            },
        });
    });

})();Code language: JavaScript (javascript)

I’ve put all the controls I could find that would be useful. We have a textbox for writing simple text or pasting image URL, as I’ve intended here. Button or button groups for multiple buttons. An important thing to notice here is that you can add a custom class to your field using classes property. This will add a class that you can use (you’ll see this just in a second). We also have color picker, combo box, radio buttons, etc.

On submit you’ll notice that in the editor you’ll insert a shortcode tag, with your options inserted like




e.data.nameCode language: CSS (css)

The final step you’ll need to add after this code is a simple opening of the wp.media uploader on button click. Since our modal appears when we click the ‘My button name’ button, we need to add the on click event to our document object

jQuery(document).ready(function($){
    $(document).on('click', '.mce-my_upload_button', upload_image_tinymce);

    function upload_image_tinymce(e) {
        e.preventDefault();
        var $input_field = $('.mce-my_input_image');
        var custom_uploader = wp.media.frames.file_frame = wp.media({
            title: 'Add Image',
            button: {
                text: 'Add Image'
            },
            multiple: false
        });
        custom_uploader.on('select', function() {
            var attachment = custom_uploader.state().get('selection').first().toJSON();
            $input_field.val(attachment.url);
        });
        custom_uploader.open();
    }
});Code language: JavaScript (javascript)

Here we used the fact that we added a custom class to our button – this way we can attach to that specific class, and run our media chooser. The custom class that you add will get a ‘mce-‘ prefix, be sure not to leave this out.

And that’s it. Once you’ve placed all this you’ll have something like the following

Add TinyMCE button for shortcode
Add TinyMCE button for shortcode, step by step

Hope this tutorial helped you guys. If you have any questions feel free to ask them in the comments below. Happy coding!

14 responses

  1. […] for you. All you need to do is pick one from the dropdown list of existing shortcodes (remember my article about it few weeks ago ;) […]

  2. Oliver Meyer Avatar
    Oliver Meyer

    Hi Denis,
    this article is from 2016. I guess we need to modify the code because it’s not working any longer (with WordPress 5.3.2). Any hint?

    Thank you,
    Oliver

    1. If you are referring to the new core editor, this won’t work with it. This is only for the classic editor.

  3. Thanks, Denis. Great tutorial :)

    1. Thanks :) Happy to help

  4. Amazing, thank you very much, you have saved my project, I never write comments but now I think I should say thank you, very big thank you dude, you really cool! Thanks a lot, best regards!
    P.S.
    WordPress 4.9.1
    TinyMCE Advanced 4.6.7
    All works perfect and wonderful and amazing!

    1. Thanks :) Happy that I could help ^^

  5. Alexander “Vitorials” Avatar
    Alexander “Vitorials”

    Yep, thanks, Denis. Great post.

  6. Abdul Ahad Khan Avatar
    Abdul Ahad Khan

    Good one! nice tutorial

    thanks

  7. King GeneraL Avatar
    King GeneraL

    coolstuff

  8. Mohsin Rafique Avatar
    Mohsin Rafique

    Hi,
    I have just read your tutorial and found out very helpful.

    I have a question here that you are using colorpicker option for color selection. Now Is there a way to append alpha color option for color picker in TinyMCE Editor while creating TinyMCE Shortcode builder?

    If so then do you mind to help me out in this regard.

    Thanks in Advance

    1. Hi!

      By the default IRIS color picker that comes bundled with the WordPress doesn’t have an alpha channel. And they don’t seem to be interested in expanding it. But there are instances that people made that you can check out here:
      https://github.com/Codestar/codestar-wp-color-picker
      https://github.com/23r9i0/wp-color-picker-alpha
      https://github.com/BraadMartin/components/tree/master/customizer/alpha-color-picker
      All you’d need to do is to enqueue their script and modify admin.js a bit to call it on button click. Shouldn’t be too hard :)

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.