Create a License Controlled Update System: Doing the Update

In the first two tutorials in the series, we have built a WordPress plugin for managing software licenses. In part 1, we built the administration tools needed for adding products and licenses, and in part 2, we created an API that plugins and themes can use to access the license and product information. 

Now, it's time to tie it all together and make a WordPress theme and a WordPress plugin use the license server to check for updates. To do this, we will create a class that can be dropped into any WordPress plugin or theme's codebase to make the theme or plugin use a WordPress site running our WP License Manager plugin to check for updates. 

In this tutorial, you will learn about the following topics: 

  • How to hook into the WordPress theme update system.
  • How to hook into the WordPress plugin update system (yes, the two are separate—but similar—and need to be addressed one by one).
  • How to call an external API from WordPress.

The functionality we will create in this tutorial requires us to dig deeper into how WordPress works under its hood, which makes this tutorial a bit more advanced than the first two in the series. However, we will go through every step thoroughly, so if you have been following the tutorial this far, there should be no problem finishing this one.

Just like in the previous parts, you can follow the tutorial writing your own code as we go, or download the final class and use it as a reference.

Let's get started.

1. Setting Up the Testing Environment

To build the license manager client class and to test it, you need a testing environment with the following components:

  • A WordPress server running the WP License Manager plugin created in parts 1 and 2 of the tutorial series. We'll call it the license server.
  • A WordPress server for running the plugin and theme to be updated. We'll call it the test server.
  • A theme for testing the update system.
  • A plugin for testing the update system.

You can use one of your existing themes and plugins for testing, or create a placeholder with no real content or functionality other than the updates, following the instructions below.

Step 1: Create a WordPress Theme for Testing

In wp-content/themes, create a new directory called hello-world-theme (for example).

In that directory, add a style.css file with the standard theme heading. This is needed for WordPress to find the theme. 

Add an index.php file. It can be empty, or you can use the following placeholder HTML:

For testing the license manager client class, we still need a functions.php file. This is where you'll initialize the class we will create in this tutorial—for now, you can leave the file empty.

With this minimum setup, WordPress will accept the theme without errors. Activate it now.

Once you have decided on the theme to use, add a product to represent it on the license server. Call the new product "Hello World Theme" and set its version number to something higher than the 0.1 we defined in the theme header above. 

Mark down the "post slug" part of the product's permalink (hello-world-theme) as it will be used as the product's ID when talking to the license manager API. 

Step 2: Create a WordPress Plugin for Testing

Creating an empty test plugin is even faster than creating a test theme. All you need is a PHP file with a bare-bones plugin header. 

In wp-content/plugins, create a directory called hello-world-plugin and inside it, a file, hello-world.php, with the following content:

The plugin is now ready so go ahead and activate it.

Just like you did for the theme, create a product for this plugin on the license manager server. Call the product "Hello World Plugin"—this should give you the id (slug) hello-world-plugin—and set its version number to something higher than the 0.1 defined in the plugin header.

Now that you have set up your testing environment and have a plugin and a theme ready for testing, it's time to get started with the license manager client class itself. 

2. Setting Up the License Manager Client Class

Let's start creating the License Manager Client class by building the common functionality that is shared by both themes and plugins. As we need to test the code in some context, we'll start with the theme and add plugin specific stuff once the theme updates are working.

Step 1: Create the License Manager Client Class and Initialize It

In your theme directory (hello-world-theme), add a new class called Wp_License_Manager_Client (using the file name class-wp-license-manager-client.php). This class will hold all the code related to checking the licenses and handling the theme's updates.

As this class file is meant to be included in any number of WordPress themes and plugins, surround it with a class_exists check. This way, if there are many installed plugins and themes that use this class, they won't break the system by trying to create the class many times.

Let's continue with some initialization before moving to actual functionality.

First, add some variables that will be initialized in the class's constructor. The code comments explain the variables, and I will mention them again when we use them in the code.

Then, add a constructor for setting these instance variables:

To better understand what these parameters are used for, and to link the class to your theme, open your theme's functions.php file and create an instance of the Wp_License_Manager_Client class:

Let's go through the code line by line:

Line 2: Include the license manager class we are about to create.

Line 4: The license manager client is only needed in admin screens, so in order to not create unnecessary objects, check for whether the user is viewing one of WordPress's admin screens or not.

Lines 5–9: Create the Wp_License_Manager_Client instance using the following parameter values:

  • hello-world-theme: The slug (id) of the product (theme) on the license server.
  • Hello World Theme: The name of the theme. It will be used for settings screens and other situations where we need to refer to the product by its name.
  • hello-world-text: The text domain your theme uses for localization. Using the same text domain for the license management screens makes it easier for localizers to translate the entire theme at once.
  • http://<URL_TO_LICENSE_SERVER>/api/license-manager/v1: This is the URL to the API we created in the previous part of the tutorial series. Replace URL_TO_LICENSE_SERVER with the URL to the WordPress site on which you installed the WP License Manager plugin.

The remaining two parameters in the constructor are only needed when the class is used with a plugin, so we'll leave them to their default values for now and come back to them later in the tutorial when we move to plugin updates.

Step 2: Create a Settings Screen for Entering the License Key

The first thing a user needs to be able to use the license manager API is a valid license key—and a way to enter it into the theme. 

For this, we will now create a new options screen. 

As we have already created a number of similar screens in parts 1 and 2 of the tutorial series, I will go through the code only briefly.

Notice that every piece of functionality that goes into the license manager client class should be activated by attaching one of the class's functions to a WordPress action or filter. This way, everything stays clean and easy to manage. To keep things as simple as possible for the theme developer who will be using this class, I have put all the initialization in the class's constructor.

Add the settings screen related hooks to the constructor, right below the initialization code we added in the previous step:

Then, create the two functions mentioned, add_license_settings_page and add_license_settings_fields. Notice how we use the $product_name and $text_domain variables from the constructor when defining the title for the settings page.

Both functions created above define a number of callbacks for rendering the settings page and the settings fields. Add the callback functions:

To minimize the risk of a typo when typing the settings field's name, I have created a helper function, get_settings_field_name, that is used throughout the code snippets above. Another helper function, get_settings_page_slug, is used for defining the path to the settings screen.

Add those two function at the end of the class to make the code run without errors:

The functions use the $product_id passed in the constructor to create the settings field and the settings page slug. This way, every theme or plugin using the class will get their own settings and there will be no conflicts.

The settings page is now ready and looks like this:

License settings screen

Step 3: Add a Reminder For the User

It's easy for a user to miss a setting like this and end up wondering why the theme isn't getting updates. That's why it's a good idea to create a reminder that will be shown in the admin, until the user configures the theme and enters the license key.

It's time for another WordPress action. The admin_notices action is used for all WordPress notifications and errors, therefore fitting our purposes perfectly.

In the constructor, right below the two actions we added earlier, add:

Then, add the function, show_admin_notices:

The function first checks if the options have been set. If the options item doesn't exist or any of the two required fields are empty, it shows the notice and a link to the theme's license settings page where the user can complete the setup. The class, update-nag, defines the look of the notification.

Here's what the "nag" looks like, appearing at the top of the admin dashboard:

Reminder for updating the license settings

Now, to make the nag go away, let's put in the license information. 

First, visit the license server's WordPress admin area and create a new license for the test theme (Hello World Theme). After you have clicked on Add License, you will see the list of licenses. 

Copy the license key that was generated by the license manager plugin. Then, go back to your test site and configure the theme's license settings by entering your email address and the license key copied from the license manager server.

Your nag is gone and your theme settings are in place for testing the functionality we will create next.

3. Communicating With the License Manager API

With the basic framework in place, let's look at communicating with our license manager API.

Step 1: Add Functions for Calling the API

First, add a function for calling the License Manager API. The function takes two parameters: the API method or action to call on the license manager server, and an array with parameters for the action:

Line 13: Create the URL to call, using the base URL passed as a constructor parameter ($api_endpoint), and the action passed in as a parameter ($method).

