How To Create a TinyMCE Editor Dialog Window in a WordPress Plugin

My last post generated a few inquiries.  It seems a demand exists for popup dialog boxes in WordPress’ TinyMCE editor.  This is pleasantly surprising since there is so little relevant information available.

I thought I was alone when I recently tackled this task in one of my plugins.  I struggled greatly with the poorly documented procedures.  Tears flowed.  Vulgarity spewed.  Fetal positions were entered.

Because of these queries, and so that I don’t forget how to do this myself, I’ve decided to teach how to create these dialog boxes.

The tutorial assumes you understand how WordPress works, how to make a WordPress plugin, and can at least read and understand CSS, HTML, and JavaScript.

 

Understanding the Basics

WordPress’ default editor is the TinyMCE editor.  TinyMCE is a “platform independent web based JavaScript ‘what you see is what you get (WYSIWYG)’ editor.”  TinyMCE is not a part of WordPress, but WordPress has integrated and customized TinyMCE to fit with their product.

Ultimately, for us plugin developers, this separation makes our job difficult.  We have to deal with a poorly defined cultural and programmatic paradigm shift between the two.  There is a wall separating WordPress and the editor that can only be passed through in specific and awkward ways.  And, the WordPress methods for manipulating the editor are just different enough from stock TinyMCE that TinyMCE’s documentation is often unhelpful.

 

To make matters worse, the programming language used to interact with WordPress is PHP, while TinyMCE’s is JavaScript.

Not only are these two separate languages with stark differences.  But they also run at different times in the loading process and don’t interact with each other directly.  PHP is executed server-side before anything is sent to the client’s web browser.  JavaScript is client-side and executed long after it is sent to the web browser.

This disconnect is inconvenient and occasionally infuriating.  Several portions of this tutorial will focus on breaking this wall and making WordPress and TinyMCE talk to each other.

 

What’s the Plan?

This post is going to teach you how to make a WordPress plugin.  This WordPress plugin is going to register a custom shortcode with WordPress, and create a TinyMCE toolbar button and dialog box for end users to create the shortcode.

This plugin is simple.  It will turn specified text red.  Pointless, I know, but easy to demonstrate and understand.

The complete, working and finished code can be browsed and downloaded on GitHub.  The code is heavily documented and, I hope, easy to follow when accompanied by this tutorial.

Browse Finished Plugin Source Code on GitHub

 

Start Your Plugin!

Let’s start a new WordPress plugin, create the main plugin file, and add the plugin file header.  Since I’m assuming you already know how to make basic WordPress plugins, let’s race through this procedure quickly.

The plugin is named TinyMCE Developer Starter Kit.  The plugin file is tinymce-dev-starter.php.  The plugin file header is as follows:

 

After the header, let’s define some constants. I always define constants like these in my plugins to smooth out some of WordPress’ confusing edges.

To keep in line with WordPress plugin best practices, everything I define in this plugin will be prefixed to prevent plugin naming collisions.  My prefix will be TDSK which is the acronym for the plugin name.

 

Now, register the dumb shortcode

 

Then enqueue jQuery, which we’ll use in the TinyMCE dialog box later.

 

 

Now We Start Worrying About TinyMCE

Everything we’ve done so far is typical in a WordPress plugin.  It’s been pretty straightforward, and the procedures are well documented online.

The remainder of this plugin, however, will be for TinyMCE.  Documentation is much harder to find, and things get stranger.  Here, I’m going to get more descriptive about what I’m doing.

 Step #1:  Register a TinyMCE Toolbar Button

There’s several things we’ll need to do to get a button on the toolbar.  Right now, we must tell WordPress to tell TinyMCE that we are going to define this button.  We’ll actually define it later, but we need to signal intent.

We signal our intent with WordPress filters.  WordPress has a handful of filters for the TinyMCE toolbar.  They are mce_buttonsmce_buttons_2mce_buttons_3, and mce_buttons_4.

