WordPress’s core functionality is proven, tested, and thoroughly maintained by the core committers at Automattic, and throughout the rest of the WordPress community. However, while core WordPress provides you with the primary tools you need to manage a website—such as adding, modifying and deleting pages and posts, managing users, and other basic functions for a modern website—most sites need more functionality to be truly useful to the end user. To meet this need, WordPress offers plugins, a standard way to introduce additional functionality outside the scope of core WordPress to extend it for just about any purpose.
In truth, the ease with which users can add plugins to WordPress, along with the simple mechanisms available to developers for creating plugins, has contributed to making WordPress the most popular CMS on the Web today. If there’s a task you want to perform on your website that isn’t available specifically within core WordPress, there’s probably a plugin for it.
Plugins are incredibly powerful; they heighten the scope and functionality of any WordPress project, but they can also cause many of problems on any given site. For that reason, it’s important to cover some basic information about plugins before we dive deep into the code.
Plugins are so easy to find and install. From right within the Plugins menu in the admin area, you can search for, browse, and install plugins that are listed—as seen in Figure 5.1—in the WordPress Plugin Directory.
Beware, though, that not all WordPress plugins are listed within the WordPress Plugin Directory, but as an end user seeking plugins, Google is a great option for you. Many premium, commonly used plugins like Gravity Forms or Shopp need to be purchased separately and installed into your WordPress installation through the
option.Aside from their easy installation, there is a bunch of other useful aspects about plugins; for instance:
You’ll almost always be able to find a plugin for what you are looking to accomplish.
It’s common to be able to find several plugins that perform the same task in a different way, should you be unhappy with the way a particular plugin works.
Plugins provide a certain level of future-proofing insurance for your site, because you’re able to easily add the latest functionality when new technologies are quickly embraced by the public.
While the upsides are fairly obvious, there’s a darker side to plugins that’s often overlooked by end users, but all too apparent to the seasoned developer who’s had to deal with the aftermath. While core WordPress is developed, maintained, and vetted by a team of trusted developers with core commit privileges, the plugin space is, in many ways, the equivalent of the Wild West.
On the most fundamental level, plugins typically comprise nothing more than a few PHP functions and possibly some supporting JavaScript or CSS. As we’ll demonstrate in just a bit, writing a plugin and registering it with your WordPress installation is a simple process, but the quality of the code therein is only as solid and thought-out as the developer chooses to (or is able to) make it. Reality dictates that there are plugin developers all along the talent spectrum, ranging from beginners to experts, and, naturally, the products each developer produces directly corresponds to their level of proficiency. Furthermore, plugins are typically developed by either one developer or a small team of two to three working on the project. Things can get missed, and everybody has bad days.
In short, there are some poorly coded plugins out there.
While no method is 100% guaranteed, you can largely avoid poorly coded plugins by doing your homework on them before you install them. Start by looking at the overall star ranking of a plugin—these rankings are subjective, but a large enough sample user base will give you some reasonable expectations. You can also look at the discussion about the plugin right from the plugin’s comments section: see if there have been complaints about the plugin (and if you are so inclined, try to make a judgment as to how relevant those complaints are). Another tried-and-tested way to check out plugins before installing is to simply google them and see what’s been said about them. And, of course, even a highly rated plugin can be problematic if it hasn’t been in production for some time, and was last compatible with an older WordPress version than you happen to be running.
Just what are some of the issues that come up with plugins? There is a litany, but here are a few common problems:
A portion of plugins use deprecated action hooks that either are no longer in use, or are in the process of being phased out of core WordPress.
Some plugins have poor naming conventions that conflict with other active plugins, thus creating unexpected results on the site.
Occasionally, a plugin simply isn’t coded correctly, so it just fails to work.
Another issue with many plugins—especially those in the WordPress Plugin Directory—is that since most of them are created by non-compensated developers in their spare time, you may not receive prompt technical support when the need arises; this fact has almost single-handedly driven the popularity of the premium WordPress plugin market, where tech support is more widespread.
With all of that said, plugins are essential to bending WordPress to your will; it’s just necessary to keep a few guidelines in mind when using them. As an end user, follow these two tips and you’ll be fine:
Every time you add a new plugin to WordPress, you are introducing a new set of PHP functions that are designed to perform tasks. By definition, adding PHP functions to a website adds programming that makes your WordPress installation a little more complex, and creates potential tripping points for problems to occur down the track. It’s actually a lot like installing and running software programs on your computer: the more programs you have running at any one time, the slower your computer may run, especially if some of those programs are poorly put together. In this way, running superfluous plugins possibly increases your site’s execution time, thus slowing it down. Given that search engines factor site speed as a part of their ranking algorithms, that’s potentially a sensitive issue.
It’s a corollary to the rule above, but when unused plugins are kept active, it’s common to see conflicts crop up later on with other plugins (or themes), should you make a change to them. Your best practice is to—at the very least—keep unused plugins deactivated.
Worth mentioning as well are must-use plugins, which comprise an underutilized but extremely useful technique for working with plugins. Must-use plugins are handy for developers who want to add plugins to a WordPress installation in a way that makes them more difficult for end users to deactivate. There’s nothing particularly special about must-use plugins themselves—any plugin can be a must-use plugin. What’s unique is how and where the plugins are installed. Instead of being downloaded from the WordPress Plugin Directory (or uploaded from your computer system) into the wp-content/plugins directory, they must be manually installed via FTP or your web host’s control panel into the wp-content/mu-plugins directory. This directory does not exist by default—you’ll need to create it—but when WordPress sees that it’s there, it will automatically activate and load any plugin it finds therein.
Must-use plugins have several special properties in the way that they’re handled by WordPress:
You can’t just drop a plugin subdirectory into wp-content/mu-plugins. Must-use plugins either need to be individual PHP files in the directory, or they need to have a PHP include file pointing to the subdirectory that the plugin is sitting in.
If a plugin is loaded as a must-use plugin, there’s no need to activate it—it’s always on. This is useful to developers when they want to ensure that a client does not remove a particular plugin.
WordPress won’t notify you when a must-use plugin has an update. Instead, you’ll need to manage updates manually … or not. This can be useful when you want to continue using an older version of a plugin for any reason.
Must-use plugins are loaded by PHP before all standard plugins, so API hooks added in a must-use plugin will apply to all other plugins.
When you’re running a website on WordPress and you begin to experience problems, start by looking at your plugins and use this elimination technique. Deactivate all your plugins, and check to see whether your problem still exists on the website. If it does, you can be reasonably certain that the issue is either coming from within your content or within the WordPress core installation itself (it’s rare, but it does happen).
It’s common, however, for the issue to resolve itself after the deactivation of all your plugins. From here, reactivate your plugins one by one, starting with the most important plugins necessary for your site to function properly. Sooner or later, you’ll locate the offending plugin, where you can then seek an alternate solution to managing that piece of functionality on your site.
When you’re troubleshooting WordPress by removing plugins, remember that must-use plugins won’t show up in the main plugins listing. Unless you physically remove must-use plugins from the wp-content/mu-plugins directory, those plugins will be active and loading their functions into your WordPress environment. If you forget to remove them, you can foil your own divide-and-conquer trouble-shooting technique that we described above. A handy technique is to zip all your plugins up into a zip file or a tar file on your server for safekeeping, and then delete the runtime files. Once you’re done troubleshooting, you can restore your zip or tar file, restoring your plugins to their original state.
Another special type of plugin is a drop-in plugin. Drop-in plugins replace entire portions of WordPress core functionality. They are specifically named files that you can create and customize, and must be located within the wp-content/ directory. A complete listing of drop-in plugins can be found in Table 5.1, along with the context (single WordPress installation or WordPress Multisite installation) in which each is appropriate.
Table 5.1. Drop-in plugin availability chart
Whether you’re a developer or an end user, ask yourself whether it’s necessary to develop (or contract out the development of) a plugin before you begin the work. There are literally dozens of ways to do many common programming tasks within WordPress, and with the prolific use of the platform, it’s rare that you’d be creating a plugin that nobody else has attempted to do before you. Whether searching in the WordPress Plugin Directory or Google, you are apt to find several solutions that meet your needs. In the event that you’re unable to find a suitable plugin solution, sometimes the creative use of several plugins can accomplish your goal equally as well.
While many of us in the development community cut our teeth on writing every single line of code ourselves, you’ll often find that others who’ve tackled the same problems before us have created scripts with more fleshed out and better interfaces than we’d manage—at least, not without a significant investment of time and effort on our own. Use the open-source community; it’s there to help you. If after an exhaustive search you still find that no existing plugin meets your needs, or you have an extremely specific piece of functionality that’s unique to your application, by all means knock yourself out!
If you choose to modify an existing plugin, you will need to pay attention to the software license associated with it. If you’re modifying a plugin for the plugin directory, make sure it has a GPLv2 license, which allows any user to modify and reuse code so long as an attribution is given to its original author.
None of us are perfect, and experienced plugin developers will
testify that debugging your plugin as you develop it is just a fact of
life. While there are several techniques you can use to debug your code,
the most fundamental is to go into your
wp-config.php file in your sandbox
WordPress installation and ensure you’ve enabled
WP_DEBUG
, so that it appears in your code as follows:
define('WP_DEBUG', true);
This will
automatically make WordPress spit out any PHP warnings or notices that
you’ll need to be aware of in order to correct, displaying them in real
time as they happen. Furthermore, if you’re going to be modifying any of
WordPress’s built-in JavaScript, make sure you enable
SCRIPT_DEBUG
as well, so that it appears in
wp-config.php as follows:
define('SCRIPT_DEBUG', true);
In
the same way as WP_DEBUG
,
SCRIPT_DEBUG
will display JavaScript issues in real
time as they occur.
Now that we’ve addressed the basics of what plugins are, how they’re installed, what to be careful of when you use them, and when to actually create one yourself, let’s get our hands dirty and pick them apart. Upwards and onwards!
Conceptually, every plugin is broken up into two basic pieces:
the wrapper, or packaging, which tells WordPress that it is, indeed, a valid plugin
the scripting that makes the plugin actually perform a useful task
Let’s investigate the wrapper component of a plugin first.
WordPress provides us with a standardized place to keep all the plugins within a particular WordPress installation: the wp-content/plugins directory. Because all plugins are stored in the same location, your plugin must have a unique name, lest it cause an error as WordPress attempts to initialize it upon running. This unique name should be built right into the main PHP file for the plugin that will live in the wp-content/plugins directory, or in the name of the directory that will house the primary plugin PHP file. While technically all a plugin needs to function is just one properly formatted PHP file, it’s generally considered best practice to give each plugin its own directory, storing all files associated with the plugin within subdirectories therein. At the very top of our primary PHP file for our plugin, we’ll add a standard identifying WordPress plugin header. We introduced the concept of the plugin header briefly back in the section called “Creating Your First Custom Post Types ” in Chapter 4, but it’s worth going into more detail here:
<?php /* Plugin Name: The Name of Your Plugin Here Plugin URI: Link to the Home page for the Plugin Description: Brief descriptive text for the Plugin Version: What Version is the Plugin Author: Author Name Author URI: Author Home page */
The only required line in this header is the Plugin
Name
, but the rest of the information is extremely important,
and WordPress will use the information within this header when
initializing your plugin for use. Aside from verifying that it is a
valid WordPress plugin file, WordPress gathers the information about the
plugin in this header for your users to view in the Manage
Plugins screen, as shown in Figure 5.2.
Making sure that you include correct, up-to-date information about who the plugin is written by and where users can go for updates ensures its usefulness over time. Additionally, you should make sure to include licensing information about the plugin. Most plugins are GPL-compatible, and indeed must be so if they are to be included within the WordPress Plugin Directory (more on that in the section called “The WordPress Plugin Directory ”). Licensing information should directly follow your header information. Standard GPL licensing (with dummy text inserted for copyright year, plugin author name, and plugin author email) looks like this:
<?php /* Copyright YEAR PLUGIN_AUTHOR_NAME(email : PLUGIN AUTHOR EMAIL) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
While it is customary to insert the plugin license directly underneath the plugin header, it’s also a nice idea to include it in a text file called license.txt within the plugin directory. An additional (and completely optional) step is to include a readme.txt file with your plugin, to provide any basic information or frequently asked questions to your plugin users that you might deem useful, but that’s entirely up to you.
That’s pretty much all we need to do in order to set up a plugin in the correct format so that WordPress can recognize it. Now’s when the fun begins, and we can actually start doing something genuinely useful.
We know that WordPress is a collection of PHP scripts that executes functions in a specific order, creating an end result that essentially comprises our website. One way to visualize the way core WordPress works is to think of it as a conveyor belt that moves through a specific process to get from point A to point B, producing a website as its end product. There’s no black magic here, but rather an assembly line of PHP functions that moves information in different directions, depending on a set of predefined rules.
When we write plugins, what we are really doing is introducing new PHP functions that add to the predetermined rules WordPress has already given us. To make that happen, WordPress has been good enough to create a special set of PHP functions that give us the ability to connect with that conveyor belt in the specific place it is appropriate to do so. These functions are called hooks, and they are the essential tools we need to be able to take our great new ideas and latch them into WordPress in a way that makes them useful to our users. Two hooks exist: Action hooks and filter hooks.
Action hooks are used when specific
events take place within WordPress’s execution process. For instance, if
you’d like to add some inline CSS code into the header of a page
template, you could use the
wp_head()
action hook to do so,
executing your function to create the appropriate lines of code when the
wp_head()
function is run in the standard assembly
line of WordPress core functions. A properly formatted action hook call
follows this format:
<?php add_action( hook, function, priority, accepted_arguments ); ?>
Where the parameters are equivalent to:
hook
(string): the action hook to use
(required)
function
(string): the name of your
function to add (required)
priority
(arguments): the priority in
which the function should be run (optional, and defaults to
10)
accepted_arguments
(arguments): the
number of arguments the defined function can accept (optional, and
defaults to 1)
Let’s consider our simple CSS insertion example above, and see what that code might look like:
<?php function inline_css() { ?> <style type="text/css" > .mockingbird {padding-top:15px;} .mockingbird .famous {display:block;padding:1em;} .mockingbird .famous p.label_title {font-size:12px;font-weight:bold; display:block;margin-bottom:5px;} .mockingbird .famous label.no_bold {font-weight:normal;} </style> <?php } add_action( 'wp_head', 'inline_css' ); ?>
Easy stuff, right? We’ve just created a short function called
inline_css()
containing some CSS code that defines
styles for a class called mockingbird
, as well as
a subclass that defines what a famous mockingbird
might look like. Then, using our add_action()
function, we employ the
wp_head()
action hook to add the code in this
function to the header of our WordPress page; this is when events occur
that are associated with the wp_head
action
hook.
There are many action hooks available to latch into our WordPress assembly line process where we want to, but here are some that are more commonly used:
wp_head
: triggered in the
<head>
section of the loaded
theme
init
—triggered after WordPress
has finished loading, but before any headers are sent; excellent
place to intercept
$_GET
and
$_POST
HTML requests
admin_init
—same as
init
, but runs only on admin Dashboard
pages
admin_head
—triggered in the
<head>
section of the admin
Dashboard
Where action hooks are used to execute functions at a
certain time during the WordPress assembly line process,
filter hooks, are used when you want to modify
information before saving it to a database or outputting it to a
browser; they’re typically used when modifying text in some way, shape,
or form. The classic example of a filter would be in censoring out
profane language that your users might try to add to pages or posts on
your site. In this instance, you might apply a PHP function to a
specific filtering hook such as the_content
, so
that you can remove words such as putz, dumdum head, or dimwit with a
standard phrase like [mean name]. Let’s take a peek at what an example
of the PHP function and the associated filter might look like in this
instance:
<?php function play_nice($content) { $mean_words = array("putz","dumdum head","dimwit"); $content=str_ireplace($mean_words,'[mean name]',$content); return $content; } add_filter ('the_content', 'play_nice'), ?>
Here, we’ve written a little function that identifies and replaces
words that we want to omit. Then, we’ve added our filter, instructing
WordPress to run everything that goes through
the_content
(our filter hook) through our function,
thus ensuring nobody can call anybody else a dimwit.
Did you notice that the syntax of the filter hook in
our example above looks a whole lot like the syntax for a properly
formatted action hook? There’s good reason: while filter hooks and
action hooks serve very different purposes, the
add_action()
and
add_filter()
functions have
identical syntaxes and take parameters in identical ways. In truth,
you can really view the add_filter()
function
as a mechanism to keep concepts straight in your mind, and keep
yourself (and other programmers working on your code) sane when
trying to figure out exactly what and how it works.
As much as we all like to be trusting, the truth is that every room has a shadow or two, and sometimes bad things lurk in the shadows. And if we have a user who tries to call another user a dumdum head on our public website for all to see, maybe they’ll want to do worse! There’s power in paranoia, kids, and for this reason you always need to be certain to validate and sanitize your data.
Much like brushing your teeth and (we trust) showering with soap every morning, data validation and sanitization should be treated as a habit every time you either output to a browser or save to your database. Essentially, what you want to do here is scrub every piece of data you can that is coming from a location external to your own code, as it may have illegal characters or genuine malicious intent.
WordPress gives us a standardized set of escaping functions we can use to scrub our data and ensure it’s safe for our use. Consider Figure 5.3, which describes the WordPress escaping API.
In this diagram, we see that there are three components to the function set, as described following:
esc
: the prefix for the escaping
function
attr
: the context being escaped, with
possible values including:
_e
: an optional translation suffix,
with possible values including:
__
: returns a translated
value
_e
: echoes a translated
value
For example, in order to remove any HTML tags from a text string, you would use this format:
<?php esc_html( $text ); ?>
Alternatively, if you were looking to escape any illegal characters from within an HTML attribute, you might code this:
<input type=”text” name=”name” value=”<?php echo esc_attr($text); ?>”>
For more information on data validation, take a look at the WordPress Codex.
When you are validating and sanitizing your data, make sure you do it as late as you possibly can before outputting it or saving it to your database. Running your validation too early will leave your data susceptible to any errors or vulnerabilities introduced in code that’s run after the validation has been complete, potentially causing issues. And nobody wants that.
Okay, we’ll avoid acting like a parent here and beating this into the ground, but seriously: don’t be an ingrate, remember to validate!
For the purpose of illustrating how all the pieces of any given plugin work in practice, we’ll use an example of a fictitious plugin created especially for you and this book: Antelope General Social Media Links (or AGSocialMedia, for short). AGSocialMedia is a simple plugin that will show us how to do some basic but useful tasks, such as:
create a plugin settings screen inside WordPress that lets us update and manage links to our four favorite social media sites: Facebook, Twitter, YouTube, and LinkedIn
add a
link in the plugin management screen that takes us directly to the aforementioned plugin settings pageadd a simple widget that lets us integrate our social media links wherever and however we want on our WordPress site
Of course, it’s not really a fictitious plugin; in actuality, it works quite well and you can feel free to use it or extend it for yourself. After all, it’s being presented to you by us under the GPL license, and you can find the code in this book’s code archive.
The purpose of this section is not to teach you how to code PHP—we’ll assume you have a working knowledge of that. Rather, the purpose of this demonstration is to illustrate how your PHP code will interact with the various hooks and filters that connect your script to WordPress and make all the magic happen.
We’ll begin our examination of AGSocialMedia by looking at the code for the entire plugin, and then pick it apart limb by limb:
<?php /* Plugin Name: Antelope General Social Media Links Plugin URI: http://mickolinik.com/plugins/antelope-social-media-links Description: Easily add links to your social media profiles Version: 1.0 Author: Mick Olinik Author URI: http://www.mickolinik.com */ /* Copyright 2011 Mick Olinik (email : [email protected]) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ //load textdomain for localization settings load_plugin_textdomain('agsml', false, basename( dirname( __FILE__ ) ) .↵ '/languages' ); add_action('admin_menu', 'agsml_create_menu'), function agsml_create_menu() { //create new top-level menu add_options_page('Antelope General Social Media Links',↵ 'AG Social Media Links', 'manage_options', __FILE__, 'agsml_settings_page'), add_filter( "plugin_action_links", "agsml_settings_link", 10, 2 ); //call register settings function add_action( 'admin_init', 'agsml_register_settings' ); } //add settings link to plugins list function agsml_settings_link($links, $file) { static $this_plugin; if (!$this_plugin) $this_plugin = plugin_basename(__FILE__); if ($file == $this_plugin){ $settings_link = '<a href="options-general.php?page=AGSocialMedia/↵ antelope-social-media-links.php">'.__("Settings", "agsml_social_media").↵ '</a>'; array_unshift($links, $settings_link); } return $links; } function agsml_register_settings() { //register our settings register_setting( 'antelope_social_group', 'agsml_facebook' ); register_setting( 'antelope_social_group', 'agsml_twitter' ); register_setting( 'antelope_social_group', 'agsml_youtube' ); register_setting( 'antelope_social_group', 'agsml_linkedin' ); } //create css for admin screen function agsml_admin_css() { ?> <style type="text/css" > .agsml_social_list {padding-top:15px;} .agsml_social_list .setting {display:block;padding:1em;} .agsml_social_list .setting p.label_title {font-size:12px;font-weight:bold; display:block;margin-bottom:5px;} .agsml_social_list .setting label.no_bold {font-weight:normal;} .agsml_social_list .setting label span.slim {width:200px;float:left; display:block;margin: 1px;padding: 3px;} .agsml_social_list .setting p.desc {font-size:10px;font-style:italic; text-indent:10px; text-align:left;} </style> <?php } add_action('admin_head', 'agsml_admin_css'), //html for settings form function agsml_settings_page() { ?> <div class="wrap agsml_social_list"> <h2>Antelope General Social Media Links</h2> <form method="post" action="options.php"> <?php settings_fields( 'antelope_social_group' ); ?> <div class="setting"> <p class="label_title"><?php _e('Facebook Profile URL:', 'agsml') ?></p> <p><label class="no_bold" for="agsml_facebook"> <span class="slim"><?php _e('Facebook URL', 'agsml') ?></span> <input name="agsml_facebook" type="text" id="agsml_facebook" value="<?php form_option('agsml_facebook'), ?>" /></label></p> <p class="desc"><?php _e('Enter the URL to your Facebook profile.') ?></p> <p class="label_title"><?php _e('Twitter Profile URL:', 'agsml') ?></p> <p><label class="no_bold" for="agsml_twitter"> <span class="slim"><?php _e('Twitter URL', 'agsml') ?></span> <input name="agsml_twitter" type="text" id="agsml_twitter" value="<?php form_option('agsml_twitter'), ?>" /></label> </p> <p class="desc"><?php _e('Enter the URL to your Twitter profile.') ?></p> <p class="label_title"><?php _e('YouTube Profile URL:', 'agsml') ?></p> <p><label class="no_bold" for="agsml_youtube"><span class="slim"> <?php _e('YouTube URL', 'agsml') ?></span> <input name="agsml_youtube" type="text" id="agsml_youtube" value="<?php form_option('agsml_youtube'), ?>" /></label></p> <p class="desc"><?php _e('Enter the URL to your YouTube profile.') ?></p> <p class="label_title"><?php _e('LinkedIn Profile URL:', 'agsml') ?></p> <p><label class="no_bold" for="agsml_linkedin"><span class="slim"> <?php _e('LinkedIn URL', 'agsml') ?></span> <input name="agsml_linkedin" type="text" id="agsml_linkedin" value="<?php form_option('agsml_linkedin'), ?>" /></label></p> <p class="desc"><?php _e('Enter the URL to your LinkedIn profile.',↵ 'agsml') ?></p> <p class="setting"> <input type="submit" class="button-primary" value="<?php _e↵ ('Save Social Media Links', 'agsml') ?>" /> </p> </div> </form> </div> <?php } function agsml_enqueue_styles() { // url to stylesheet $agsml_css_url= WP_PLUGIN_URL . '/' . plugin_basename(dirname(__FILE__)) .↵ '/agsml-widget.css'; //register and enqueue stylesheet wp_register_style('agsml_styles', $agsml_css_url); wp_enqueue_style( 'agsml_styles'), } add_action( 'wp_print_styles', 'agsml_enqueue_styles' ); /* Register the widget */ function agsml_register_widget() { register_widget( 'Antelope_Widget' ); } /* Begin Widget Class */ class Antelope_Widget extends WP_Widget { /* Widget setup */ function Antelope_Widget() { $widget_ops = array('classname' => 'agsml_widget', 'description' => ↵ __( 'Your Social Media Links', 'agsml') ); // The actual widget code goes here parent::WP_Widget( false, $name = 'AG Social Media Links', $widget_ops ); } /* Display the widget */ function widget( $args, $instance ) { //get widget arguments extract($args); //get widget title from instance variable $title = apply_filters('widget_title', $instance['title']); //insert before widget markup echo $before_widget; //if theres a title, echo it. if( $title ) echo $before_title . $title . $after_title; //start list $social_list .= '<ul>'; // define list if (get_option('agsml_facebook' )){ $social_list .= '<li class="facebook"><a href="'. ↵ get_option('agsml_facebook').'">' . __('Friend us on Facebook', 'agsml') .↵ '</a></li>'; } if (get_option('agsml_twitter' )){ $social_list .= '<li class="twitter"><a href="'.↵ get_option('agsml_twitter').'">' . __('Follow us on Twitter', 'agsml') .↵ '</a></li>'; } if (get_option('agsml_linkedin' )){ $social_list .= '<li class="linkedin"><a href="'. ↵ get_option('agsml_linkedin').'">' . __('Link us on LinkedIn', 'agsml') .↵ '</a></li>'; } if (get_option('agsml_youtube' )){ $social_list .= '<li class="youtube"><a href="'.↵ get_option('agsml_youtube').'">' . __('Watch us on Youtube', 'agsml') .↵ '</a></li>'; } // end list $share_content .= '</ul>'; //display assembled list echo $social_list; //insert before widget markup echo $after_widget; } /* Update the widget settings, just the title in this case */ function update( $new_instance, $old_instance ) { $instance = $old_instance; $instance['title'] = strip_tags($new_instance['title']); return $instance; } //form to display in widget settings. Allows user to set //title of widget. function form( $instance ) { $title = esc_attr($instance['title']); ?> <p> <label for="<?php echo $this->get_field_id('title'), ?>"> <?php _e('Title:'), ?></label> <input class="widefat" id="<?php echo $this->get_field_id('title'), ?>" name="<?php echo $this->get_field_name('title'), ?>" type="text" value="<?php echo $title; ?>" /> </p> <?php } } /* Load the widget */ add_action( 'widgets_init', 'agsml_register_widget' ); ?>
This is the meat of the plugin. The AGSocialMedia folder also includes several other directories and files, including an external CSS file to provide essential styles for the widget output, an /images directory for the social media icons we’ll use in our plugin, and a /languages directory with a .pot file for localization purposes. We’ll touch on some of these files in other areas of the book; for now, let’s just focus on the actual plugin file doing all the work.
Let’s take a look at that first chunk of code:
<?php /* Plugin Name: Antelope General Social Media Links Plugin URI: http://mickolinik.com/plugins/antelope-social-media-links Description: Easily add links to your social media profiles Version: 1.0 Author: Mick Olinik Author URI: http://www.mickolinik.com */ /* Copyright 2011 Mick Olinik (email : [email protected]) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
Nothing too exciting’s going on here, but there’s stuff we need to take care of anyway. We have our standard WordPress header detailing exactly what the name of the plugin is, what it’s for, the version, who wrote it, and so on. We’re also telling people what the terms of use are—will your end users need to promise you their firstborn child to use this thing, or are the terms of use easy like the summer breeze? Let the good people know whether it’s GPL or commercial.
WordPress queues plugin data in the plugin repository based on the information you provide within the header of your plugin. If you list your plugin in the Plugin Directory and forget to update the version number, WordPress installations running your plugin won’t be prompted that there’s an update available. Nobody wants to see your efforts wasted like that.
Next comes the code for our localization settings:
//load textdomain for localization settings load_plugin_textdomain('agsml', false, basename( dirname( __FILE__ ) ) .↵ '/languages' );
Before we really do anything in our plugin, we should
check to see whether we have a language translation file that matches
the language that has been set in
wp-config.php. WordPress is brought
to you in English by default, and most plugins are as well, but if we
have localized our plugin (and we have), we enable our users the ability
to extend the plugin themselves by translating it into a language of
their choice. Consider the function
load_plugin_textdomain()
, which takes
the three parameters described below:
load_plugin_textdomain( $domain, $abs_rel_path, $plugin_rel_path )
$domain
—a unique identifier
assigned to your custom translatable strings
$abs_rel_path
—an optional,
deprecated function as of WordPress 2.7. Default it to false or
just omit it; it’s nothing to worry about
$plugin_rel_path
—the relative
path to your translation key file. If you fail to define this
path, it will default to the root directory that the file is in.
While this is by definition an optional parameter, it’s best
practice to keep your language translation files separate from
your logic files, so you’ll usually want to specify a value
here.
A more detailed explanation of localization can be
found in Chapter 11, but for our purposes here,
we’re looking in the
/languages directory of our plugin to
see if there’s a language translation file that matches the language
we’re running WordPress in (if we’re not running it in English already).
If we find a match, load_plugin_textdomain()
will
grab all the translated text strings and swap them out for their
counterparts; these counterparts are defined within the plugin’s code as
it executes and outputs to the screen.
Now matters become interesting. Let’s look at this next code block and break it down into pieces:
add_action('admin_menu', 'agsml_create_menu'), function agsml_create_menu() { //create new top-level menu add_options_page('Antelope General Social Media Links',↵ 'AG Social Media Links', 'manage_options', __FILE__, 'agsml_settings_page'), add_filter( "plugin_action_links", "agsml_settings_link", 10, 2 ); //call register settings function add_action( 'admin_init', 'agsml_register_settings' ); } //add settings link to plugins list function agsml_settings_link($links, $file) { static $this_plugin; if (!$this_plugin) $this_plugin = plugin_basename(__FILE__); if ($file == $this_plugin){ $settings_link = '<a href="options-general.php?page=AGSocialMedia/↵ antelope-social-media-links.php">'.__("Settings", "agsml_social_media").↵ '</a>'; array_unshift($links, $settings_link); } return $links; } function agsml_register_settings() { //register our settings register_setting( 'antelope_social_group', 'agsml_facebook' ); register_setting( 'antelope_social_group', 'agsml_twitter' ); register_setting( 'antelope_social_group', 'agsml_youtube' ); register_setting( 'antelope_social_group', 'agsml_linkedin' ); }
In full, what we’re doing here is laying the groundwork for working with our plugin inside the WordPress admin area. Let’s start by looking at the top code block:
add_action('admin_menu', 'agsml_create_menu'), function agsml_create_menu() { //create new top-level menu add_options_page('Antelope General Social Media Links',↵ 'AG Social Media Links', 'manage_options', __FILE__, 'agsml_settings_page'), add_filter( "plugin_action_links", "agsml_settings_link", 10, 2 ); //call register settings function add_action( 'admin_init', 'agsml_register_settings' ); }
We start by first using an action hook, requesting that the
agsml_create_menu()
function. And what does that
function do? It utilizes the
add_options_page()
function to add
the Antelope General Social Media Links plugin and label it AG Social
Media Links. The other parameter of interest in the
add_options_page()
function is
agsml_settings_page
, which defines the callback
function to be used that displays the contents of the page within the
link. We’ll cover this function further on in our explanation.
WordPress provides easy wrapper functions that allow developers to add sublevel menu items to the primary top-level administrative menu items such as WordPress Codex.
, , , , , and so on. For more information as to how to add them, have a look at theThe next line is an example of a filter hook, whereby we are
calling
plugin_action_links()
and running it
through the agsml_settings_link()
function that
we’ll explore in a moment. This filter serves to insert the function
that creates the link to our plugin settings page directly within the
Manage Plugins listing page, which is utilized by
many of the nicer plugins.
Finally, we use another action hook to initialize the
agsml_register_settings()
function, which we’ll use
to save our data directly to the wp_options table
in our WordPress database. More on this shortly.
Let’s look at our next function:
//add settings link to plugins list function agsml_settings_link($links, $file) { static $this_plugin; if (!$this_plugin) $this_plugin = plugin_basename(__FILE__); if ($file == $this_plugin){ $settings_link = '<a href="options-general.php?page=AGSocialMedia/↵ antelope-social-media-links.php">'.__("Settings",↵ "simple-social-sharing").'</a>'; array_unshift($links, $settings_link); } return $links; }
This little code block just defines the PHP code we’ll use to
actually create the link and label that we’ll insert into our plugin
settings page. This will be placed directly within the Manage
Plugins listing page, as described previously in the filter
hook in the agsml_create_menu()
function. This is
really just PHP code, and there’s not much here that involves
WordPress.
Next up comes our
agsml_register_settings()
function:
function agsml_register_settings() { //register our settings register_setting( 'antelope_social_group', 'agsml_facebook' ); register_setting( 'antelope_social_group', 'agsml_twitter' ); register_setting( 'antelope_social_group', 'agsml_youtube' ); register_setting( 'antelope_social_group', 'agsml_linkedin' ); }
Here we go, back to WordPress functions. This is a useful spot to discuss database considerations when dealing with WordPress plugins.
When you are working with WordPress plugins, you’ll almost always need to save your data to the database at some point. There are generally two ways to do this:
Save your data to the wp_options table within your WordPress database.
Create a new table within your WordPress database and save your data there.
Space doesn’t allow us to cover the creation of new tables for plugin data storage in the context of this chapter; however, bear in mind that when you go in this direction, there’ll be several considerations you’ll need to keep in mind including initially setting the table up, ensuring that there are no naming conflicts, and developing a mechanism to safely remove the table upon uninstall. With that said, unless your plugin is extremely specialized and complex, and requires you to save an extensive quantity of data, you’ll typically use the first method and store your data in the wp_options table.
The register_setting()
function is
useful for defining the data you want to save for your plugin, and takes
the following parameters:
register_setting( $option_group, $option_name, $sanitize_callback )
$option_group
(string): a settings
group name, typically used to identify your plugin
(required)
$option_name
(string): the name of an
option to sanitize and save (required)
$sanitize_callback
(string): a
callback function that sanitizes the option’s value
(optional)
In our case, our intent is quite simple. We only really
have four values we want to save with this plugin, so we register each
of these with its own unique name and tag it back to a unique group
name: antelope_social_group
. Once we’ve done this, we
have our framework in place and we’re off to the races … what’s
next?
Check out the CSS file in the AGSocialMedia folder:
/* Antelope General Social Media Links */ .agsml_widget { overflow: hidden; padding: 0; } .agsml_widget ul { list-style-type:none; margin:0; padding:5px 0; } .agsml_widget ul li a{ padding: 5px 10px 5px 20px; line-height:18px; margin:0; } .agsml_widget ul li { padding-bottom:5px; } .agsml_widget ul li.twitter a { background:url(images/mini_twitter.png) no-repeat left; margin:0; } .agsml_widget ul li.facebook a { background:url(images/mini_facebook.png) no-repeat left; margin:0; } .agsml_widget ul li.linkedin a { background:url(images/mini_linkedin.png) no-repeat left; margin:0; } .agsml_widget ul li.youtube a { background:url(images/mini_youtube.png) no-repeat left; margin:0; }
Again, not a whole lot to see here aside from demonstrating the
addition of internal CSS styles within a plugin. We’re just creating a
function, inlaying some CSS for the purpose of styling our
Admin page to make it look pretty, and then adding
the function in to run with the
admin_head
action hook. At this
point in the game, we’ve seen this stuff before; let’s keep
moving.
Following this comes a chunk of form-building HTML:
//html for settings form function agsml_settings_page() { ?> <div class="wrap agsml_social_list"> <h2>Antelope General Social Media Links</h2> <form method="post" action="options.php"> <?php settings_fields( 'antelope_social_group' ); ?> <div class="setting"> <p class="label_title"><?php _e('Facebook Profile URL:', 'agsml') ?></p> <p><label class="no_bold" for="agsml_facebook"><span class="slim"> <?php _e('Facebook URL', 'agsml') ?></span> <input name="agsml_facebook" type="text" id="agsml_facebook" value="<?php form_option('agsml_facebook'), ?>" /></label></p> <p class="desc"><?php _e('Enter URL to your Facebook profile.') ?></p> <p class="label_title"><?php _e('Twitter Profile URL:', 'agsml') ?></p> <p><label class="no_bold" for="agsml_twitter"><span class="slim"> <?php _e('Twitter URL', 'agsml') ?></span> <input name="agsml_twitter" type="text" id="agsml_twitter" value="<?php form_option('agsml_twitter'), ?>" /></label></p> <p class="desc"><?php _e('Enter the URL to your Twitter profile.') ?></p> <p class="label_title"><?php _e('YouTube Profile URL:', 'agsml') ?></p> <p><label class="no_bold" for="agsml_youtube"><span class="slim"> <?php _e('YouTube URL', 'agsml') ?></span> <input name="agsml_youtube" type="text" id="agsml_youtube" value="<?php form_option('agsml_youtube'), ?>" /></label></p> <p class="desc"><?php _e('Enter the URL to your YouTube profile.') ?></p> <p class="label_title"><?php _e('LinkedIn Profile URL:', 'agsml') ?></p> <p><label class="no_bold" for="agsml_linkedin"><span class="slim"> <?php _e('LinkedIn URL', 'agsml') ?></span> <input name="agsml_linkedin" type="text" id="agsml_linkedin" value="<?php form_option('agsml_linkedin'), ?>" /></label></p> <p class="desc"><?php _e('Enter the URL to your LinkedIn profile.',↵ 'agsml') ?> </p> <p class="setting"> <input type="submit" class="button-primary" value="<?php _e('Save Social Media Links', 'agsml') ?>" /> </p> </div> </form> </div> <?php }
There are three main points in this code block worth noting.
First, we see our definition of our
agsml_settings_page()
function, which we last
referenced as the callback function in our
add_options_page()
function further back in the
code. In other words, it’s this function that defines the instructions
for displaying all the code for our actual Settings
page in the menu.
Secondly, since this is the administrative settings page output, it follows that we have text strings here being displayed to our users. Because we are thoughtful developers and want to make sure that folks all over the world are able to use our plugin in their own native language, we need to prepare our text strings for localization. This means that instead of just typing in an output string like this:
<p class="label_title">LinkedIn Profile URL:</p>
We’ve
added our _e()
wrapper tags around each of our
strings to produce results that look more like this:
<p class="label_title"><?php _e('LinkedIn Profile URL:', 'agsml') ?></p>
You’ll
notice here that we’ve defined both the string to be translated (in this
instance, 'LinkedIn Profile URL:'
) as well as the
unique domain namespace we’ve created for our localization at the top of
our code (agsml
). Again, for more on localization,
have a look at Chapter 11.
Finally, notice that we’ve taken care to sanitize the data
here before committing anything to our database. Here, we’ve used
form_option()
to output our social media values.
form_option()
runs these values through
esc_attr()
to sanitize them, ensuring that they are
safe for our use.
This next piece of code is an example of the proper and safe way to insert an external CSS sheet into your plugin:
function agsml_enqueue_styles() { // url to stylesheet $agsml_css_url= WP_PLUGIN_URL . '/' . plugin_basename(dirname(__FILE__)) .↵ '/agsml-widget.css'; //register and enqueue stylesheet wp_register_style('agsml_styles', $agsml_css_url); wp_enqueue_style( 'agsml_styles'), } add_action( 'wp_print_styles', 'agsml_enqueue_styles' );
We create a function called
agsml_enqueue_styles()
, and then define the URL to
the CSS for our widget to be displayed on the front end of our website,
but the next two functions are really worth taking note of. Rather than
just injecting the link to the CSS sheet directly into the
<head>
section of your WordPress site,
enqueueing a WordPress CSS file allows you to specify
dependencies that tell WordPress your CSS depends on another CSS file,
and should be loaded afterwards. While the
wp_register_style()
function essentially serves as
a helper function to prepare your data for
wp_enqueue_style()
,
wp_enqueue_style()
is where all the magic happens.
Our example is a very simplified form of
wp_enqueue_style()
, and so in the situation of
AGSocialMedia we’re really including it for proper form, and to prepare
to extend the plugin later on if we so choose.
It’s common to see plugin authors try to help people out by providing styles within the context of their plugins, essentially forcing user plugins to look a certain way. While the thought is usually well-intentioned, it can be a real pain in the behind for WordPress site developers trying to use your plugin on their own sites, especially when their visual style differs markedly to what you’ve supplied. Instead of creating a bevy of styles for your users choose from when using your plugin, make the choice to go minimal. Particularly if you are planning on releasing your plugin into the WordPress Plugin Directory, ask yourself this important question: “What’s the least amount of CSS I can supply for this functionality to display properly?” When you come up with an answer, add only that styling, and try to err on the side of less is more. If you were to put a Do and a Don’t list together for this, think of it this way: Do provide easy-to-use classes that designers can grab to style your plugin to suit their own needs. Don’t try to force your own perception of how your plugin should appear onto your end users.
Before we further continue tearing apart our Antelope General Social Media Links plugin, let’s take a moment to talk about widgets and how they work.
Useful for dragging different pieces of functionality around to sidebars, footers, and other widgetized areas on a WordPress site, widgets extend the functionality of a plugin by allowing users to place it in an appropriate place on their websites. They’re not appropriate to add to all plugins, but in the case of our AGSocialMedia plugin, they are, as the entire purpose of the plugin is to display icons and links to our four most used social media sites: Facebook, Twitter, LinkedIn, and YouTube.
Thanks to the widget API that WordPress gives us, creating and using widgets isn’t as difficult as it could be. There are three basic steps to creating and using a widget in your plugin, broken down as follows:
To further understand how widgets work, let’s look briefly at the basic structure of the widget class in WordPress:
<?php class Antelope_Widget extends WP_Widget { function Antelope_Widget() { // actual widget code that contains the function logic } function widget($args, $instance) { // display the widget on website } function update($new_instance, $old_instance) { // save widget options } function form($instance) { // form to display widget settings in WordPress admin } } ?>
As you can see, the widget class contains four basic components:
declaration of the widget name that extends the
WP_Widget
object and the subsequent
initialization of the function of the same name (in this case,
Antelope_Widget
)
logic that actually outputs the widget coding to the website as desired
functionality that allows you to create and save instances of the widget
a form that allows users to make changes to the widget if the developer has made them available in the Widgets management screen
If you are fairly new to programming and have never dealt with objects and instances before, widgets might throw you a bit. Here’s a layman’s version of what you need to understand. When you are registering a widget, you’re essentially creating an object, which can be thought of for our purposes as a template framework for how that particular widget should function and appear. Any given working example of that widget would then be referred to as an instance of the widget. Therefore, when we talk about widgets, we are really discussing two different aspects: initializing the template for how it will work (the object), and creating and outputting individual instances that you actually see and use. This may seem as clear as mud. It’ll get easier.
Let’s build upon these definitions and take up our AGSocialMedia plugin code where we left off: setting up the widget to output our links.
Check out our first piece of widget-related code:
/* Register the widget */ function agsml_register_widget() { register_widget( 'Antelope_Widget' ); }
The first piece here is super-easy, and it’s our first step in
creating and using a widget for our Antelope General Social Media Links
plugin. We’re just going to register our widget so that WordPress knows
we have a new one coming, and it’s called
Antelope_Widget
. Easy peasy.
Now we jump into step two of creating our widget, which is really step one of its own four-step process: defining the widget class functionality:
/* Begin Widget Class */ class Antelope_Widget extends WP_Widget { //* Widget setup */ function Antelope_Widget() { $widget_ops = array('classname' => 'agsml_widget', 'description' =>↵ __( 'Your Social Media Links', 'agsml') ); // The actual widget code goes here parent::WP_Widget( false, $name = 'AG Social Media Links', $widget_ops ); }
In the code, you can see that we begin by extending the
WP_Widget
object with the
Antelope_Widget
class, thus extending it and
creating a namespace for ourselves to work with. After that, we create a
basic localized array (note the double underscore wrapper that
encompasses the string 'Your Social Media Links'
and which denotes the unique domain namespace we’ve created earlier in
'agsml'
) and toss it all into a variable. Then we
utilize the widget API to create a new object for our actual widget,
which we’ll be able to find in the >
menu area within WordPress. Notice that
we have a label for our widget here ('AG Social Media
Links'
), and we’re also passing our array in as well, so we
have our object’s data handy for use.
The second piece of the widget class involves actually outputting the instance of a given widget to the browser:
/* Display the widget */ function widget( $args, $instance ) { //get widget arguments extract($args); //get widget title from instance variable $title = apply_filters('widget_title', $instance['title']); //insert before widget markup echo $before_widget; //if theres a title, echo it. if( $title ) echo $before_title . $title . $after_title; //start list $social_list .= '<ul>'; // define list if (get_option('agsml_facebook' )){ $social_list .= '<li class="facebook"><a href="'.↵ get_option('agsml_facebook').'">' . __('Friend us on Facebook', 'agsml') .↵ '</a></li>'; } if (get_option('agsml_twitter' )){ $social_list .= '<li class="twitter"><a href="'.↵ get_option('agsml_twitter').'">' . __('Follow us on Twitter', 'agsml') .↵ '</a></li>'; } if (get_option('agsml_linkedin' )){ $social_list .= '<li class="linkedin"><a href="'.↵ get_option('agsml_linkedin').'">' . __('Link us on LinkedIn', 'agsml') .↵ '</a></li>'; } if (get_option('agsml_youtube' )){ $social_list .= '<li class="youtube"><a href="'.↵ get_option('agsml_youtube').'">' . __('Watch us on Youtube', 'agsml') .↵ '</a></li>'; } // end list $share_content .= '</ul>'; //display assembled list echo $social_list; //insert before widget markup echo $after_widget; }
Because we’ve passed the object’s data to the instance of the
widget as just described, the first step is to extract the
$args
so that we can use them. The AGSocialMedia
widget has the capacity to create a custom title on an
instance-by-instance basis as we’ll see in a moment, so one of the first
items here is the extraction and sanitization of the title string from
our array, after which we drop it into the $title
variable.
You may notice that there are several variables sprinkled
throughout this code block that we aren’t defining:
$before_title
,
$after_title
,
$before_widget
, and
$after_widget
. These tags are provided
to you by the widget API, and are available to theme designers to
manipulate in different ways, so that they can add code to make their
websites look pretty. Make sure the tags stay as positioned, so those
designers avoid running into any unforeseen and unexpected
surprises.
Aside from that, the rest of this code is fairly self-explanatory. Because we’re outputting to the browser here, we once again have strings that are being localized in the same format as before; it doesn’t bear any more explanation, but when you are writing your own plugins, you will want to remember this important detail. Spread the WordPress love by localizing—have you noticed this as a recurring theme yet? We should make bumper stickers: “Be Wise and Localize!”
The third piece of the widget class is the nearest we have to a standardized component, and it’s very simple to see what’s happening:
/* Update the widget settings, just the title in this case */ function update( $new_instance, $old_instance ) { $instance = $old_instance; $instance['title'] = strip_tags($new_instance['title']); return $instance; }
All we are doing here is sanitizing the only input we have for our widget—in this case the customized title—and saving it as a new instance, replacing the old instance if it existed.
The final component of the widget class provides the logic for the form, which is necessary to update the title of the instance of the widget:
//form to display in widget settings. Allows user to set title of widget. function form( $instance ) { $title = esc_attr($instance['title']); ?> <p> <label for="<?php echo $this->get_field_id('title'), ?>"><?php _e('Title:'), ?></label> <input class="widefat" id="<?php echo $this->get_field_id('title'), ?>" name="<?php echo $this->get_field_name('title'), ?>" type="text" value="<?php echo $title; ?>" /> </p> <?php } }
Here we find a very simple form with just one label (that includes our now all-too-familiar localized string) and its corresponding input field, which autopopulates with the existing title, if one has been previously set.
The final touches on our Antelope General Social Media Links plugin are finally within our grasp:
/* Load the widget */ add_action( 'widgets_init', 'agsml_register_widget' ); ?>
Now all we need to do is use widgets_init
to
load up our agsml_register_widget()
function. After
this function fires, we’re home, and while most antelopes run out of
control, our antelope is running with all the controlled precision we
could possibly hope for!
While we’ve pieced together an entire plugin bit by bit, we’ve only begun to touch on what you can do with plugins. In truth, the only limit to what you can accomplish with a plugin is your imagination, as there are thousands of hooks to work with, and even more standardized tools that you can use to continue to push the envelope with WordPress. While it made no sense to include them within the context of the Antelope General Social Media Links plugin example, there are two pieces of functionality commonly implemented within plugins that we should cover: meta boxes and shortcodes. We won’t go into nearly as much depth on either piece of functionality, but we’ll look at them so that you’ll at least be able to use them. Let’s start with a basic discussion of meta boxes.
In many plugins, you’ll want to give your end users the ability to add information in a standardized way right on the page or post editing screen. One way to accomplish this is to utilize meta boxes, which we looked at back in the section called “Meta Boxes ” in Chapter 2. If you recall, meta boxes are customized dialog boxes you can insert on administrative editing screens, seen in Figure 5.4.
Meta boxes are added using a standard function that takes seven parameters, as shown below:
add_meta_box($id, $title, $callback, $page, $context, $priority, $callback_args)
Each of the parameters is defined as follows:
$title
(string): the title
displayed within the header of the meta box (required)
$callback
(string): the name
of the function that displays the meta box information
(required)
$page
(string): the type of
page that you want the meta box displayed on; for example,
post
, page
,
link
, or
custom_post_type
, where
custom_post_type
is the custom post type slug
(required)
$context
(string): the
specific area within the edit screen of where you want the meta
box displayed, such as normal
,
advanced
, or side
(optional)
$priority
(string): the
priority within the context where the meta box should be
displayed, such as high
,
core
, default
, or
low
(optional)
$callback_args
(array):
arguments to pass into your callback function (optional)
The add_meta_box()
function is
typically used in conjunction with the
admin_init
action hook, which you can
use to create your custom meta box within page types associated with
your plugin. While most of the parameters that
add_meta_box()
takes are self-explanatory, there’s
a couple of really cool ones that make this a particularly flexible
function. Notably, the $page
parameter queues up
the type of page that your meta box can be displayed on; because it ties
into custom post types, it gives you an additional level of control when
morphing WordPress into the specialized CMS you envision for your
website, as described in Chapter 4. The
$context
parameter is equally useful, giving you
control over exactly where that meta box will show up on the page type
editing screen. This is sexy stuff that lets you carve out WordPress to
make it look and function however you see fit.
Another extremely useful concept to dig into is shortcodes.
Shortcodes are essentially sanitized placeholders for PHP functions that
are either initialized by core WordPress, from within a plugin, or even
from within the functions.php file of your theme.
They can accept parameters that make them perform tasks, and are very
useful when you want to insert fairly complicated code into a page or
post without actually inserting that code. Instead, you can think of a
shortcode as a placeholder that WordPress will identify
when outputting your website, replacing it with the appropriate code
associated with the shortcode.
They are formatted with opening and closing brackets that
look like this: [my_super_awesome_shortcode]
.
Shortcodes are easy to create, and for the most part you can embed any functionality you want into them. WordPress gives us the following standard function to use when we want to create one:
add_shortcode( 'shortcode-name', 'shortcode-function-name' )
Here, the parameters are straightforward, with the name of the shortcode (the text we insert in our brackets) being defined within the first string parameter, and the associated function that calls the PHP function we’ll be executing where the shortcode is inserted in our page. Let’s take a look at a very simple example of a shortcode in action:
<?php function thank_you() { return 'You can feel good about Hood.'; } add_shortcode( 'mrminer', 'thank_you' ); ?>
Here, we have defined an extremely simple shortcode named
mrminer
, which makes reference to a function
called thank_you()
. While you can be extremely
creative and intricate with the functionality you’d like to introduce in
your shortcode, we’ve kept it very simple here to illustrate the
process. In our example, we can add the shortcode
[mrminer]
to any post or page, and it’ll print out
“You can feel good about Hood.”
in that
space.
A common mistake that many plugin developers make when
they are beginning to work with shortcodes is to try to
echo
the results of a shortcode, rather than
return
the response. Don’t sweat it, though … now
that you’ve read this tip, you’ll avoid this pitfall!
All right, so let’s say you’ve created a plugin and you want to give back to the community by sharing your creation with the world. One of the easiest and most effective ways of doing that is to submit it to the WordPress Plugin Directory on WordPress.org. There are several really cool things that result from listing your plugin in the directory, most notably that it instantly becomes accessible to anybody who’s running a WordPress installation. With just a few clicks and the right search, your plugin can come up in the plugin search and be added to anyone’s WordPress site in just a few minutes. Additionally, when you update your plugin inside the directory, your users will be immediately notified and prompted to upgrade from right within their WordPress admin back end … and that just feels so cool the first time you ever see it happen with one of your plugins. Finally, WordPress.org also gives you access to statistics, so you’ll be able to see exactly how many people have downloaded your plugin and the ratings they’ve given it, as well as view and respond to comments.
You’ll need to adhere to several blanket terms and conditions if you want your plugin to be listed in the directory, namely:
Your plugin should have a license that is GPL-compatible.
Your plugin can’t do anything illegal, or be morally offensive in any way.
You’ll need to host the plugin using the WordPress.org subversion repository.
You’ll need a valid readme.txt file for your plugin.
Your plugin can’t embed an external link to the author’s site without giving the user an option to easily remove it.
If you choose to submit your plugin to the WordPress Plugin Directory, it’s an easy process, even though it’s not immediate. You’ll need to be a WordPress.org registered user, and then you submit your plugin at http://www.WordPress.org/extend/plugins/add/. Upon adding your plugin, it will need to be reviewed and approved by the staff managing WordPress; it’s a process that can take some time, as it is manual.
Upon having your plugin approved, you will be given access to the WordPress.org Subversion repository, where you’ll commit the uncompressed plugin to the SVN repository, along with a valid readme.txt file that describes the information needed for listing a plugin in the directory. A sample readme.txt file can be found at http://wordpress.org/extend/plugins/about/readme.txt, and a readme validator that will help you determine whether you’ve added the required elements for a listing is available at http://www.WordPress.org/extend/plugins/about/validator/.
Few things are worse in the WordPress community than a poorly supported plugin, especially if it starts off with a head of steam and gains a following. Such plugins are one of the reasons why WordPress earned a bad name in the past, so we encourage you to give some thought to how comfortable you are in providing a reasonable level of support to others using your plugin. When you submit a plugin to the WordPress Plugin Directory, you really are making a loose, implied agreement to provide a basic level of support to others who may use your plugin; otherwise, releasing it into the directory makes little sense outside of satisfying your ego. Submitting your plugin to the directory is a choice that you as the developer can make—it’s not a requirement.
Plugins are powerful pieces of functionality, and are essential in extending the functionality of any WordPress website. Poorly coded or out-of-date plugins are one of the most common reasons why WordPress websites occasionally have problems, so the easiest place to begin troubleshooting is to turn off all your plugins.
If you are a developer, you can make WordPress do backflips with the creative use of plugins (all right, maybe not backflips, but you could probably make it order pizza for you). Mastering action hooks and filter hooks are essential to making your plugins hum, but you’ll need a solid foundation in PHP to make anything really go. Just remember, if you can dream it, you can do it with plugins in WordPress!