Shortcodes in Drupal? Yes, you can!

Shortcodes are for embedding complex elements with very little effort.  They are perfect for users with little to no HTML knowledge.  Shortcodes are often used in Wordpress, but they can also be used in Drupal as well by creating a simple custom module.  We're going to walk through adding a custom [button] shortcode for Drupal 7.

EDIT (2/22/2015): If you're interested in adding buttons to the WYSIWYG toolbar (TinyMCE) to make it easier for end users to add in the shortcodes, check out this post written by Jacob Swain!
EDIT (10/31/2015): I have created a repo on Github with a working version of the module, using everything we've gone through in this post.  I hope you find it useful!

Wordpress is growing on me the more I develop websites with it.  One thing about Wordpress that I’ve grown to love is shortcodes.  They make adding complex elements pretty easy for the users managing the website, without them having to dive into HTML.  After a bit of research and trial and error, I was able to integrate shortcodes with Drupal too! Let's dive in and create a button shortcode for our Drupal 7 website.

I was developing a Drupal website and I realized shortcodes would be really useful in the sidebar of the website.  I decided to do some research and see if there were any similar modules for Drupal that would allow for shortcodes.  Whaddya know, there’s a stable Shortcodes module for Drupal 7!  The module comes with sub modules that have a set of predefined shortcodes.  I quickly realized that there is no documentation (or any good documentation I could find) on setting up your own custom shortcodes.  After a little playing around, I was able to create my own custom shortcodes pretty easily.

For this tutorial we are going to create a simple button shortcode.  It will look as follows:

[button link="http://google.com"]Button Text Here[/button]

Our HTML that will be outputted is:

<div class="button"><a href="http://google.com">Button Text Here</a></div>

Step 1: The Obvious – Install and enable the Shortcodes module

Of course, you need to make sure you have the Shortcodes module installed and enabled.  It comes packaged with several sub modules, but for this tutorial we will only need to enable the Shortcode module (and our custom module, which we will create now).

Step 2: Create our custom module .info file

To create our custom shortcodes, we need to create our own custom module.  Let’s call it Custom Shortcodes.  Here is an example of a module .info file:

name = Custom Shortcodes
description = Provides custom shortcode tags to be used within site content.
package = Shortcode
core = 7.x
dependencies[] = filter
dependencies[] = shortcode

Create a new folder within your sites/all/modules directory named custom_shortcodes and save this file in there.

Step 3: Create our module file

There are two main functions we will use within our custom module file that define our shortcodes and their properties.  Our two main functions are:

/**
 * Define our shortcodes and their titles, descriptions, and callback functions
 *
 * See comments below for explanation
 */
function custom_shortcodes_shortcode_info() {

    $shortcodes['button'] = array(
        'title' => t('Link Button'),  // The title of this shortcode, displayed in the Drupal backend for administrators to enable/disable shortcodes for input types
        'description' => t('A simple button.'),  // Description shown along with the title in the Drupal backend
        'process callback' => 'custom_shortcodes_shortcode_button', // Custom function that deals with the variables and html output
        'tips callback' => 'custom_shortcodes_shortcode_button_tip' // Custom function that displays some help text to the user
    );

    // $shortcodes['second_shortcode'] = array();
    // $shortcodes['third_shortcode'] = array();
    // and so on...

    return $shortcodes;
}

/**
 * Define our variables (parameters) for each shortcode
 */
function custom_shortcodes_theme() {
  return array(
    'shortcode_button' => array(
      'variables' => array('text' => '', 'link' => ''),
    ),
    // 'second_shortcode' => array(),
    // 'third_shortcode' => array(),
    // and so on...
  );
}

Each shortcode will then have three of it's own functions that collect its properties and output them.

function MYMODULE_shortcode_SHORTCODENAMEHERE($attrs, $text) {}
function theme_shortcode_SHORTCODENAMEHERE($vars) {}
function MYMODULE_shortcode_SHORTCODENAMEHERE_tip($format, $long) {}

These 3 functions will be used each time you create a different shortcode.  Note that you don't necessarily need the third function for the WYSIWYG tip, but I believe it is a useful feature.  You can show the user which shortcodes are available and explain how to use each one and what parameters they have.

Now, lets use these 3 functions to create our button shortcode:

/**
 * Define our process callback function for our [button] shortcode. This
 * takes in our shortcode attributes from the shortcode and if empty, sets the property
 * to the default value stated in this function.  We then pass in our attributes to the
 * theme() function which outputs the HTML.
 *
 * $attrs = shortcode_attrs(array(
 *     'attribute' => 'default_value_goes_here'
 * ),
 */
function custom_shortcodes_shortcode_button($attrs, $text) {
    $attrs = shortcode_attrs(array(
        'link' => 'http://mywebsite.com'
      ),
      $attrs
    );

    return theme('shortcode_button', array('link' => $attrs['link'], 'text' => $text));
}

/**
 * This function uses the attributes passed in to return the HTML of this shortcode.
 */
function theme_shortcode_button($vars) {
  return '<div class="button"><a href="' . $vars['link'] . '">' . $vars['text'] . '</a></div>';
}

/**
 * This function outputs some tips to the user beneath the WYSIWYG editor so they know
 * what the shortcode does and how to use it.
 */
function custom_shortcodes_shortcode_button_tip($format, $long) {
  $output = array();
  $output[] = '<p><strong>' . t('[button link="http://URLhere.com"]text[/button]') . '</strong> ';
  if ($long) {
    $output[] = t('Outputs text that is displayed as a button, which links to a specified URL.') . '</p>';
  }
  else {
    $output[] = t('Outputs text that links to a URL.') . '</p>';
  }

  return implode(' ', $output);
}

Now we have finished our module file. Save it and if necessary upload it through FTP to your website.

Step 4: Enable our custom module and shortcodes.

Now you can go ahead and enable your custom module.  Next, you must enable the individual shortcodes.  You can enable different shortcodes per input format which gives you a ton of control. 

  1. Go to Configuration > Content Authoring > Text Formats.
  2. Find the input format you want to enable the shortcode for and click on Configure in that row.
  3. Enable the two shortcode filters (Shortcodes - HTML corrector and Shortcodes).
  4. Under Filter settings, check the checkbox next to 'Enable Link Button Shortcode'.
  5. Click Save.

Step 5: Test it out!

I added a custom block to test out my shortcode.  If you notice below the WYSIWYG editor is our helpful tip to users on how to use the shortcode:

Thumbnail

After some simple styling with CSS, we now have a clickable button!

Thumbnail

Comments

Awesome guide, thank you! You should submit this to the module for documentation because they don't have any instructions.

I'm happy you found this useful! I think I will submit this to the module for documentation, I feel like it would help other developers set up their shortcodes more easily!

Thank you so much for this guide, it is the only piece of documentation I have found about shortcodes. Works perfectly !
Agree you should submit it for documentation, it would be really useful !

Thank you thank you thank you! I've been hunting for a solution like this for a while. I adapted your code so that I can stick an inline Shockwave MP3 player into my blog posts, and it works! However, one little issue: The attributes aren't passing through, not even the default values for the attributes. The text between the tags is being passed, but nothing within the tags. In other words: [button link="This is not passed"]This is passed successfully[/button]. I'm not enough of a programmer to figure out where the code is going wrong.

Thanks again! I really appreciate you posting this tutorial.

It's great to hear you found this useful! I'd love to help you figure out why the attributes aren't been passed through to the shortcode.  I just sent you an email; my guess would be the issue is somewhere within the MYMODULE_shortcode_SHORTCODENAMEHERE function.  I would make sure all of the attributes are being passed into the theme function in the return statement:

return theme( 'shortcode_button', array('ATTRIBUTE1_NAME' => $attrs['ATTRIBUTE1_NAME'], 'ATTRIBUTE2_NAME' => $attrs['ATTRIBUTE2_NAME'], 'text' => $text) );

If this doesn't fix the issue, feel free to email me your module and I'll try and find what the error might be.

There is a D6 version of the Shortcode module, though I haven't used it on any project yet.  I assume these instructions may also work for the D6 version, but I can't totally say for sure.  I'll try and look into it and I'll update this post with my findings.

Hi Brianna, May you please help me?
How can i create options to user choose?
eg: i created column shortcode, i would like have select box to user choose 1/2 or 1/3....
How can i do that, help me please thank in advance