Each of those filters corresponds to a row in the TinyMCE toolbar.  mce_buttons refers to the first/top row.  mce_buttons_2 refers to the second row.  mce_buttons_3 refers to the third row.  And mce_buttons_4 refers to the fourth row.  In WordPress, only two toolbars are used in the default editor, and the second row is hidden unless you toggle it with a button in the first row.

Since our plugin is supremely important, let’s add the button to the first row.  If you later want a different row, simply replace the filter with the one corresponding to your row.

 

We’re going to add the filter, and define a function to handle the filter’s task.  First, we add the filter.

Then we define the function to handle it.  The function is passed a numeric array of button IDs.  The purpose of this array is to tell TinyMCE which buttons to look for, and in what order to put them.  As with all WordPress filters, when finished altering what we’re given, we must return it.

My filter is going to add an entry to the end of this array of button IDs.  The simplest form of this is as follows.

 

However, it is common to want to restrict when this button appears in the toolbar.  For example, there are plugins available that present the TinyMCE editor on the public facing portion of your site.  You probably don’t want the public using your button.

In every case in which I’ve used a TinyMCE button, I’ve only wanted it available on post and page editors in the admin.  This plugin, is going to use those same constraints.  Simply modify the if statement below if your constraints are different.

 

Now, WordPress will inform TinyMCE to look for a button with an ID of tdsk_button, and add it to it’s first toolbar.  Later, we will define this button and tell TinyMCE what to do with it.

 

But, before we can do that, we need to tell TinyMCE where we are going to define this button.  We do this, by registering a TinyMCE “plugin.”

 

Step #2:  Register a TinyMCE Plugin That Will Define Our Button

The procedure for doing this is very similar to adding a button.  WordPress provides a filter.  The filter is mce_external_plugins.

 

This filter’s handler function is passed an associative array where each key is an ID of the TinyMCE plugin, and the value is the URL of the JavaScript file containing the plugin definition.  We must add an entry for the TinyMCE plugin we’re going to create in the next step.  Once the plugin is added to the array, return the array.  The simplest form is this:

However, just like with the button, it is probable that you will want to add some conditions so the plugin doesn’t get included with EVERY TinyMCE editor.  The conditions will be the same as the button’s.

 

Now, we can create the TinyMCE plugin.  WordPress is going to tell TinyMCE the new plugin’s ID is tdsk_plugin, and the plugin definition is in a file called tinymce-plugin.js in the root of our WordPress plugin’s directory.  So we need to create that file, and move into it for the next step.

 

Step 3:  Define our TinyMCE Plugin

Okay, we should now be in tinymce-plugin.js.  We are now completely outside of the realm of WordPress, and instead inside the realm of TinyMCE.  This means we switch to using JavaScript, and we hook into TinyMCE, not WordPress.

I’m going to start this JavaScript file by creating an immediately invoked function expression (IIFE).  If you don’t know what that is, I recommend learning about it.  Essentially, it is a method for preventing bad habits and difficult to trace bugs in situations like this.  In this IIFE, I’m going to need jQuery, so I need to pass it in.  I’ll discuss why jQuery is needed a bit later.

 

Now, all of our code goes inside the IIFE construct.  The code we’re going to put in there is our TinyMCE plugin definition.  I’m going to give you the entire plugin definition now.  It has comments explaining what’s happening, and I will break it down momentarily.

 

Okay, that was pretty ugly and confusing.  So let me break it down.  Let’s start here:

This is the opening line for creating a TinyMCE plugin.  The first parameter for the tinymce.create function is the plugin ID we specified earlier preceded by tinymce.plugins.  The second parameter is a JavaScript object with the meat of the plugin.

Now, onto the meat of the plugin.  The object can have a couple properties, but we are only interested in one.  The init property.  This property should contain a function which will be run when the TinyMCE plugin is initialized (when TinyMCE loads).  It is in here that we will define our button.

The function we’re attaching to init gets two parameters.  The first is the TinyMCE editor instance that is initializing the plugin.  The second is the absolute URL to the directory containing the plugin file.

 

Now, inside this init function, we want to define our button.  Fortunately, creating a button is simple.  We simply use the addButton function available in the editor instance.  This function is given two parameters.  The first is the ID of the button, which we defined earlier (tdsk_button).  The second is an object with the button’s properties.