Line 16: Use the PHP function http_build_query to create a URL encoded parameter string and append it to the GET request.

Line 19: Use WordPress's built-in HTTP function wp_remote_get to send a GET request to the URL generated on the previous lines.

Lines 20–22: Do some basic error handling to see if an error occurred. We'll add the is_api_error function next.

Lines 24–25: Read the response and parse it from a JSON encoded string to an array containing the data received from the license server. 

Line 27: Return the response.

Next, add the error checking function. The handling is very rough and doesn't go into any detail about what went wrong—just enough to know if there was an error or not:

Step 2: Add Functions for Retrieving License and Update Information

Now that we have created the function for talking with the API, we can use it to call the license server to get some information about the current theme or plugin.

Add the following function for calling the info action:

Line 8: Retrieve the license manager client's options.

Lines 9–12: Verify that the license key and email options have been set. If we don't have them, calling the API will be a waste of time—we already know that the request will fail.

Lines 14–21: Call the license manager API's info action with the following parameters:

  • p: The product ID defined in the Wp_License_Manager_Client constructor.
  • e: The license owner's email address, read from settings.
  • l: The license key, read from settings.

Line 23: Return the license data. The data will include the following fields:

  • name: The name of the product (theme or plugin).
  • description: A description of the product.
  • version: Current version available on the server.
  • package_url: The download URL for the product. This is a URL to the get API action on the license manager server.
  • last_updated: When the product has been updated.
  • description_url: A URL to a page that can be used for showing more information about the product.
  • tested: The highest WordPress version on which the product has been tested (needed only for plugins).
  • banner_low: Low resolution (regular) version of the product banner image (only needed for plugins).
  • banner_high: High resolution (retina) version of the product banner image (only needed for plugins).

Next, let's use the function to check if there is an update waiting to be downloaded. Add the following function:

Line 8: Request product information using the get_license_info function you just created.

Lines 9–11: If an error occurred while making the API call, return false. It would also be a good idea to show the error to the user—to keep things simple, I left that functionality out for now.

Line 13: Use PHP's version_compare to see if the version number received from the server is higher than the local version number.

Line 14: If an update is available, return the license data. By returning the data right away, we save ourselves the need to make an extra API call to get the data again.

To complete this function, we still need to implement the function for retrieving the local version number, used on line 13. Plugin and theme data are accessed a little differently, so the function will have separate implementations for both:

Line 2: Get the current theme's data.

Line 3: Read the current version and return it.

Line 5: Plugin data is read using a different function, get_plugin_data, that uses the plugin's main file name as the identifier. We will set the variable, $this->plugin_file, later when integrating to our test plugin.

Line 6: Return the plugin's version number.

Add the function is_theme for checking for the type of the product inside which we are operating:

We have now created the functions needed for connecting to the license server and checking if there is a newer version available. Next, it's time for the really interesting part: tying this to the WordPress update functionality.

4. How WordPress Checks for Updates

The framework for building our theme and plugin update system is in place. It's time to start digging deeper into the WordPress update functionality. 

At the admin_init action (and a number of other, more specific, actions), if enough time has passed since the last update check, WordPress compares its plugins and themes against the versions hosted in the WordPress.org Plugin and Theme directories to see if new versions have been released. 

After the check, WordPress stores the results into a site transient: update_themes for themes and update_plugins for plugins.

Then, when you visit the Updates page (or the Themes and Plugins pages), WordPress checks these transients to see and mark themes and plugins that have updates available.

When you look at the code for this functionality (which you can find in wp-includes/update.php) you can see that it has been designed to work with the official directories and nothing else: there are no hooks for specifying the server to check against or for sniffing the requests before they are sent. 

But that hasn't stopped plugin and theme developers from using their own license servers—and it's not going to stop us either.

The solution lies in the way the results of the update check are saved. I mentioned above that the results of the update lookup are stored in a transient. And right at the beginning of the WordPress function set_site_transient, we find:

This filter gives us access to the contents of the plugin and theme update data before it is saved, just in time to add our own data to it!

5. Hooking Into WordPress Theme Updates

Let's begin implementing our own update checking code, starting from themes.

Step 1: Create a Filter to Hook Into the Theme Update Check 

As we saw above, when WordPress has finished checking for theme updates from the WordPress.org theme directory, it stores the information about themes in need of an update in a transient called update_themes

To check for our own updates and add the data into the transient before it is saved, we will hook a function of our own to the filter, pre_set_site_transient_update_themes. In this function, we will call the license server to check for this theme's updates, and then, if there is an update available, add the information in the transient.

First, add the filter at the end of Wp_License_Manager_Client's constructor:

Then, create the function, check_for_update:

Lines 10–13$transient->checked is an array with all currently installed themes and their version numbers (theme stylesheet as key and version as value). If you were to check updates for multiple themes at once, you could use this array to collect the data and send it all to your license server. In this simple version, however, we just verify that the array isn't empty and move on.

Lines 15–16: Check if there is an update available for this product (theme) on our license server, using the is_update_available function we created earlier. The function returns the product information if the version on the license server is higher than the installed one and false if there are no updates available.

Line 18: Check if this is a theme or a plugin. At this point, we'll only focus on the theme updates, so I have left the else branch empty for now.

Lines 20–21: Find the theme's "slug" to use it as a key for marking the current theme in need of update.

Lines 23–27: Insert the information related to the theme update into the transient's $response array, using the theme's slug as key. 

  • new_version: Theme version on server.
  • package: The URL for downloading the theme package. As WordPress needs to be able to download the theme directly from this URL, we created the license manager to return a fully formed URL in the license information, including the user's email address and license key.
  • url: A URL for a page to show information about the theme. This parameter is used when clicking for more information on the Themes page.

Line 33: Return the transient so that WordPress can save it—with our theme update information appended to it.

And that's it! You have now built a complete update system for your self-hosted WordPress themes. Let's test the functionality and then move on to updating plugins.

Step 2: Test the Theme Update

Before moving on to updating plugins, let's test the functionality to see the update in action. 

Updating the theme you are using for testing will overwrite the theme, so as the first thing, create a new zip file using its files. Then upload the zip file to Amazon S3 as we did in part 2 of the tutorial series.

Next, log in to your license server and edit the Hello World Theme's product information, making sure it uses the zip file you just uploaded to Amazon S3 and that the product's version number is higher than the one of the theme installed locally.

Edit product information

Save the changes and head back to the test server to test the update.

On the test server, click on Dashboard > Updates. WordPress will now perform the API calls to check if there are themes in need of an update. If nothing happens, it could be that the previous check has just taken place and WordPress is still waiting for the time limit in between version checks to pass. In that case, wait for a minute and click on Check Again to force the check.

Now, you should see your theme in the list of themes that can be updated.

Hello World Theme is in need of an update

Next, try doing the update. If all goes well — as it should — you will see the following output. By default, the details are not show unless there is an error, so you'll have to click on Show Details to see what happened in the update.

Notice how the theme is not downloaded from WordPress.org but rather your own license manager server.

Theme update

6. Hooking to WordPress Plugin Updates

With the theme updates done and tested, we are almost finished with the tutorial. A big part of the code that will be needed for updating plugins is already in place. But there are some differences and additions needed, so let's get to work and add support for plugins to our license manager client class.

Step 1: Include the Client Class in Your Plugin

Copy the file class-wp-license-manager-client.php over to your test plugin's source folder. Then, in a suitable place in the plugin, place the following initialization code. 

If you are using the empty test plugin, just drop it into the main plugin file, right below the plugin header. Remember to replace <URL_TO_LICENSE_SERVER> with your own server URL.

Most of the parameters are the same as when initializing the class for use with a theme—with different, product specific values (e.g. hello-world-plugin instead of hello-world-theme).

The last two parameters were not used when creating a theme, so let's take a look at them: 

  • $type (with value plugin) tells the Wp_License_Manager_Client instance to use the plugin specific functionality.
  • $plugin_file (last parameter) is used for retrieving the plugin's identifier for requesting plugin data such as its current version. When this initialization is done in the plugin's main class, we can use __FILE__.

Step 2: Check for Plugin Updates

Earlier in this tutorial, we hooked our Wp_License_Manager_Client class to look for changes in the update_themes transient by placing an add_filter row at the end of the constructor. Plugin updates will be handled similarly, only using the transient update_plugins instead of update_themes.

So, right after the existing filter, add the plugin specific code (lines 1–3 were already present in the constructor, so I'm including them here to show where the new code should go):

As you notice from the code above, the new filter uses the same function as the one we added for themes, check_for_update.

Earlier, when we created the check_for_update function, we left one else branch empty, waiting to be filled with the code for handling plugin updates. Now, let's add content to that section. Here's the entire function with the plugin-related code added starting from line 27:

Looking at the code, it's good to notice that while the function is the same, now we're handling a different transient, update_plugins rather than update_themes. One way this shows is that while the data in update_themes is stored as an array, update_plugins uses an object (line 30)!

The calls to our License Manager API are the same as when using a theme, but the data stored to the transient is a little different—based on what WordPress expects to find in the transient.

With this code in place, you can already test for updates in the same way we did with the theme (create a zip file, upload it to S3, edit product properties). Make sure the plugin's version number on the license server is higher than the installed one, and navigate to the test server's Updates page. An update for the Hello World Plugin should appear:

update for the Hello World Plugin appears

When you update the plugin, you will see that it is downloaded from your own server just like the theme. 

But we are not quite done yet: If you look at the plugin information in the screen shot above, you'll notice that "Compatibility with WordPress 4.0.1" says "Unknown". Also, if you click on View version 0.2 details, an error message is shown.

Let's fix this.

Step 3: Provide Plugin Information

To collect the information it shows about plugins, WordPress uses a function called plugins_api. By default, the function calls the WordPress.org API, just like the version checks. However, unlike the version check, this function contains three powerful filters: plugins_api_args for editing the parameters sent to the API, plugins_api for overriding the default API requests, and plugins_api_result for editing the results received from the API.

We will use plugins_api as it gives us the greatest level of control over the functionality: when WordPress initiates a Plugins API call to get information about the current plugin, our function will jump in and handle the request using the license server instead of WordPress.org. The rest of the requests will be left for WordPress to process.

First, add the filter in the Wp_License_Manager_Client constructor's plugin specific else branch:

Then, add the function:

Line 16: Check the requested Plugin API action. We are currently only interested in plugin_information, so if WordPress asks for something else, we just return false and let the request go all the way to WordPress.org.

Line 19: Check if the request is about the current plugin. If yes, we'll take the request and handle it. If not, return false so we don't break other plugins' Plugins API requests.

Line 20: Call our license server to retrieve the information for the current product.

Lines 22–48: Collect all the plugin data we got from the info request. Now, we are finally using all of the fields returned by the API.

Line 50: Return the result object.

To test this functionality, edit the Hello World Plugin product on your license server and add values to all of the product settings fields. In addition to the settings defined earlier, enter the following, setting the values to anything you like—we are just testing...

  • Tested with WordPress version: The highest WordPress version you have tested the plugin on.
  • Requires WordPress version: The minimum WordPress version required to run your plugin.
  • Last Updated: The date of the last update to this plugin, in YYYY-MM-DD format.
  • Banner low and Banner high: These fields define the normal (low) and retina (high) versions of the banner shown on top of the plugin information screen. Insert URLs to images of the following sizes: 772x250 for low and 1544x500 for high.

Save the product and head back to the test site's Updates page. Now, you will see that there the WordPress version compatibility field is no longer Unknown, and when you click on View version 0.2 details, you will see this popup:

Hello World Plugin popup

Conclusion

We have now created a fully functional, license controlled WordPress plugin and theme update system. It's still rather basic, but can already be used for passing updates to your organization's internal users or your premium plugin and theme customers.

Hopefully, you have also learned something new about working with WordPress plugins and themes and can use the knowledge in creating your own projects.

Tags:

Comments

Related Articles