Using Shortcodes
Most of the custom functionality I’ve shown so far would only be seen by the database administrator. But what if you want to change what is displayed to website visitors? There are multiple techniques you can use. I’ve talked about using filter hooks to intercept the default content to be displayed in a post or page, alter it in some way, and then send it on for output. That’s great if you want to alter the output on every post, or on a bunch of posts with the same characteristics. But what if you have something you want to insert more selectively? Maybe you have a data entry form or a whole database report you want to display, but only on a single page of your website.
One of the best techniques I’ve found is to use a shortcode — a WordPress placeholder you include in your post or page that gets replaced at runtime with the output of your custom function.
The format is something like this:
[demotag]
or
[demotag title=”Date” date=”r”] Date in RFC822 Format [/demotag]
[demotag title=”Server Array Variables” server=”yes”]This is the PHP $_Server Array[/demotag]
And the code to process it looks like this:
shortcode.php :
<?php function demotag_func($atts = array(), $content = '') { if(!$atts["title"]) $atts["title"] = "Default Title"; $output = "<h3>".$atts["title"]."</h3>\n".$content; if($atts["server"] == "yes") $output .= "<pre>".var_export($_SERVER,true)."</pre>"; if($atts["date"]) $output .= "<p>".date($atts["date"])."</p>"; return $output; } add_shortcode('demotag', 'demotag_func'); ?>
Any attributes included inside the square brackets are passed to your custom function as an array ($atts in this example). If you use the form that includes a bracketed close tag, any content between the open tag and close tag are passed to the function as a string ($content in this example).
The output of this example function, looks like this:

Contact Form Example
For a slightly more realistic example, consider this contact form. You would have to do some more work to make this pretty, or to make the email message it generates get past spam filters, but it will indeed take the user’s input, generate an email, and display the appropriate success or error message. You can see it in action at http://www.webliberationnow.com/demo-form/.
contact_form.php :
<?php function demoform_message($message) { return '<div style="background-color: #DDD; padding: 20px;"><div style="background-color: #FFF; padding: 20px;">'.$message.'</div></div>'; } function demoform_func($atts, $content) { global $post; $permalink = get_permalink($post->ID); ob_start(); if($error = $_GET["demoform_error"]) echo demoform_message("Error processing submission"); elseif($success = $_GET["demoform_success"]) echo demoform_message("Thank you for your submission"); else { // show the form ?> <h2>Request a Quote</h2> <form id="form1" name="form1" method="post" action="<?=$permalink?>"> <p> Name: <input type="text" name="name" id="name" /> </p> <p>Email: <input type="text" name="email" id="email" /> </p> <p>Project:</p> <p> <textarea name="project" id="project" cols="45" rows="5"></textarea> <input type="submit" name="button" id="button" value="Submit" /> </p> <p><a href="#" id="showhide">Show Instructions</a></p> <div class="stuff" style="display: none;"> <p>Fill out the form and click submit.</p> <p>Fill out the form and click submit.</p> <p>Fill out the form and click submit.</p> <p>Fill out the form and click submit.</p> <p>Fill out the form and click submit.</p> <p>Fill out the form and click submit.</p> <p>Fill out the form and click submit.</p> <p>Fill out the form and click submit.</p> </div> <?php wp_nonce_field("cfn","contact_form_nonce"); ?> </form> <p>&nbsp;</p> <?php } return ob_get_clean(); } add_shortcode('demoform', 'demoform_func'); function handle_contact_post() { global $post; $r = $_SERVER['HTTP_REFERER']; if($nonce = $_POST["contact_form_nonce"]) { if(wp_verify_nonce($nonce, "cfn") ) { foreach ($_POST as $name => $value) $message .= "$name : $value \n"; mail("david@carrcommunications.com","Contact Form",$message,'From: "Contact Form" <'.$_SERVER["SERVER_ADMIN"].">\n"); header("Location: $r?demoform_success=1"); exit(); } else { header("Location: $r?demoform_error=1"); exit(); } } } add_action('init','handle_contact_post'); ?>
For a more souped up, user interface we may want to use JavaScript and perhaps AJAX techniques for submitting data without reloading the page. Without getting too fancy, here is an example that is used to show or hide the instructions at the bottom of the page. WordPress ships with several standard JavaScript libraries, and in this case I’m going to use jQuery. See the documentation on wp_enqueue_script for the list of bundled JavaScript libraries and instructions on how to invoke them.
Essentially, wp_enqueue_script tries to prevent the same script from being loaded multiple times by different plugins. It can also help ensure that scripts are loaded in the right order. Below, we register a new script and pass a list of its dependencies in an array. In this case, there is only one item in the array, jquery.
The custom function that includes these instructions is triggered by the ‘wp’ action. This occurs a little later than ‘init’ — just late enough that I can test for the presence of my shortcode in the current post or page — but before WordPress has started sending HTML to the browser.
load-jquery.php :
<?php function load_my_js() { if(is_admin() ) return; // never mind if this is an admin page, should only load on public pages global $post; if(strpos($post->post_content,'[demoform]') == false) return; wp_register_script( 'js_test', plugins_url() . '/demo/test.js', array('jquery') ); //don't need to enqueue jquery if included in dependencies array //wp_enqueue_script('jquery'); wp_enqueue_script('js_test'); } // official docs say to do wp_enqueue_script on 'init' but 'wp' action lets us test for post identifier add_action('wp','load_my_js'); ?>
The JavaScript loaded by this routine invokes jQuery when the document is ready for action (when it has finished loading into the browser). This script detects the block of text with the class identifier “stuff” and hides it to start with. When the user clicks on the link with the ID “showhide”, the script toggles between showing the hidden text or hiding it again.
test.js
jQuery(document).ready(function($){ //hidden to start with $(".stuff").hide('fast'); $("#showhide").toggle(function(){ $(".stuff").show('fast'); },function(){ $(".stuff").hide('slow'); }); });
One thing to note is that jQuery is invoked with jQuery(document).ready(function($)
. You can cause yourself considerable grief trying to use standard examples from jquery.com and other sample sites that use the $ notation as a reference to jQuery objects from the outset.
On a standalone web page that includes the jquery library, code like the following would work fine.
$(document).ready(function(){ $("a").click(function(event){ alert("As you can see, the link no longer took you to jquery.com"); event.preventDefault(); }); });
But because WordPress includes multiple JavaScript libraries that can be invoked by different plugins, it loads jQuery in a "no conflict mode" that limits you to using the $ notation within an enclosure that starts with jQuery(document).ready(function($)
.
For a more sophisticated use of jQuery with AJAX, download a copy of Vladimir Prelovac’s WP Wall plugin and look through the source code. Prelovac’s book WordPress Plugin Development gives a detailed walk through of the code and the ideas behind it.
I hope this has given you a taste of how you can customize WordPress with plugins, whether for your own use or for broader distribution.