Two of the properties are straightforward.  The title property is the text in the tooltip when you hover over the button.  The image property is the URL to the button image you want to display.  The third, less clear property is cmd.  cmd is the name of a TinyMCE “command” you want to execute when the button is clicked.  We are going to create our own command momentarily, so we just need to give it the name of our command.  I call mine tdsk_command.

 

 

Now, we get to create this command.  We do this by using the addCommand function in the editor instance.  This function has two parameters.  The first is the name of the command, which is tdsk_command.  The second is a function that is run when the command is executed.

 

So, let’s do a quick review before continuing.  We told WordPress to tell TinyMCE we would be defining a button.  We told WordPress to tell TinyMCE we would be defining a plugin to handle the button definition.  The button gets defined in our TinyMCE plugin.  The button, when clicked, executes a command we define.  This command runs the code in the function above.  It’s been a long chain of events, but we’re almost done dealing with TinyMCE (thank goodness).

What we need to do now is have the command open our dialog window.  We can open a dialog by using the open function in the editor instance’s windowManager.  This function has two parameters.  The first is an object with properties of the window.  The second is an object with any data we want available to the dialog.

The properties are fairly straightforward if you read the comments next to them.  For the data parameter, we must pass the dialog at least two things.  The first is the editor instance.  This is going to be used to put our shortcode in the editor.  The second is jQuery.

 

And one last thing with TinyMCE before we can move onto the dialog window’s contents.  After the plugin’s create function, we must tell TinyMCE we’re done with the plugin and it’s okay to use it.  We use the add function in TinyMCE’s PluginManager for this.  The first parameter of the add function is the plugin’s ID.  The second parameter is the plugin we created.  This second parameter is the same as the first parameter of the tinymce.create function, but without quotes.

 

And now we can move on!  Finally!

 

Dialog About Dialogs

In that mess of code just finished, we told TinyMCE that the dialog it creates should fill itself with the contents of tinymce-dialog.html in the root directory of the WordPress plugin.  So, that file needs to be created, and we’ll switch to it for the next portion of this tutorial.

This dialog content file should just contain a normal HTML page.  It can use JavaScript, CSS, images, and everything else a normal HTML page does.  For the purposes of this tutorial, the dialog is only going to display a form input in which the user types the text we want the shortcode to show.

We will then use JavaScript and jQuery to take that form data and turn it into a shortcode in the TinyMCE editor when submitted.

The HTML

First, let’s create a basic HTML page that we can build on.  The only strange item here will be a few <meta> tags that disables browser caching.  Because of the way TinyMCE creates the dialog, browser caching gets in the way more than it helps, especially when testing, so we disable it.

The rest is basic HTML.

 

Now, add a form with a label, text input, and a submit button in the body. Note that, if you had a lot of inputs, you would probably want to divide them into logical sections and give everything ID’s and classes for easier CSS and JavaScript targeting later.

 

The CSS

Personally, I find the default styling of everything created to be unappealing.  Therefore, let’s add some CSS to beautify what we have.  We can create a separate CSS file and link it like any other HTML page.  We can use inline CSS.  Or, we can add a <style> tag and include the CSS in the <head> of the page.

I’m going to add a <style> tag to the head.

 

The JavaScript

At the moment, clicking our submit button doesn’t do what we want.  We want a shortcode in the editor.  To get this, we must use JavaScript to extract form data, construct the shortcode, and tell TinyMCE to insert that shortcode into it’s contents.  jQuery makes this job much simpler and cross-browser compatible, so let’s use it.

Like with the CSS, JavaScript can be included in it’s own JavaScript file, or put on the page in a <script> tag.  Here, let’s do the latter.  Let’s put the <script> in the footer, just before the closing </body> tag.

 

But, before we can jump into the shortcode creation, we need to deal with two other TinyMCE oddities.  Passed arguments and using jQuery.  This was the topic of my previous post.  A summary of the post is below, but I recommend you check out the original for more detail.

 

The short version of my previous post is that TinyMCE opens our dialog in an iframe, and this iframe’s only connection to the rest of the WordPress site is the arguments passed to it in tinymce-plugin.js.

