Creating complex shortcodes in WordPress

I occasionally dabble in custom WordPress themes on my wife’s behalf and in furtherance of her vast eBook empire. As part of her current theme, she wanted some shortcodes to do neat things like having a multi-column section of a blog post. That way, she can write:

[column]First column here![/column]
[column]Second column here![/column]

instead of the actual mess of HTML and CSS required to make such magic happen. Plus, if we ever decide to change the markup we’re using to generate columns, we can just change the implementation of the shortcode instead of going and updating a whole bunch of blog posts. Cool feature!

Defining a shortcode

In your theme’s functions.php, you need to do (at minimum) two things:

  1. Create a function that replaces the shortcode with its actual content.
  2. Register your shortcode with WordPress.

This part is straightforward. Say we wanted to create a [contentbox] shortcode to automatically wrapped its contents into an <aside>. That could look something like:

We could use it this way:

[contentbox align="right"]Here's a nice right-aligned piece of content![/contentbox]

When included in a post, that would become:

<aside class='content-box right'>Here's a nice right-aligned piece of content!</aside>

(CSS to make it actually look like a box and aligned right is left as an exercise for the reader.)

The evil that lies within

There’s a problem. WordPress has an ancient, vile, and notorious function in place which is designed to make users’ lives easier and developers’ lives miserable. This function is called wpautop and I hate it passionately.

The idea behind wpautop is simple: convert all double linebreaks into paragraph elements, and tack on <br> tags to whatever’s left. This lets an average user type text into the editor and have it converted to HTML even without using WordPress’ visual editor. The problem is that wpautop doesn’t know anything about shortcodes, so if our user splits our shortcode into several lines (such as the [columns] example at the top of this post), wpautop comes through and sprays <br> tags everywhere. Incidentally, <br> stands for “break”, which is exactly what wpautop is designed to do to nice complex shortcodes.

This problem has been known for ages – it was reported at least 9 years ago as of this writing, was still allegedly being discussed by WordPress devs 7 months ago, and there is no fix. Ridiculous.

Rescuing the shortcodes

There are at least four ways to deal with the problem of wpautop murdering our shortcode content:

Option 1: Change do_shortcode filter priority

While loading a page, WordPress executes a long list of “filters”, which are just functions that operate on the post’s content. wpautop is one such filter, as is do_shortcode. The latter is what ends up calling our own custom shortcode functions, replacing the shortcodes with actual content in the post. In WordPress core, wpautop runs at priority 10 and do_shortcode runs after that, at 11. So one way to thwart wpautop is to change do_shortcode‘s priority to 9, meaning our shortcodes will be resolved first and hopefully wpautop won’t mangle the output too badly.

This isn’t a great solution because it can potentially break all kinds of other themes or plugins (which might rely on WordPress’ default filter order) in subtle ways. Until today, I was using this approach for my wife’s site theme, but then we started switching her shopping cart to WooCommerce and discovered that my hack broke the text on the “proceed to checkout” button (because wpautop vomited a <br> randomly into the button text).

So if you take this route, expect occasional oddities with other plugins. This is not a well-behaved solution.

Option 2: Disable wpautop entirely

I love this option because I hate wpautop. However, this will likely break even more themes/plugins than option 1, and also users may not get the expected results if they’re composing posts in the text editor without using a sensible text parser like Markdown. Still, if you want to do it, just put these two lines into functions.php:

remove_filter( ‘the_content’, ‘wpautop’ );
remove_filter( ‘the_excerpt’, ‘wpautop’ );

Option 3: Disable wpautop on a post-by-post basis

You can install a plugin to disable wpautop only on certain posts–in our case, the ones with shortcodes on it.

I like how the plugin description explains its existence by saying “Back in the day, when the wpautop filter was really sucky…” What, you mean like yesterday?

Option 4: Hide the shortcodes behind a false bookcase

Like squirreling away guns or hiding Jews from the Nazis, this approach pretends that shortcodes don’t exist until after wpautop runs. It’s the most complicated, but it’s probably also the best because it lets wpautop commit whatever debauchery it wants on the post and then puts our precious shortcodes back in place unmolested. There are two steps:

  1. Register a filter before wpautop to search the post content for any shortcodes and replace them with random strings.
  2. Register a filter after wpautop but before do_shortcode to switch out the random strings for the original shortcode text.

Easier said than done, because finding complex shortcodes is no walk in the park. Fortunately, I’ve done the hard work for you!

When included in your functions.php file, the above code will:

  1. Search for any shortcodes in your post/page content.
  2. Once a shortcode opening tag is found, attempt to locate its corresponding closing tag.
  3. Replace the entire shortcode contents in the post with a random string.
  4. After wpautop has run, replace the random strings with the original shortcode markup.

After that, do_shortcode should run and evaluate your shortcodes normally.

Back to GMail

A little over a year ago Google announced Inbox, touting it as some kind of new way to revolutionize email. (Never heard that one before!) Some of my coworkers gave it a try and said they liked it, so I went to sign up. I was promptly rebuffed by a page which insisted that in order to use the web application, I’d need to first activate Inbox on my smartphone. Problem is, I didn’t have a smartphone, because I was secure and happy in my position as a dumbphone-equipped living anachronism. Yet, in an instant, I had become a victim of smartphone bigotry by this prejudiced and opinionated web application refusing to serve me just because I didn’t own a smartphone. I had become the Rosa Parks of the tech world.

So I told them (that is to say, I told myself in a passive-aggressively belligerent fashion) they could keep their lousy bus; I’d walk. And I stuck to GMail.

Then, earlier this year, to the shock and amazement of all who knew me, I bought a smartphone and signed up for Ting (FYI, that’s a filthy capitalist pig referral link, and you’ll get $25 credit for signing up with it). This was prompted in part because my dumbphone had an encounter with the Columbia River (it having the misfortune to be sitting in my pocket as I jumped into the water to shove my sailboat off the muddy shores of Bateman Island following a particularly heavy grounding). The phone did actually recover from the incident, but afterward the battery would only hold a charge for about a day. And I mean to say, if I’m going to be plugging in my phone to charge it every single day like some smartphone peasant, then I might as well be deriving some smartphone benefits instead of being stuck with only the drawbacks.

So anyway, after selling out all my principles to the smartphone, I promptly signed up for the Inbox bus. (I pulled my hat low over my eyes so they wouldn’t recognize me.) The mobile app turned out to be pretty great; I still have it installed. The web app also seemed great…at first. But here are the things wrong with it.

Nowhere is Safe (to Click)

I don’t know how most people set focus on their windows (like if they switched to a different program and now want to switch back), but I pick a blank space on the page and click on it. This doesn’t work in Inbox, because if you have an open conversation, clicking anywhere in the grey area outside the email will cause the whole conversation to collapse. This is especially annoying when the conversation has multiple emails in it and you were reading one in the middle, because by default Inbox will just open the first and last message with the rest collapsed in the middle. (Same as GMail, but GMail doesn’t throw its hands up and close everything whenever you click on some whitespace on the page.)

Drag-and-drop Attachments Don’t Work (as Well)

GMail had this workflow mastered: drag and drop a file from your computer onto the email and it provides a nice interface to automatically upload and attach it. Smooth and easy! Inbox is supposed to do the same thing, I think, but it doesn’t. Dragging a file onto the email you’re composing flat out does not work unless you have “popped out” the email (i.e., you’re not composing a reply inline). Even when “popped out”, when dragging the file over the message, Inbox does not present a nice UI the way GMail does – the only feedback I get is the operating system “Move” cursor. Dropping it sometimes does attach it to the message, and sometimes it just opens the file in the browser (navigating away from Inbox entirely).

(Some) Keyboard Shortcuts Don’t Work

