Chapter 5
Plugins

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.

The Basics

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.

The Upside to Plugins

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.

The WordPress Plugin Directory

Figure 5.1. 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 Upload 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.

The Downside to Plugins

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.

Rules to Follow When Using Plugins

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:

Only use what you need

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.

Deactivate or delete what’s not being used

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.

Must-use Plugins

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.

Note: Deactivation Can Be a Solution

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.

Warning: Zip It

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.

Drop-in Plugins

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

Drop-in Plugin NameDescriptionContext
advanced-cache.php Advanced caching pluginSingle
db.php Custom database classSingle
db-error.php Custom database error messageSingle
install.php Custom installation scriptSingle
maintenance.php Custom maintenance messageSingle
object-cache.php External object cacheSingle
sunrise.php Advanced domain mapping Multisite
blog-deleted.php Custom blog deleted messageMultisite
blog-inactive.php Custom blog inactive messageMultisite
blog-suspended.php Custom blog suspended messageMultisite

Determining When to Create a New Plugin

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!

Note: Can I see your license?

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.

Debugging Your Plugin As You Go

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!

The Anatomy of a Plugin

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.

Standard Plugin Packaging

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:

chapter_05/standard-header.php
<?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.

The Manage Plugins screen

Figure 5.2. The Manage Plugins screen

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:

chapter_05/standard-gpl-license.php
<?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.

Action Hooks and Filter Hooks

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:

action-example.php
<?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

  • wp_footer—triggered in the footer 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

  • user_register—triggered whenever a new user is registered

  • publish_post—triggered whenever a new post is published

  • comment_post—triggered whenever a new comment is posted

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:

chapter_05/filter-example.php
<?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.

Note: You Say Po-TAY-to, I Say Po-TAH-to

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.

The Power of Paranoia: Data Validation

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.

WordPress escaping API

Figure 5.3. WordPress escaping API

In this diagram, we see that there are three components to the function set, as described following:

  1. esc: the prefix for the escaping function

  2. attr: the context being escaped, with possible values including:

    • attr—HTML attribute scrubbing

    • jsJavaScript scrubbing

    • html: HTML character scrubbing such as <, >, ', and "

    • sql MySQL query scrubbing

    • urlURL scrubbing

    • url_raw: URL scrubbing before saving to a database

  3. _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.

Important: Leave It Till Last

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!

Dissecting a Plugin: Antelope General Social Media Links

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 Settings link in the plugin management screen that takes us directly to the aforementioned plugin settings page

  • add 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.

Note: Keeping Our Eye on the Prize

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:

chapter_05/AGSocialMedia/antelope-social-media-links.php
<?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.

Header and License

Let’s take a look at that first chunk of code:

chapter_05/AGSocialMedia/agsml_header_license.php
<?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.

Important: Version Control

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.

Localization Settings

Next comes the code for our localization settings:

chapter_05/AGSocialMedia/agsml_localization.php
//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.

Creating the Menu Item for the Settings Page

Now matters become interesting. Let’s look at this next code block and break it down into pieces:

chapter_05/AGSocialMedia/agsml_createadmin.php
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:

chapter_05/AGSocialMedia/agsml_createadmin1.php
                  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 Admin menu run 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.

Note: It’s a Wrap

WordPress provides easy wrapper functions that allow developers to add sublevel menu items to the primary top-level administrative menu items such as Dashboard, Posts, Media, Appearance, Settings, and so on. For more information as to how to add them, have a look at the WordPress Codex.

The 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:

chapter_05/AGSocialMedia/agsml_createadmin2.php
//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:

chapter_05/AGSocialMedia/agsml_createadmin3.php
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?

Styling the Admin Screen

Check out the CSS file in the AGSocialMedia folder:

chapter_05/AGSocialMedia/agsml-widget.css
/* 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.

Formatting for the Settings Page

Following this comes a chunk of form-building HTML:

chapter_05/AGSocialMedia/agsml_settingspage.php
//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 Admin 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.

Getting Output Styles Ready

This next piece of code is an example of the proper and safe way to insert an external CSS sheet into your plugin:

chapter_05/AGSocialMedia/agsml_enqueuestyles.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' );

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.

Important: Don’t Force Your Design on Users

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.

Widgets 101

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:

  1. registering your widget using the register_widget() function

  2. defining the widget via the WordPress-provided widget class, which we’ll touch on more in a moment

  3. loading the widget via the widgets_init action hook

To further understand how widgets work, let’s look briefly at the basic structure of the widget class in WordPress:

chapter_05/AGSocialMedia/widgetclass.php
<?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

Important: Widgets Extend Classes

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.

Registering Our Antelope General Widget

Check out our first piece of widget-related code:

chapter_05/AGSocialMedia/agsml_registerwidget.php
/* 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.

Define What the Widget Should Do

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:

chapter_05/AGSocialMedia/agsml_widgetlogic.php
/* 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 Appearance > Widgets 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.

Display Logic

The second piece of the widget class involves actually outputting the instance of a given widget to the browser:

chapter_05/AGSocialMedia/agsml_widgetdisplay.php
/* 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!”

Updating the Instance of the Widget

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:

chapter_05/AGSocialMedia/agsml_widgetupdate.php
/* 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.

Creating the Form to Change the Title

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:

chapter_05/AGSocialMedia/agsml_widgetform.php
//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.

Load Our Widget into WordPress

The final touches on our Antelope General Social Media Links plugin are finally within our grasp:

chapter_05/AGSocialMedia/agsml_loadwidget.php
/* 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!

Taking Plugins Further

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.

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.

Custom meta boxes

Figure 5.4. Custom meta boxes

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:

  • $id (string): CSS id attribute for the meta box (required)

  • $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.

Shortcodes

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:

chapter_05/shortcode-example.php
<?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.

Tip: Return, Don’t Echo

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!

The WordPress Plugin Directory

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/.

Note: Some Food for Thought

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.

Plug In All the Way

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!

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset