How to Add Shortcodes to Your WordPress Plugin

WordPress shortcodes provide an easy way to insert complex content into your posts. Content generated by plugins might be a complex HTML table, a video and playlist, some fancy jQuery interface elements–the possibilities are endless. Shortcodes empower the author to say “put that generated content right here in my post,” and then not worry about it.

Shortcodes for our Simplenotes plugin

Simplenotes plugin option screenRecently I wrote a tutorial showing how to add WordPress Pointers to your plugins. As part of the tutorial, I included a little dummy plugin so people could see the fully-working code. The plugin, called Simplenotes, does the following:

  • Provides a custom metabox on edit screens for authors to input a note.
  • Allows options to automatically place the note before a post’s content, after it, or not at all.

More flexibility needed

What if we want our note to appear after the second paragraph, or some other custom location in our content? We can’t do that with the first Simplenotes plugin. If we had shortcode support in there, we could have the plugin insert a post’s custom note anywhere the author entered the shortcode.

Simplenote 1.1 goal

Our main objective, then, is to make Simplenote respond to a shortcode, [simplenote], and replace it with whatever custom note has been attached to that post–if any.

Digging in to the Simplenote code

Backups and a version change

Zip up a copy of the original pk-simplenote directory and add a “-10” to the end so you’ll have the 1.0 version tucked away safely. Then open the pk-simplenote.php file. Before we go any further, let’s change the version number towards the top of the page from 1.0 to 1.1.

Telling your plugin to look out for shortcodes

Unless you tell your plugin to look for certain shortcodes, you can put “[my-shortcode]” in your post content all day long, and nothing will happen. There is nothing special about the square brackets (“[“ and “]”) or about your shortcode name–until we tell our plugin they are special.
In our plugin’s constructor function, at the end, we’ll tell the plugin to look out for a shortcode called “simplenote.”

