Creating a Plugin from Scratch

Links:

Project files: GitHub | download zip
Completed project files: GitHub | download zip


What is a plugin?

WordPress is a powerful Content Management System (CMS) but without the Plugin architecture added in WordPress 1.2 (“Mingus”), it is little more than a blogging platform. Using WordPress Actions and Filters, plugins can be used to customize any and every aspect of a WordPress site.

So what is a plugin? A plugin is a file or group of files that customize or add functionality to your WordPress site.

Plugin Uses

So what are some of the uses for plugins? Let’s look at a few of the most popular WordPress plugins and see what they do:

  • WordPress SEO by Yoast – Optimizes your site for search engines by adding post meta and author information to each page.
  • Gravity Forms – A complete form solution adding functionality for everything from surveys, to e-commerce, to newsletter signups.
  • MailPoet Newsletter – Send posts or post digests out as newsletters to subscribers.

Commercial, project, .org

Plugins also come in several different flavors. There are over 31,000 free plugins available in the WordPress.org plugin repository. Other free plugins can be found on GitHub, Bitbucket, and other places on the web. There are countless premium plugins like Gravity Forms. Finally, there are custom project plugins developed specifically for a certain client or website.

Plugins can be used to extend WordPress functionality and even other plugin functionality.

Plugin functionality v. theme functionality

So when should you create a custom plugin and when should you just add functionality to the themes functions.php file? Good question. If you are not working on a custom built theme (including a child theme) then NEVER modify the functions.php file. This is for the same reason that you should never modify WordPress core files; once you update the theme, all that custom functionality will be overwritten.

If you are working on a custom theme or child theme then the line is a little blurred in my opinion. There are some who will say that functionality should never be included in a theme, that presentation and functionality should be kept separate. For most simple site builds I tend to agree. The advantage of keeping functionality in a plugin instead of a theme is that the theme can then be changed to update the look (I know people do that sometimes, right?) without losing the custom functionality.

There are those times though, that you might be building a very complex custom theme where separating custom features into plugins would just create a huge mess.

Here’s the breakdown:

Reasons to separate functionality into a plugin

  • Portability – The theme can be changed without modifying functionality.
  • Flexibility – Reuse common features across multiple projects.
  • Cleaner Code – Separate presentation from functionality.

Reasons to keep functionality in a theme

  • Manageability – If custom theme is built around a specific feature, it probably makes more sense to include the feature as part of the theme.
  • Purpose – The purpose of the feature is not clearly defined outside the scope of the theme.

This being said, functionality included in a theme should still be separated into functionality files. I like to create a folder called includes and put all functionality there.

Getting Started – Jetpack Awesome Icons

Alright, now that we have all of that out of the way, let’s get down to building our plugin!

Plugin Meta:

The bar for creating a plugin is actually very low. Technically a plugin only requires the following information in a file located in the plugins directory.


<?php
/**
 * Plugin Name: My Awesome Plugin
 */

So let’s do that. Pull down the blank project files and place them into a folder called “jetpack-awesome-icons” located in the plugins directory. Ignore the assets folder for now, we will be using it later but it is not doing anything at the moment.

Open up the empty file jetpack-awesome-icons.php and input the following code:


<?php
/**
 * Plugin Name: Jetpack Awesome Icons
 */

If you log into your development site and view your available plugins, you will now see Jetpack Awesome Icons listed as an available plugin.

Congrats! You have just created a plugin!!! Now let’s add some more information to our plugin. Add the following information to the meta section of the plugin file, replacing my information with yours:


<?php
/**
 * Plugin Name: My Awesome Plugin
 * Plugin URI: https://github.com/tannerm/jetpack-awesome-icons
 * Description: Use FontAwesome icons instead of Jetpack icons for sharing buttons
 * Version: 0.1.0
 * Author: Tanner Moushey
 * Author URI: https://tannermoushey.com
 * License: GPL2
 */

Perfect! Now refresh the plugins page on your local WordPress install and you will see the new meta appear by the plugin name.

Finally, let’s activate the plugin. It won’t do anything yet, but that doesn’t matter. We’ll work on that soon.

Define Plugin Constants

You will find it helpful to define a few constants for use within our plugin. We will define constants for:

  • Plugin Url – used for enqueueing scripts and styles.
  • Plugin Path – used for including other .php files
  • Plugin Version – used to control file versions across the plugin.

Add the following lines to the plugin file underneath the meta:


define( 'JAI_URL',     plugin_dir_url( __FILE__ )  );
define( 'JAI_PATH',    plugin_dir_path( __FILE__ ) );
define( 'JAI_VERSION', '0.1.0'                     );

Note: We are prefixing our constants with “JAI” to make sure that we don’t cause compatibility issues with other plugins. “JAI” for Jetpack Awesome Icons. 🙂

Intro to actions and filters

WordPress action and filter hooks allow us to customize the way that WordPress and other plugins and themes work.

Actions

Actions allow us to trigger functions to run at certain points during the code execution process. We find one example of an action definition in wp-settings.php on line 236:


do_action( 'plugins_loaded' );

We will be hooking into this action to initialize our functionality. So underneath our constant definitions, add the following code:


function jai_init() {}
add_action( 'plugins_loaded', 'jai_init' );

Here we are using the WordPress function add_action to run our custom function jai_init each time the plugins_loaded action is triggered.

Note: When defining functions within our plugin it is important to use a prefix to make sure that we don’t have compatibility issues with WordPress or other plugins.

Filters

Filters are very similar to actions in that they are triggered at a certain point in the code execution. The difference is that filters pass a variable that we can modify in our function.

On line 589 of class.jetpack.php in the Jetpack plugin we find this filter defined:


return apply_filters( 'jetpack_development_mode', $development_mode );

This filter is used to control whether or not Jetpack is in development mode. Since we are developing our plugin locally, we want to make sure that Jetpack is in “Development Mode” so that we can have access to the sharing buttons without having to connect Jetpack to WordPress.com. So add the following function to jai_init:


add_filter( 'jetpack_development_mode', '__return_true' );

So jai_init should now look like this:


function jai_init() {
    add_filter( 'jetpack_development_mode', '__return_true' );
}
add_action( 'plugins_loaded', 'jai_init' );

__return_true is a special WordPress function that always returns true. It is helpful in situations like this so that we don’t have to define our own function that just returns true.

Note: One thing to remember about actions and filters is that they must be hooked into before they run. We are using plugins_loaded for our init function because it is one of the first actions to run and we can be sure that any action or filter we hook into from here will be run after we hook into it. Here is a link to the order that WordPress actions are triggered.

Perfect! Now our plugin is actually doing something! At this point, if our plugin was called “Jetpack Development Mode” we’d be done. It’s not though, and this is just the beginning. Let’s checkout that assets folder now and see if we can customize Jetpack’s sharing icons.

Including Assets

If you open up the assets folder you will notice that I have already created a stylesheet and included the FontAwesome font. I’m sure you are fully capable of doing these steps on your own, but I figured for sake of time I’d get this time consuming stuff out of the way.

Technically there are a few ways to enqueue these assets into the page header. If you guessed that we should hook into the wp_head action and echo out the markup for the resources you would get some huge brownie points. But you’d be wrong. 😉 Please do not echo markup for stylesheets or scripts into the head elements of the page. This goes for both plugin and theme development and is one of the most common mistakes that I see developers make.

WordPress has a special way to include page assets and it uses a function called wp_enqueue_script and wp_enqueue_style respectively. These functions should be included when the wp_enqueue_scripts action is run.

So now create a new function called “jai_enqueue_scripts” and put the following code into it:


function jai_enqueue_scripts() {
    wp_enqueue_style( 'jai-awesome', JAI_URL . '/assets/css/font-awesome.min.css', array(), JAI_VERSION );
    wp_enqueue_style( 'jai-icons', JAI_URL . '/assets/css/icon-styles.css', array( 'jai-awesome' ), JAI_VERSION );
}

And then in the jai_init function add this:


 add_action( 'wp_enqueue_scripts', 'jai_enqueue_scripts' );

This will run our jai_enqueue_scripts when wp_enqueue_scripts is triggered.

Now load a page on your local site that has sharing buttons on it and check out the cool new icons that we just customized. Nicely done!

Getting Fancy

Alright, sweet! So we now have a plugin that actually does something. When active, the plugin customizes the Jetpack icons with our custom CSS and FontAwesome. But what if we decide we don’t want the icons customized? We have to deactivate the plugin. We can do better.

Custom Settings

Let’s add a checkbox on the Jetpack Sharing option page that will allow us to turn on and off the custom icons.

The settings for the Jetpack Sharing icons are created in sharing.php (/jetpack/modules/sharedaddy/sharing.php) and on line 352 is an action called “sharing_global_options” that we can hook into to create our own option. In our jai_init function, add the following code:


 add_action( 'sharing_global_options', 'jai_enable_custom_icons' );

Now let’s go create our callback function for this action.


function jai_enable_custom_icons() {}

Now, time to write some HTML. Generally it is a good idea to separate PHP from HTML as much as possible. What I like to do is create a separate folder called “views”. Here we can store the different HTML snippets we’ll use in the plugin and include them in the function callbacks.