I love keyboard shortcuts. Why use a mouse when you don’t have to? And shortcuts in GMail are great. you can do all sorts of things without ever touching the mouse. You can also do most of these things in Inbox, but not all – Ctrl+Shift+C and Ctrl+Shift+B to focus the Cc: and Bcc: lines on a message you’re composing do not work in Inbox. This annoys me to a surprising degree. Likewise, labeling: l should offer to label the current conversation. This does not work in Inbox. Speaking of labels…

Labels? What Labels?

Labels exist in Inbox, but they are not the same. They’re treated more like folders, where you “move message to…” whatever label you want. In GMail, you can apply multiple labels to a conversation and they all show up on the message itself in whatever view you’re using. This is helpful for keeping track of which conversations need a reply, which want a followup later on, and so on, while still allowing you to organize mail by project, client, or any other desired metric. (Inbox partially solves the “needs a reply, but not right now” problem with the concept of “snoozing” a mail – making it disappear from the inbox until a specified time. I really like this concept, but it’s not enough by itself.)

Which Message are You Replying to?

If you’re using keyboard shortcuts (and you should be), then you will find immediate annoyance in how Inbox handles replies in a multi-email conversation. Sometimes, when multiple people are involved in a conversation, it is useful to reply to a particular message in the middle of the thread rather than the most recent message. Often, if a particular thread is long and I’m just catching up on it, I may read through all the messages and then go back and reply to one in the middle. This is one case where I admit to using the mouse; I find it quicker to click on the particular message I’m interested in rather than repeatedly pressing p or n and then o to highlight and open the message I care about. So I’ll click on an email and then hit r. In GMail, this opens a reply to that particular email; in Inbox the clicking does not focus the message in the middle of the thread so r generates a reply to the latest email. It’s annoying when you catch it and confusing to others when you don’t (since your reply no longer makes any sense).

Where is My Favicon?!

GMail favicon It seems to inconsequential, yet it is so important. The number of important unread messages displays in the GMail favicon, so I can see whether I’ve got new mail at a glance. Not so with the Inbox favicon, which remains its same-old hip blue envelope-with-a-checkmark regardless of whether you have no unread emails or 44,000 unread emails.

The End of the Matter

Sadly, Inbox is overall a nicer app to deal with – the web application is more responsive and I like many of the user interface changes. Unfortunately, Google took several steps backward in the user experience department, and until they address these things I’ll be sticking with GMail on the web. I do still use the Inbox app on my smartphone, so I guess they got me there.

Dates Matter, and Not Just for Singles

I’m not sure exactly when it started becoming popular, but at some point removing the date from blog posts became chic. I believe the idea was that if an article has a date on it, then people who arrive at your site by searching Google for content can tell how old the article is, and if it’s old, they will no longer be interested in it. Therefore, to increase traffic from Google, you should remove all the dates from your websites.

I cannot express the extent to which this drives me completely bonkers.

There are two types of writing: timeless and time-sensitive. This post you’re reading is an example of timeless content. It does not matter whether you read it the day after it was written or twenty years down the road; my opinion remains the correct one.

An example of a time sensitive post would be anything written about the technical details of Ruby on Rails, where the likelihood of it being completely out of date and irrelevant to the current version of the framework approaches 95% approximately four months after the date of authorship.

For some people, I’ve read that removing dates from your content can increase your traffic from Google, because if two articles are written about substantially the same topic and one is a few years newer, then who wants to read the old one? In order to continue driving eyeballs to your site, you’ve got to remove dates entirely so that the eyeballs can’t tell at a glance whether the content is still relevant or not.

In a piece of fiction, or perhaps an opinion piece, I don’t really care when it was written. (I might still like to know, to get a better idea of the historical context, but it won’t drive me nuts.) In the case of anything technical, if the article is not up-front about when it was written, I will not even bother to read it (unless I’m desperate).

Furthermore, do not ever try to placate both sides by only including part of the date. I will not pick on anyone in particular here by including direct links, but suffice it to say that the other day I was seeking an answer to a technical question and found a blog which only listed month and day. “August 30th?” I said. “That’s relatively recent.” Except after reading some of the article, it became clear that it was the August 30th of several years ago and the answer in the post was no longer the correct one.

Perhaps my biggest gripe arising from this pet peeve of mine is the purported goal of this date eradication: to drive traffic to your website. The goal of good marketing should be to direct people to solutions that solve their problems. (Hopefully, your solution is one that will solve lots of people’s problems.) The goal should not be to grab as many random strangers as possible off of Google and then waste a few minutes of their time staring at your website. That might be lucrative (or it might not; there seem to be arguments from both sides), but it’s neither helping people nor making the web a better place.

Make the web more useful. Say “yes” to dates.

Have a Slap in the Facebook

When you’re young, you look at television and think, There’s a conspiracy. The networks have conspired to dumb us down. But when you get a little older, you realize that’s not true. The networks are in business to give people exactly what they want. That’s a far more depressing thought. Conspiracy is optimistic! You can shoot the bastards! We can have a revolution! But the networks are really in business to give people what they want. It’s the truth.

Steve Jobs

Q. How do you know when somebody closes their Facebook account?
A. They’ll tell you about it.
— The Internet

I wouldn’t normally keep track of this sort of thing, because I’m not on Facebook. However, I do read a few tech blogs and news aggregators, and recent events have bubbled into my consciousness. In case you, also, are healthy enough to maintain general ignorance of Mr. Zuckerberg’s latest capers, allow me to briefly summarize the news of the last few weeks:

  1. Facebook gives all users an email address and changes user profiles to display this address instead of their previous one..
  2. Users of Facebook’s contact sync for mobile devices had their address books overwritten with email addresses.
  3. Users noticed that addresses were losing messages.
  4. Facebook announced that the contact sync bug was not intentional, but that users were just confused about missing messages.

So then, why do I care about this? After all, I’m not on Facebook, I don’t have a mobile device, and in my opinion, anyone who is still on Facebook deserves whatever they get, because they should have known better already. Respecting Hanlon’s Razor, I don’t know whether Facebook is actually a malicious entity, but either way I try to make sure my friends on Facebook are aware that the company is not out to do them any favors. If you’re not paying for the service, then you’re not the customer–you’re the product.

This is not a Facebook witch hunt post. Rather, I am completely fascinated by people’s sheer willingness to put up with continual abuse. And I’m not talking about high-minded abuses of liberties, abstract freedom, the downfall of society as we know it, or other holier-than-thou screeds I could write. No, I’m talking about abuse in terms of software slapping a user in the face by throwing away user data and disregarding user preferences. Losing email due to Facebook hijacking your address? Who puts up with that?!

My guess is that virtually everyone will put up with that, and this guess is confirmed by my casual conversations with several Facebook users at a 4th of July picnic today. You’ll have a tiny technical minority that’s outraged, a larger set of users who are puzzled and mildly annoyed, and an enormous plurality who either won’t know or won’t care about the changes. From the set of those who actually know what Facebook is doing and are angry about it, how many do you suppose will go so far as to close their account? My guess is an infinitesimal fraction, a rounding error in Facebook’s daily account signups.

What’s the lesson here? I write software and I am susceptible to trying too hard for perfectionism rather than pragmatism (resulting in analysis paralysis and deadlock and not achieving anything worthwhile), so the lesson to me is poignant and depressing:

Matt’s Law of User Abuse

The degree to which you can abuse your users is directly proportional to the number of users you have multiplied by the degree to which they are addicted to your service.

That’s too wordy, however. There is a succinct version I like better:

Matt’s Law of User Abuse

Most people don’t care.

Your software might have to be good and respectful of users if you only have a few, but once you have mass-market appeal, you can just about do whatever you like. I would put money on the notion that Facebook could post video of its staff sacrificing kittens on an altar every week and they still wouldn’t lose an appreciable number of users, because people don’t care.

Nobody should be surprised by Facebook’s success at the user abuse game, however. The observant reader will note that this type of behavior also has rather direct parallels to government and civil liberties. After all, people are willing to be publicly molested at airports in exchange for illusions of safety. Why would they object to having their privacy molested online in exchange for illusions of community?