To create the functionality you want, ideally you'd want some sort of button in the WYSIWYG editor for creating your shortcode.  Clicking on the button will fire a pop up and ask the user for information (inputs, select boxes, whatever you may need).  It would then generate the shortcode and insert it into the WYSIWYG editor.  That's a bit beyond what I've shown in this article. 

I haven't created custom buttons for WYSIWYG editors in Drupal yet, but it's something I'd love to try.  I'll post a new blog post once I've got a working example!

yes, now i know the way to create form to get user options, i use hook_shortcodename_attributes($form, $form_state) {} to do that. i have an another problem, wysiwyg render <p> tag which made my shortcodes display is not correct, how can i remove all <p> tag in wysiwyg

I'm glad you figured it out!  Regarding the extra tags around the shortcodes: can you verify that you have the 'Shortcodes - html corrector' filter enabled for the text format you're using (Configuration > Content authoring > Text formats > [Your format name, eg: Full HTML].  Perhaps that filter is disabled on your end.

Thanks for the instructions. Had the module definable for a new site I imported content from. Wasn't working in blocks and content types and I thought it might have been a filters thing, but your post confirmed it.

All the pages I've seen on short codes show Full HTML. However, we don't allow our editors to have Full HTML. Can it be used with filtered HTML? I want to give them things to include like a Twitter feed without allowing them to input scripts. My shortcode would then enter the script hopefully after Drupal has finished its filter. Is that just a matter of the order or will Drupal just not allow it in Filtered mode?

Yes! You should be able to enable the shortcode filters for any text format, not just Full HTML.  If you have a lot of filters enabled for your Filtered HTML format, you might want to try moving the shortcode filter below all other filters so it gets processed last.  Otherwise, the other filters may cause issues with the output generated by your shortcode.

Thanks a lot mate! There is no proper documentation on the internet about this module. Helped me a lot.

Awesome, I'm glad I could help! Thanks for taking the time to let me know this post was helpful! :)

Hi I have installed this module but when I enable the module is shows blank page module is not installed. I am using drupal7.

In reply to by Dhirendra Kumar (not verified)

Hello there! It's hard for me to say what's causing the issue without looking directly at the website you're working on.  I have updated the post with a link to a Github repo with a working version of the module.  Would you mind downloading that module and seeing if it works on your end?

"Applause!!" Nice stuff.
Here is something I did to expand on it to set up a form to set the attribute settings from the shortcode button. This does require the Shortcode WYSIWYG module to see the form.

Add a callback to the main function.

'attributes callback' => 'custom_shortcodes_SHORTCODENAMEHERE_attributes' // Custom form to set attributes

Then Add a 4th function for this callback

function custom_shortcodes_SHORTCODENAMEHERE_attributes($form, &$form_state) {
$form['VARIABLENAME'] = array(
'#title' => t('VARIABLENAME'),
'#type' => 'textfield',
'#states' => array(
'visible' => array(
':input[name="shortcode"]' => array('value' => 'SHORTCODENAMEHERE'),
),
),
);

return $form;
}

This populates the variable from the form that appears in the pop-up. This is for those clients that get wigged out when you start talking shortcodes. And for shortcodes that have multiple variables.

In reply to by jberg1 (not verified)

Why thank you! :)

I didn't realize that the Shortcode for WYSIWYG module existed.  I just tested out the code and it worked perfectly :) Shortcodes can definitely be confusing and intimidating for clients. If we can make using shortcodes easier it will benefit them greatly, and this definitely helps a lot! Thanks for the tip!

This is great thank you so much. FWIW I had to disable "Convert URLs into links" in text format settings to get this to work properly. Great addition to my Drupal toolkit!

You're very welcome! Thanks for the tip, I'm sure someone will find that useful!

I've tried developing an additional shortcode but copying the button shortcode in your example, however when I change the shortcode from [button] to [audio] my WYSIWYG strips out the first shortcode but then renders everything else inline - including the [/audio]. Any ideas what the issue might be?

Hi there Saundra! Would you mind sending me your code so I can take a peek? I tried messing around with my code but I couldn't reproduce your issue. I'm sure looking at the actual code will help! Either email it over to me or maybe put it on Github if you can!

Very Easy to follow... just worked. Thanks very much!

In reply to by Nate (not verified)

Great!! Thank you for taking the time to let me know it was helpful to you!

The content of this field is kept private and will not be shown publicly.