Monthly Archives: February 2007

Adding Quicktags to WordPress and to the TinyMCE Editor for WP Plugin Authors in Many Easy Steps

A while back I wrote a WordPress plugin that adds popup images to the blog. The way the plugin works is that you add in fake HTML-ish <popim> tags and the plugin filters those and changes them to the actual HTML required to create the popup image.

That wasn’t the hard part. The hard part was when I decided to add a UI that automatically generated thumbnails for the plugin. Moreover, I decided to add a button to the WordPress editor as part of that UI. Surely that would be no problem. After all, there should be documentation for adding editor buttons, right?

Ha! I say to you, and ha! again. Clearly you are new to computers.

It took a lot of digging for me to figure out what all I needed to do. I decided to collate the information I found and present it here for other WordPress plugin authors. If you’re in a real hurry, at the end of the article you can grab files with all of the code that is shown in this article.

What the Hey Are You Talking About?

Open up your WordPress admin panel and go to the Write tab. See the part where you write your post? That’s the editor. WordPress has two different editors. One is the visual rich editor, which is a modified version of the javascript-based TinyMCE editor. It hides all of that nasty HTML markup from you and formats your posts nicely. The other is the regular editor, which shows you all of the HTML markup you could ever want and is the editor that real blog writers use. To switch between the two, go to the Users tab and look at the bottom of your profile, in the Personal Options section. The checkbox there toggles whether or not you use the visual rich editor.

If you’re using the regular editor, above the edit panel is a row of buttons. Those are called the Quicktags. They’re a fast way to add HTML tags to your post, similar in spirit to the tag buttons a lot of bulletin board systems have. The TinyMCE editor has a row of pretty graphical buttons that do similar things.

Before We Begin, a Brief Note on Coding Standards

If you’re writing a plugin and you’re not encapsulating your code in an object, you should preface your function names and global variables with a prefix that’s specific to your plugin. That makes it less likely that your function names will be the same as those of another plugin or WordPress itself, since PHP doesn’t have proper namespace support. In this post, I’ll use the prefix “pluggy”.

Adding a Quicktag

Adding a button to the Quicktag bar requires a bit of javascript. What we’re going to do is add some code to the bottom of every page that contains the editor. The javascript code will create the button element and append it to the Quicktags.

First of all, let’s write a function that will (eventually) add our specialized javascript to all editor pages. In your plugin’s .php file, add the following:


// Add javascript to generate a quicktag button. If the quicktag bar
// isn't available, instead put a link below the posting field entry.
function pluggy_quicktag_like_button() {
  // Only add the javascript to post.php, post-new.php, page-new.php, or
  // bookmarklet.php pages
  if (strpos($_SERVER['REQUEST_URI'], 'post.php') ||
      strpos($_SERVER['REQUEST_URI'], 'post-new.php') ||
      strpos($_SERVER['REQUEST_URI'], 'page-new.php') ||
      strpos($_SERVER['REQUEST_URI'], 'bookmarklet.php')) {
    // Print out the HTML/Javascript to add the button
?>


Now we need to add the javascript to create our button. Quicktags' javascript code come from the file wp-includes/js/quicktags.js. If you look in that file, you'll discover that the <div> element that holds the Quicktags has an HTML ID of "ed_toolbar" and that each button has a class of "ed_button". So we need some javascript to create a button of class "ed_button" and append it to the "ed_toolbar" div. This next bit of code replaces the "JAVASCRIPT WILL GO HERE" comment above.


var pluggy_toolbar = document.getElementById("ed_toolbar");

if (pluggy_toolbar) {
  var theButton = document.createElement('input');
  theButton.type = 'button';
  theButton.value = 'Pluggy';
  theButton.onclick = pluggy_button;
  theButton.className = 'ed_button';
  theButton.title = 'Pluggy!';
  theButton.id = 'ed_Pluggy';
  pluggy_toolbar.appendChild(theButton);
}

If you add all of that code to your plugin and then visit the Write Post page, you'll see a button called "Pluggy!", assuming that you're using the regular editor. Success! Except that the button doesn't do anything yet. Thanks to the onclick value it wants to call the function pluggy_button(), but that doesn't exist. We need to write that function. Add this snippet of javascript after the above bit:


function pluggy_button(querystr) {
  alert("Pluggy button pressed!");
  return false;
}

and that will make the button pop up an alertbox when you push it. If this were a more complicated plugin, we could use the pluggy_button() javascript function to pop open a new window or insert code into the editor window. I'll leave those as an exercise for the reader, though you can see an example of popping open a UI window in my Simple Popup Images plugin.

Adding a Button to the Visual Rich Editor

The visual rich editor has pretty graphical buttons for you to push. You really don't want to add a new one of those.

I mean it. For full functionality, you'll have to write a TinyMCE plugin, and that's a process that's even more sketchily documented than that of writing WP plugins, plus you have to write your plugin in javascript.

If you're determined to write a TinyMCE plugin, all I can do is point you to the TinyMCE documentation; mention the tinymce_before_init, tinymce_plugins, and tinymce_buttons filters that WordPress has made available; show you other TinyMCE plugins, and wish you the best.

There are other, easier options than creating a graphical button and writing a new plugin. WordPress 2.1 introduced the Code tab in the visual rich editor. If you open the Code tab, you'll see the usual array of Quicktags...including our new one. But what if you want to support the WP 2.0 branch? You can't easily add a button, but you can add a nice link just below the editor that will do the same thing as the button. We'll add an invisible <div> to every post page and, if the Quicktags aren't available, make that div visible and move it into position.

Above the <script type="text/javascript"> in your code, add the following:



(Note that, for code-posting reasons, I have changed the <a> in the code snippet to [a]. Change the []s to angle brackets for it to work.) After the "if {}" block of javascript code that creates the Quicktag button, add:


else {
  var pluggyLink = document.getElementById("pluggy_link");
  var pingBack = document.getElementById("pingback");
  if (pingBack == null)
    var pingBack = document.getElementById("post_pingback");
  if (pingBack == null) {
    var pingBack = document.getElementById("savepage");
    pingBack = pingBack.parentNode;
  }
  pingBack.parentNode.insertBefore(pluggyLink, pingBack);
  pluggyLink.style.display = 'block';
}

That bit of javascript makes the <div> with our link visible and moves it to just before the secret hidden pingback input elements that come right after the editor. Tah-dah!

Bonus Topic 1: Inserting Text in the Editor

Your plugin, like mine, may pop up a window, do some processing, and return, inserting some text into the editor window. You can do that with two javascript functions:


// Insert myValue into an editor window
function insertHtml(myValue) {
        if(window.tinyMCE)
                window.opener.tinyMCE.execCommand("mceInsertContent",true,myVal\
ue);
        else
                insertAtCursor(window.opener.document.post.content, myValue);
        window.close();
}

// Insert text into the WP regular editor window
function insertAtCursor(myField, myValue) {
        //IE support
        if (document.selection && !window.opera) {
                myField.focus();
                sel = window.opener.document.selection.createRange();
                sel.text = myValue;
        }
        //MOZILLA/NETSCAPE/OPERA support
        else if (myField.selectionStart || myField.selectionStart == '0') {
                var startPos = myField.selectionStart;
                var endPos = myField.selectionEnd;
                myField.value = myField.value.substring(0, startPos)
                + myValue
                + myField.value.substring(endPos, myField.value.length);
        } else {
                myField.value += myValue;
        }
}

Calling insertHtml('some text') will paste that text in the editor window.

Bonus Topic 2: Adding HTML-Like Tags to the Visual Rich Editor

For your plugin, you may end up inventing an HTML-like tag to be filtered later. For example, the WP-FLV plugin adds an <flv> tag that, when a post is displayed, is converted to a flash-embedded object.

TinyMCE won't like that. It strips out tags it doesn't know anything about. To make TinyMCE like your new tag, you need to add it to TinyMCE's list of valid elements. WordPress gives you a filter for doing that: mce_valid_elements. Define your tag using the TinyMCE format for valid elements and then add it to the valid elements list using the mce_valid_elements filter.

You know what? Let me show you an example. Let's say you've defined a new <pluggy> tag for your plugin. It can have two attributes called "width" and "height". To get the visual rich editor to leave your tag alone, do the following:


add_filter('mce_valid_elements', 'pluggy_mce_valid_elements', 0);

function pluggy_mce_valid_elements($valid_elements) {
  $valid_elements .= 'pluggy[width|height]';
  return $valid_elements;
}

The "pluggy[width|height]" is the TinyMCE valid element string. Now TinyMCE won't get rid of any <pluggy> tags when your users are writing their posts.

One other note. When I used this trick, I used a single self-closing tag, like <pluggy />. TinyMCE automatically turns this into <pluggy></pluggy>, and there's nothing you can do short of hacking the editor's javascript. Just a word of advice.

Conclusion and Downloads

That's it. I hope you found this article helpful. If you'd rather not type in all of the code above, you can get two files that have the code already entered for you.

Kindermusik, Will You Hire Me?

I’m a bit overwhelmed this evening.

Also, I’m feeling the love.

My sister-in-law, Joy, posted about me on some Kindermusik e-group thing-a-ma-bob and suddenly I’m the Kindermusik business card lady. I have spent all evening fielding requests for work. I have never, ever had to field requests for work. Usually it’s me asking around forlornly if I might spread a little graphic design magic over someone’s sadly neglected paper products. And usually, people don’t want to pay for said magic.

I’m actually considering making a website for Granade Graphic Design. If I’m not careful, I might actually have to do some work around here. Quick, can someone show me some HTML?

Welcome Kindermusik Educators! Yes, I’d be happy to create some nifty new business cards personalized just for your studio. Send me an email (you can find it on the About Us link on the left) and let me know what you are looking for.

The Potty’s the Place!

Today is the first of three days dedicated almost exclusively to Eli learning to use the potty. We have three books on the bathroom can-do spirit and I’ve read them what feels like 45 times today. We have moved a tall foot stool in so we have a place to sit as Eli sits on the toilet. We’ve spent a lot of quality time in the bathroom today.

I wish I could say that we’ve had success today but the only thing we’ve done is teach Eli how to pull his pants up and down. I guess that’s something at least. I would like for him to have actually hit the potty at least once by the end of our three days. I’ll be sure to let everyone knows when the big event occurs.

Friday Night Videos: Videos With Words In

Alex Gopher: The Child (1999)

Everything in this video is words, and I do mean everything. Half of the fun is reading the words used; the other half is enjoying the fonts that Antoine Bardou-Jacquet used for the words. Prepare to hit the pause button early and often.

The SoftLightes: Heart Made of Sound (2006)

Instead of using typography to form words from the song’s lyrics, Kris Moyes used stop-motion animation of everyday objects. He also throws in the occasional representation of musical elements, like the wonderful crumpled-ball snare hits. I find this video relaxing to watch despite its fast transitions.

To Eli On His Third Birthday

And now you are three. It seems like you have one of these birthdays every year or so. This year’s celebration was more low-key. Grandparents are visiting on several weekends, keeping your birthday alive for weeks. Mom took cupcakes and bags of small toys for your daycare’s class, and everyone got to mainline sugar and play tiny plastic whistles. And then one of your classmates vomited, so everyone had a great time!

You’ve gotten a lot of toys, but the ones that thrill you the most are small plastic animals. When we went to Phoenix a few weeks ago, Mom bought you a pile of plastic frogs and turtles. On the plane to Phoenix we doled them out one and two at a time. You were entranced. My favorite moment was when I palmed a frog, closed your tray, and opened it again, dropping the palmed frog on the tray. “A frog!” you trilled. Then you closed and opened your tray several times in the hopes that more frogs would fall out. The entire weekend you moved your frogs and turtles around, making them jump and fly and leap.

Based on that reaction, we got you a bunch of small plastic dinosaurs and fish. You carry them around in bunches, occasionally dividing them into good ones and not-funny ones. You assign us dinosaurs or fish depending on your whims, and you trade them out as you see fit. Often they have to watch us play games or eat.

Every year I’ve talked about your communication and how you were learning to talk to us. After this year I can stop, because you have well and truly mastered the skill. You talk almost non-stop from the time you get up until you go to bed. When we moved you from your crib to your toddler bed, we put a toddler handle on the inside of your door so you couldn’t come out until we were ready to let you out. Eventually we relented, so now the first thing I hear on most mornings is, “Mommy! Daddy! Good morning!” The second thing I hear is Mom telling you, “Did you look at the clock by your bed? Is it past 7 yet?”

Along with talking, you’ve developed opinions that you declaim forcefully enough to give Ann Coulter pause. You tell us what to do. You tell us how to do it. The funniest are when we’ll be talking at the table and you’ll tell us, “Stop it. Stop it. Stop it!” When we ask you what we need to stop, you tell us, “Stop having conversation.” At times I give into temptation. I’ll sing and you’ll say, “Stop singing!” So I’ll stop for a few seconds before starting up the song again. “No, daddy, no! Stop it! Stop singing!” And I will stop…for another few seconds. Think of it as advance payments for your teenage years.

Your brain is a huge sponge that soaks up information, runs it through a Markov process, and spits it back out in the form of words. Earlier this week you had a fish and I had a dinosaur. You walked out of your room and came back in. “You and stegosaurus have to go find Nemo fish. Nemo fish is in Seattle and he is in an aquarium. He’s stuck in the mud. Help!” I’m pretty sure that’s a Little Einsteins/One Duck Stuck mash-up with a bit of Finding Nemo thrown in for good measure.

You’ve also become a ball of energy. You run from place to place, impatient if we don’t keep up. “Run with me, daddy! Run with me!” you will say, and hold out your hand, waiting for me to take it. Your teachers see that, too. “He’s a wild one,” they’ve told us. I was like that when I was your age, too. In fact, when I played with the other kids in my neighborhood, their parents wouldn’t let me inside their houses for fear that I’d, I don’t know, set things on fire and dance on the ashes. I could only play with their children outside. Maybe you’ll learn to channel your energy sooner than I did.

Your teachers have also commented on how funny you are. “He imitates what we say, and we can’t hardly keep from laughing. And it’s hard to get angry with him because he’s so sweet-tempered.” It’s true. A lot of your cuteness comes from how pleasant and kind you are. I have no idea how you ended up that way — mom and I didn’t deliberately try to encourage that in you. You’ve just now started throwing tantrums when you don’t get what you want, but the tantrums don’t last long, and you don’t carry a grudge.

I’ve been fascinated watching you develop your own sense of self. You’ve started giving your animals personalities. When you talk for them, you affect this funny high-pitched voice. “Hi there, Eli’s daddy. I’m triceratops!” you squeak. At times you grab several small frogs or dinosaurs and have them carry on a conversation. “Hi, I’m frog!” “I’m frog, too.” “Do you want to come to my house, frog?” “Sure!” If you only have one frog, you’re sad that he has no friends and is all alone.

Each day I’m excited to get to know you better. I can now tell when you’re tired or hungry, no matter how hard you try to hide the fact. I know how easily your feelings bruise, far easier than your body. I know how you love to hold our hands and how you like to greet me when I get home from work with a cry of, “Daddy’s home!” You can barely wait for my truck to stop moving before you throw open the door and leap out into the garage.

In a few months you’re going to be a big brother. Good luck with that! Uncle Andrew and I fought, arguing over who got to sit in the front seat and who got which piece of cake and a million other trivial, stupid things that didn’t really matter then and certainly don’t matter now. I can only hope that you grow up to be as good a friend with your sibling as Andrew and I now are.

There’s so much I want to tell you about, but this letter is long enough as it is. Right after you were first born, my friend Jacqueline asked if I was going to keep writing these letters to you as you grew older. I told her I planned on it. I do it partly for selfish reasons. I just went back and re-read all of my previous letters to you, and I was reminded of things I’ve already forgotten. Time moves so quickly when you’re older, and never faster than when you’re with someone who changes on a daily basis. But these letters are, in the end, for you, and I want them to capture a few of the things I’ve always wanted to tell you.

As young as I am, I’ve still lived long enough that many things have a date attached to them like forlorn cartoon thought bubbles. We’ve lived in our house for nearly five years. My truck is twelve years old. I just listened to a song I first heard when I was a teenager some twenty years ago. That young actor from a much-loved movie series is now sixty-five. But when I’m with you, I get glimpses of what the world was like when it was new. Light reflected from you illuminates overlooked nooks and crannies of my life. My promise to you is that I’ll do my best to keep showing you as much of the world as I can get my arms around.

Eli and me at the lab