This means we need to get those arguments.  It also means that there’s no jQuery native to this dialog.  We have to load it, or somehow reference the jQuery from the main site in our dialog.

It’s bad form to reload a resource already loaded.  And, if you plan on submitting your plugin to the WordPress plugin repository, reloading jQuery will cause them to reject your plugin (trust me, I speak from experience).  So we need to do the latter and reference the main site’s jQuery.

 

Oddity #1:  Getting the Arguments

Passed arguments are accessed using the getParams() function in TinyMCE’s window manager.  Here’s how:

passed_arguments now points to the object passed in tinymce-plugin.js.  Specifically, we had an object with two properties.  The first property is editor, which is the TinyMCE editor instance.  The second property was jQuery.  Which leads us into the next oddity.

 

Oddity #2:  Referencing the Main Site’s jQuery

The only way to access the main site’s jQuery, that I know of, is to pass a reference to jQuery in the dialog’s arguments.  Fortunately, we’ve already done that.  It is, at this point, accessible using passed_arguments.jquery.  However, typing that repeatedly is going to be tedious, so let’s assign jQuery to the $ variable in the dialog.

 

Unfortunately, one of the downsides to this passing jQuery method is that jQuery is attached to the main site, not our dialog box.  When it searches for elements and makes manipulations to the DOM, it’s going to work on the main site, not our dialog.  To remedy this, we need to tell jQuery where we want it to operate.

This is accomplished by passing a 2nd parameter to jQuery selector functions when used.  This parameter will provide some context to jQuery. The parameter should be the <body> tag of the iframe.  Which, we can get using pure JavaScript.

 

Now, whenever we use a jQuery selector, we must also pass the jq_context.  Like this:

 

 

Now, Intercept Form Submission and Create the Shortcode

Now that we have our arguments and jQuery, we can work on the shortcode.  Our ultimate goal is to create a string containing the shortcode text we want and output the string in the TinyMCE editor.

I’m going to show all the code to do this below.  The code is heavily commented, and I will explain in detail further down.

 

The beginning of this is standard jQuery interaction.  We select the form, and attach a submit event handler.  In this event, we prevent the default behavior with event.preventDefault() so that we can do our own dirty work.

 

We then extract the value in the form’s text input and store it in a variable.

 

Then we begin constructing the shortcode string.  If the input_text isn’t empty, we add on the shortcode argument.  Then we close the shortcode.

 

Now, we have our shortcode stored in the shortcode variable.  We simply need to input it into the TinyMCE editor.  We do this with the following code:

 

And now, since we’re done with the dialog window, we close it.

 

Celebrate!

At this point, we have a working WordPress plugin with its own shortcode and TinyMCE dialog for creating the shortcode.  From this base framework, you should be able to expand to fit your needs.  I’ve outlined a likely addition some of you will need in your plugin below.

 

Addition #1:  Pass PHP Data to the Dialog

As I mentioned at the beginning of this lengthy tutorial, TinyMCE is completely and totally client-side JavaScript.  This makes it difficult to get data from other parts of your server-side, PHP plugin.  But sometimes we still need to do precisely that.

Imagine that your plugin includes a settings page that allows the user to alter the content of the dialog box.  The settings values can only be retrieved using PHP.  TinyMCE can’t access them.

 

To get around this problem, we have two options.  The first is to use AJAX.  But, using AJAX will typically be a bad idea.  It will slow the loading of your TinyMCE dialog box.  And asynchronous JavaScript will only make things more difficult.

A better option is to have PHP output data that you’ll need into a JavaScript variable when the page is loading.  This is easily done back in the tinymce-dev-starter.php file.  We simply tell WordPress to add your JavaScript to the admin head section.

In my example, I’m going to pass the output of the PHP function phpversion() to the dialog box because it is a simple demonstration of the technique. The method, however, should work with any PHP data.  I will simply add the following code at the bottom of the file.

 

With this setup, there is a global JavaScript variable named tdsk_data.  This variable contains an object which can hold as many properties as needed.  I only need the one property containing the PHP version.

Then, in our TinyMCE plugin file, we extract this data.

 

And now, we add it as an argument for the dialog box.

 

Then, in our dialog’s code in tinymce-dialog.html, we simply have another argument.  If I were to add a <p> tag above the form with an ID of tdsk_php_version, then I could output the PHP version with the following jQuery.

 

The End

Well, that was long.  For an example of what can be done with this method, check out my plugin Tweet This.  The plugin generates a fairly significant shortcode with several optional arguments, using data retrieved from a settings page, and created using a nicely styled dialog box.

gouda-example-0-1024x778

 

I also recommend you look at the source code of this sample plugin we’ve made and see everything in its completed state.  The entire plugin code can be browsed or downloaded on GitHub.  It is heavily commented and breaks down each step.  Thanks for reading, and show me what you make!

Browse Finished Plugin Source Code on GitHub

 

8 Comments

Agbonghama Collins

Hi, am trying to use jquery accordion in the plugin with hover intent but it seems not to be working.

Note: the accordion works, but the hover intent feature doesn’t. here is my modified overintent code.

Please tell me where am going wrong.

$(function () {
$(“#accordion”, jq_context).accordion( {
event: “click hoverintent”
});
});

/*
* hoverIntent | Copyright 2011 Brian Cherne
* http://cherne.net/brian/resources/jquery.hoverIntent.html
* modified by the jQuery UI team
*/
$.event.special.hoverintent = {
setup: function () {
$(this, jq_context).bind(“mouseover”, jQuery.event.special.hoverintent.handler);
},
teardown: function () {
$(this, jq_context).unbind(“mouseover”, jQuery.event.special.hoverintent.handler);
},
handler: function (event) {
var currentX, currentY, timeout,
args = arguments,
target = $(event.target, jq_context),
previousX = event.pageX,
previousY = event.pageY;

function track(event) {
currentX = event.pageX;
currentY = event.pageY;
};
function clear() {
target
.unbind(“mousemove”, track)
.unbind(“mouseout”, clear);
clearTimeout(timeout);
}

function handler() {
var prop,
orig = event;
if (( Math.abs(previousX – currentX) +
Math.abs(previousY – currentY) ) < 7) {
clear();
event = $.Event("hoverintent");
for (prop in orig) {
if (!( prop in event )) {
event[prop] = orig[prop];
}
}
// Prevent accessing the original event since the new event
// is fired asynchronously and the old event is no longer
// usable (#6028)
delete event.originalEvent;
target.trigger(event);
} else {
previousX = currentX;
previousY = currentY;
timeout = setTimeout(handler, 100);
}
}

timeout = setTimeout(handler, 100);
target.bind({
mousemove: track,
mouseout: clear
});
}
};

Reply
John Morris

The issue is that the hoverIntent isn’t being loaded properly inside the dialog box. It needs to be loaded outside of the dialog box.

Leave your accordion call, but remove the hoverIntent code from your dialog box (everything underneath the comment). Then, put the following code, which is a slightly modified version of the hoverIntent code, in its own JavaScript file. Then in the main plugin file, have WordPress load the JavaScript file in the admin using ‘wp_enqueue_script‘ in the ‘admin_enqueue_scripts‘ hook handler function (‘tdsk_enqueue_admin_scripts()‘ in the sample plugin), then it should work.

Modified HoverIntent Code

Enqueue the New HoverIntent JavaScript File

Reply
Ben Lowbridge

Hi,

This works almost perfectly for me. It breaks when I add in the add_action to admin_head. Is there a way around this?

Thanks.

Reply
Annie

Hi!

This works great, but i got one question: is there a way to insert it directly to the theme? we’re developing for a site where users can’t activate plugins, so it would be great if we could have this functionality directly from the theme.

Thanks!

Reply
Scott

Just tripped across this while looking for a way to add a bootstrap button creator. Very useful!

Even though it is written for those already familiar with WordPress and plugin development, it’d be good to end it with a note reminding readers to activate their new plugin 😉 Spent a little longer than I should’ve had to looking for errors in my implementation …

Reply

Leave a Reply