So create a folder called “views” and then create a file in it called “enable_custom_icons.php” (while we are writing HTML here, we will still need to process some PHP). Finally, input this code:


<tr valign="top">
    <th scope="row"><label>Use FontAwesome Icons?</label></th>
    <td>
        <input type="checkbox" name="jai-enable" <?php checked( get_option( 'jai_enabled' ) ); ?>>
        <small><em>Use custom FontAwesome icons?</em></small>
        <?php wp_nonce_field( 'jai-enable', 'jai_enable_nonce' ); ?>
    </td>
</tr>

Great! A couple of functions I want to highlight here.

The first is checked. This function will print out the appropriate attributes to check a checkbox based on the parameters passed. We have passed it the option that will store whether or not the plugin is enabled. If so, the option will be true and the checked parameters will be printed out, if not, it will do nothing.

The second is wp_nonce_field. This field is a little more complicated and is essentially it is a security check. It prints out a hidden input box that we can check when the field is submitted to verify that this is a valid submission.

Finally, we’ll include this file in our action callback which should now look like this:


function jai_enable_custom_icons() {
    include_once( JAI_PATH . 'views/enable_custom_icons.php' );
}

Super! We now have our own checkbox in the Jetpack Sharing settings. Now let’s add the processing script.

Process Custom Settings

Jetpack has another action that it triggers when it updates it sharing settings. This action is called “sharing_admin_update” and we’ll hook into it to process our custom setting field.

Add this action to our jai_init function:


 add_action( 'sharing_admin_update', 'jai_admin_init' );

Now let’s create the callback function:


function jai_admin_init() {
    if ( empty( $_POST['jai_enable_nonce'] ) ) {
        return;
    }

    if ( ! wp_verify_nonce( $_POST['jai_enable_nonce'], 'jai-enable' ) ) {
        return;
    }

    update_option( 'jai_enabled', isset( $_POST['jai-enable'] ) );
}

Let’s walk through this a little bit. The first conditional checks to make sure that the form submission even has our custom field by checking for the nonce. If it doesn’t, it exits. The second conditional validates the nonce field to make sure that this is a valid submission. Finally, we create (or update) a custom option in the database that will store whether or not our custom icon styles should be used.

To make this affective, now let’s go back to our function where we enqueue the styles and make sure that our setting is enabled. Add this code to the top of the jai_enqueue_scripts function:


if ( ! apply_filters( 'jai_enabled', get_option( 'jai_enabled' ) ) ) {
    return;
}

Here we are creating our own filter that others can use to customize the functionality of this plugin. Say someone wants to conditionally enable the custom icons based on some page meta. Now they can hook into our jai_enabled filter and pass true or false by their own criteria.

So, now our jai_enqueue_scripts should look like this:


function jai_enqueue_scripts() {
    if ( ! apply_filters( 'jai_enabled', get_option( 'jai_enabled' ) ) ) {
        return;
    }
    wp_enqueue_style( 'jai-awesome', JAI_URL . '/assets/css/font-awesome.min.css', array(), JAI_VERSION );
    wp_enqueue_style( 'jai-icons', JAI_URL . '/assets/css/icon-styles.css', array( 'jai-awesome' ), JAI_VERSION );
}

Done! Check the setting field that we just created to see if the enable/disable feature works.

Frosting

The final touch here is to get the icon previews in the backend to match the icons in the front end. The wp_enqueue_scripts actions that we are using to enqueue our custom styles only runs on the front end, so we will need to find the correct action to hook into to apply the styles on the backend as well.

Fortunately, Jetpack has this hook for this called “load-settings_page_sharing”. Let’s add that to our jai_init function like this:


add_action( 'load-settings_page_sharing', 'jai_enqueue_scripts' );

So finally our jai_init function should look like this:


function jai_init() {
    add_filter( 'jetpack_development_mode', '__return_true' );
    add_action( 'wp_enqueue_scripts',         'jai_enqueue_scripts'     );
    add_action( 'load-settings_page_sharing', 'jai_enqueue_scripts'     );
    add_action( 'sharing_global_options',     'jai_enable_custom_icons' );
    add_action( 'sharing_admin_update',       'jai_admin_init'          );
}
add_action( 'plugins_loaded', 'jai_init' );

Awesome work! You have just completed a very functional, flexible, secure, and scalable plugin!

There is a lot of information here and although we covered a whole lot, there were some things we couldn’t get to. If you have any questions or comments, feel free to post it below, I’d love to hear from you!

2 comments

Leave a Reply