// This adds support for a "simplenote" shortcode
add_shortcode( 'simplenote', array( $this, 'simplenote_shortcode_fn' );

Now, amongst the many other things WordPress will do when processing the content of our posts, it will look for “[simplenote]” and replace it with the returned results of function “simplenote_shortcode_fn.”

Building the shortcode handler

In our plugin’s class, we add the bare bones of this function. This function can go anywhere in the class, but we’ll add it down by the function from version 1.0 responsible for adding the simplenote to the beginning or end of content. That function is pksimplenote_addnote(), and makes use of pksimplenote_getnote(). After the pksimplenote_getnote() function, we add our shortcode handler.

function simplenote_shortcode_fn( $attributes ) {
    return "Something from a shortcode function.";
}

Give that scarecrow some brains

As is, that function would simply print “Something from a shortcode function” anywhere WordPress finds our shortcode in a post’s content. Let’s change the function to do what we really want. In short, we’ll retrieve the current post’s simplenote value and return it.

function simplenote_shortcode_fn( $attributes ) {
    $current_simplenote = $this->pksimplenote_getnote();
    if( $current_simplenote ) {
        return $current_simplenote;
    }
}

We use the existing function “pksimplenote_getnote” to retrieve the note itself. If the result is not empty, we return it.

At this point, any occurrence of [simplenote] in our content will be replaced with the post’s simple note content. Yay!

More power! We need more power!

Sometimes authors may want to send in their shortcode with some instructions, or attributes. These can tell the shortcode handler function to process the shortcode a bit differently, output it differently, and so forth.

Bring in the attributes

For Simplenote, let’s allow authors to specify if their note should be styled with <strong> tags, <em> tags, both, or neither.

The author can enter a shortcode like this:
[simplenote em=true strong=true]

We change the handler function like so to accommodate these options.

function simplenote_shortcode_fn( $attributes ) {
    // get optional attributes and assign default values if not present
    extract( shortcode_atts( array(
        'em' => 'false',
        'strong' => 'false',
    ), $attributes ) );
    // pass options on to the function getting our note
    // because pksimplenote_getnote is wrapping the content in styled <p> element
    $current_simplenote = $this->pksimplenote_getnote( $em, $strong );
    if( $current_simplenote ) {
        return $current_simplenote;
    }
}
  1. We simply added “$attributes” so the function can accept a value.
  2. When attributes are included with a shortcode tag, WordPress packs them up in an array, which we receive in “$attributes.”
  3. We use “shortcode_atts” to combine the provided attributes with default settings. This way, if any property was not included in attributes, it at least has its default.
  4. We use “extract” to pull key/value pairs out as local variables.

Changes are afoot

Now that we have all the attributes, we need to do something with them. Our next task is to go fetch the content of our note and return it. However, our function “pksimplenote_getnote” will return the content, but it also returns it styled. Since our attributes deal with styling, we need to send “pksimplenote_getnote” those attributes. We write the function call as:

$current_simplenote = $this->pksimplenote_getnote( $em, $strong );

We’d better go update the pksimplenote_getnote function to accept and handle these two parameters.

function pksimplenote_getnote( $em=false, $strong=false ) {
    global $post;
    $the_note = get_post_meta( $post->ID, '_pksimplenote-note', true );
    if( !$the_note ) {
        $the_note = "There is no note assigned to this post.";
    }
    // default styling
    $style_string = "border:1px solid #000;background:#FFFF7F;padding:8px;";
    // add emphasis and/or bold depending on arguments sent int
    if( $em ) {
        $style_string .= "font-style:oblique;";
    }
    if( $strong ) {
        $style_string .= "font-weight:bold;";
    }
    return "<p style='border:1px solid #000; background:#FFFF7F; padding:8px; $style_string'> $the_note</p>n";
}
  1. The pksimplenote_getnote function accepts two arguments, $em and $strong, which are false by default.
  2. We moved the paragraph element’s styling into a variable $style_string we could modify as needed.
  3. In two conditionals, we check to see if either $em or $strong are true, and add appropriate styling rules to $style_string.

Bravo! At this point, the author can include optional attributes to bold or italicize their note when it’s output!

What about shortcodes that enclose content?

There’s another way authors can add shortcodes in their post content. They can wrap some of the content inside shortcode start and end tags, like so:

[myshortcodename]Here is the text wrapped inside.[/myshortcodename]

This let’s us alter the wrapped content in some way, like a poor-man’s function call. For Simplenote, the only function I can think of that might fit this usage is to style the wrapped block of text in the same way the function styles a post’s note. If the author wants a block of text to appear with the same styling as a note, they can pass it in a shortcode like this:

[simplenote]This is the text they want styled like a note.[/simplenote]

If they want to do this and pass in emphasis and strong parameters, they still can:

[simplenote em=true]This is the text they want styled like a note.[/simplenote]

The wrapped text is passed to the shortcode handler function in a variable named $content. If no text is wrapped, $content is empty. Let’s modify our shortcode handler to accept wrapped text.

function simplenote_shortcode_fn( $attributes, $content = “” ) {
    // get optional attributes and assign default values if not present
    extract( shortcode_atts( array(
        'em' => 'false',
        'strong' => 'false',
    ), $attributes ) );
    // check to see if any wrapped text was passed in
    if( $content == “” ) {
        // no wrapped text was passed in, so let's simply look to output the post’s note
        $current_simplenote = $this->pksimplenote_getnote( $em, $strong );
    } else {
        // wrapped content is in $content, so let's style it and return it
        $current_simplenote = style_content( $em, $strong, $content );
    }
    if( $current_simplenote ) {
        return $current_simplenote;
    }
}
  1. We accept $content explicitly as a parameter, and default it to an empty string.
  2. We add a check inside to see if $content is empty. If it is, no wrapped text was passed in, and we can proceed normally.
  3. If $content is not null, we have wrapped text we need to return with the simplenote styling. We don’t need to use the _getnote function, since we want to return the provided text instead of a stored note. We do need to call the style function used to format the desired text. We use the style_content() function for this.

Note: Here, we’ve found it was shortsighted to have the pksimplenote_getnote() function responsible for both retrieving and styling the post’s note. It’s now obvious we need to access the “getting” and “styling” functions separately. No problem — we’ll separate them.

Separating functionality

We still need to call _getnote and have the post’s note returned, with style. While this isn’t our preference, there may be users of version 1.0 counting on the styled paragraph element to always be there.

We also need to be able to send in text we already have and get it styled the same way.

We accomplish this by removing the styling operations into their own function. Then, our original _getnote function can make use of the new styling function, and any other function can provide content to the styling function, as well.

// retrieve the note from the database and optionally style it
function pksimplenote_getnote( $em=false, $strong=false ) {
    global $post;
    $the_note = get_post_meta( $post->ID, '_pksimplenote-note', true );
    if( $the_note == '' ) {
        $the_note = "There is no note assigned to this post.";
    }
    // here we defer the styling operations to function “style_content()”
    return $this->style_content( $em, $strong, $the_note );
}
// style the note
function style_content( $em=false, $strong=false, $the_note ) {
    // default styling
    $style_string = "border:1px solid #000;background:#FFFF7F;padding:8px;";
    // add emphasis and/or bold depending on arguments sent int
    if( $em ) {
        $style_string .= "font-style:oblique;";
    }
    if( $strong ) {
        $style_string .= "font-weight:bold;";
    }
    return "<p style='border:1px solid #000; background:#FFFF7F; padding:8px; $style_string'>$the_note</p>n";
}

Handling shortcodes inside other shortcodes

Lastly, lets consider what happens if we put the following shortcode in a post’s content.

[simplenote]This is my text [superphoto] that will be sent to simplenote.[/simplenote]

This example contains an imaginary shortcode called “superphoto,” which will insert some sort of content when handled properly. In order for our simplenote shortcode handler to honor the superphoto shortcode, we need to make one last small change to the main shortcode handler.

function simplenote_shortcode_fn( $attributes, $content ) {
    // get optional attributes and assign default values if not present
    extract( shortcode_atts( array(
        'em' => false,
        'strong' => false,
    ), $attributes ) );
    // check to see if any wrapped text was passed in
    if( $content == '' ) {
        // no wrapped text was passed in, so let's simple look to output the custom note
        // pass options on to the function getting our note
        // because pksimplenote_getnote is wrapping the content in styled <p> element
        $current_simplenote = $this->pksimplenote_getnote( $em, $strong );
    } else {
        // wrapped content is in $content
        // it's possible content contains other shortcodes needing handled
        $content = do_shortcode( $content );
        // let's style it and return it
        $current_simplenote = $this->style_content( $em, $strong, $content );
    }
    if( $current_simplenote ) {
        return $current_simplenote;
    }
}

That’s all there is to it. While the Simplenotes plugin doesn’t provide a practical real-world benefit, I’m hoping its simplicity helps you understand how shortcode handling is added to plugins.

You can download the Simplenotes version 1.1 plugin here.

Credits

Related posts:

  1. Daily Tip: Useful Plugin Displays All Available WordPress Shortcodes Have problems remembering shortcodes? Not anymore! Check out this supremely…
  2. Maximize Sales with 4 New Shortcodes for MarketPress Product Display The latest release of MarketPress gives you more flexibility for…
  3. 10 Awesome Shortcodes For Your WordPress Blog Discover 10 shortcodes with awesome functionality that you can use…