Adding a TinyMCE button to Wordpress

[Tweet : ADN : nvALT]

I was working on a quick website for my wife, and trying to make things as simple as possible for both of us. For me, I just wanted to give her a flexible template that I didn’t have to hack much on (I went with Thesis), and for her, I wanted her to be able to start posting with a minimal learning curve. She just got a Flip, and wanted to start posting video. Since she was already posting to YouTube, I just wrote a quick shortcode in Thesis’ custom functions file that would take a YouTube id and turn it into a link that the Shadowbox plugin could pick up and make into a “lightboxed” video player. It worked great.

Then, I thought I’d just add a quick button to Wordpress’ TinyMCE editor to make adding the shortcode even easier. That turned out to be a real pain, at least the first time around, so I thought I’d detail how I ended up pulling it off. Read on for a step-by-step as I add a button to the built-in visual editor that will request an ID (or a full url) and insert a custom shortcode to play the video. You can see the end results here. I’m positive there are plugins that do just this, but I wanted to figure it out for myself…

Note that these instructions are for Wordpress 2.5+.

Want to just download the skeleton files and go for it?

First, the shortcode. Because the shadowbox plugin just needs a YouTube link to do it’s job, all the shortcode is going to do is take an id and turn it into a properly formatted link, inserting the necessary markup for shadowbox to do its job. In your functions.php (or custom/custom_functions.php if you’re using Thesis), add this code:

// add the shortcode handler for YouTube videos
function addYouTube($atts, $content = null) {
        extract(shortcode_atts(array( "id" => '' ), $atts));
        return '<p style="text-align:center"> \
        <a href="'.$id.'"> \
        <img src="'.$id.'/0.jpg" width="400" height="300" class="aligncenter" /> \
        <span>Watch the video</span></a></p>';
add_shortcode('youtube', 'addYouTube');

This lets you use a shortcode in your post that looks like [youtube id="xxxxx"] and have that turned into a centered paragraph containing a preview image and a link to the video. If you’re running the shadowbox plugin, or any plugin that detects YouTube links and handles them, clicking the link in your post will load the destination video in a hovering box, without leaving the page. Otherwise, it will go to the YouTube page for the video. The screenshot is pulled straight from YouTube, and (poorly) resized using HTML. I needed a 400x300 image to fit into the template, and since it wasn’t a huge change, it was easiest to just hardcode the size into the tag and let the browser handle it. For reference, you can get a YouTube-generated screenshot by inserting the video’s id into a string like this:[video id]/0.jpg. Use that inside of an image tag, as shown in the script, and you’ve got a preview image, ready to go.

Adding a button to the “quick tags” in the HTML editor is a cinch, but we’ll skip that since anyone using that editor can probably handle writing out a shortcode. It’s my wife and clients using the Visual editor that I want to save the trouble. So now, in your functions.php or custom_functions.php, we’ll first add the function that will insert the button:

function add_youtube_button() {
   if ( ! current_user_can('edit_posts') && ! current_user_can('edit_pages') )
   if ( get_user_option('rich_editing') == 'true') {
     add_filter('mce_external_plugins', 'add_youtube_tinymce_plugin');
     add_filter('mce_buttons', 'register_youtube_button');

add_action('init', 'add_youtube_button');

The first thing the above code does is check to make sure the current user has permission to edit, otherwise there’s no point in loading this code. Next, it checks that we’re in Rich Text (Visual) mode, and then adds filters that are executed at the time that the editor loads its plugins, and when it loads its buttons. Lastly, the add_action call tells it to run the function when we load the page.

The functions that it calls, add_youtube_tinymce_plugin and register_youtube_button are custom functions that we’ll create underneath it in the same file:

function register_youtube_button($buttons) {
   array_push($buttons, "|", "brettsyoutube");
   return $buttons;

function add_youtube_tinymce_plugin($plugin_array) {
   $plugin_array['brettsyoutube'] = get_bloginfo('template_url').'/custom/editor_plugin.js';
   return $plugin_array;

The first function tells the editor to add a divider and the button, in my case brettsyoutube, to the array of buttons. The second function tells it what that button’s going to do, which, in this case, is execute a statement found in an external Javascript file in the “custom” directory of my template.

The last thing we need to do before we get into the Javascript portion is to work a little magic on the TinyMCE version check. TinyMCE defaults to caching everything, so in order for our changes to show up, we have to trick it into thinking its version number has changed:

function my_refresh_mce($ver) {
  $ver += 3;
  return $ver;

add_filter( 'tiny_mce_version', 'my_refresh_mce');

This will intercept the version check and increment the current version number by 3. It’s the quick and dirty way to do it without messing with the settings directly, but if you’re curious, read more on the Wordpress Codex.

Now that we’ve got the necessary pieces in our functions file, it’s time to add the Javascript that the button’s going to need to function. I’ve located mine in the same folder as my functions file (custom/), but if you want to be tidier about it, make a subfolder. Just make sure that the add_youtube_tinymce_plugin function knows where to look for it. Here’s what’s in my editor_plugin.js file:

(function() {
	tinymce.create('tinymce.plugins.BrettsYouTube', {
		init : function(ed, url) {
			ed.addButton('brettsyoutube', {
				title : '',
				image : url+'/youtube.png',
				onclick : function() {
					idPattern = /(?:(?:[^v]+)+v.)?([^&=]{11})(?=&|$)/;
					var vidId = prompt("YouTube Video", "Enter the id or url for your video");
					var m = idPattern.exec(vidId);
					if (m != null && m != 'undefined')
						ed.execCommand('mceInsertContent', false, '[youtube id="'+m[1]+'"]');
		createControl : function(n, cm) {
			return null;
		getInfo : function() {
			return {
				longname : "Brett's YouTube Shortcode",
				author : 'Brett Terpstra',
				authorurl : '',
				infourl : '',
				version : "1.0"
	tinymce.PluginManager.add('brettsyoutube', tinymce.plugins.BrettsYouTube);

This function initializes the plugin, sets up what it’s going to accomplish, adds an array of info about the plugin, and then adds the plugin to the existing TinyMCE plugin list. The lines that contain brettsyoutube are referenced by the previous functions, so if you change the name of the plugin in one place, make sure to match it up in both the PHP and the Javascript portions. In the onclick handler inside the addButton call, you’ll find a function that prompts for a string (YouTube ID or URL), runs it against a regular expression to extract the ID (if necessary) and then inserts the shortcode as defined in our earlier functions. The last line of the main function does the actual adding of the plugin with the name brettsyoutube, so that it can be found by the editor when needed.

Note that the plugin references a youtube.png file in the same directory as the plugin. I lifted mine from Viper’s Video Quicktags, but you can use any 20px by 20px image, adjusting the name (and, if desired, location) in the above code appropriately.

Now I have a button in the visual editor that makes life that much easier for my wife. I can add more now, any time I like, and with a lot less effort than this first one turned out to be. I expect to be using this fairly frequently on client sites, as well.

TinyMCE Button Skeleton v1.0

A skeleton php and javascript file for adding a button to TinyMCE. This one inserts a YouTube button that adds custom shortcode to insert a YouTube video link with preview image. Includes a YouTube icon for the button.

Updated Sat Apr 17 2010.

More info…