Automatic Updates For Private And Commercial Themes

Update 2017-06-20: This library has been deprecated. Please use PUC instead. It’s more current and it supports both themes and plugins.


This is a PHP library that lets you add automatic update notifications and single-click updates to any WordPress theme. It’s purpose is to be easy to integrate for developers and to provide a familiar experience to theme users. From the users’ point of view, update notifications generated by this library will look and function just like those displayed by WP itself.

Dashboard screenshot

An update notification for a theme not hosted on wordpress.org

Download

License

This library is licensed under the GPL and is distributed free of charge. If you find it useful, consider making a donation. Commercial licensing (e.g. for projects that can’t use an open-source license) is available upon request.

Quick-Start Guide

There are two things you will need to do:

  1. Create a publicly accessible “metadata file” that describes the latest version of your theme.
  2. Add the update checker to your theme and tell it where to find that file.

First, the metadata file. Open your favourite text editor and copy the following JSON code into a new file:

{
  "version" : "2.0",
  "details_url" : "http://example.com/example-theme/details.html",
  "download_url" : "http://examle.com/example-theme/example-theme.zip"
}

Replace the placeholder values with your own data. As you can probably guess, version is the version number of your theme. details_url specifies the page that the user will see if they click the “View version 1.2.3 details” link in an update notification. Set this field to your “What’s New In Version 1.2.3” page or the theme homepage (tip: if you notice that your page looks strange when viewed from the WP dashboard, see this comment).

Finally, download_url is the URL where the latest version of the theme can be downloaded. This field is optional. If you leave it out, the user will still get an update notification when a new version comes out, but there will be no “update automatically” link. They’ll have to download and install the update manually.

Upload the metadata file to your website. You can use any directory and file name you like; just remember that the file URL should be accessible from wherever someone might install your theme.

Next, lets add the update checker library to you theme. Copy the “theme-updates” directory from the client library to your theme. Then add the following to your functions.php:

//Initialize the update checker.
require 'theme-updates/theme-update-checker.php';
$example_update_checker = new ThemeUpdateChecker(
    'example-theme',
    'http://example.com/example-theme/info.json'
);

Again, replace the placeholders with your own settings. The first argument should be the name of your theme’s directory. For example, if your theme lives in /wp-content/themes/my-theme/, use “my-theme” here. The second argument should be the URL of the metadata file you just created.

Congratulations, your theme now supports automatic updates 🙂 The update checker will automatically query the metadata file every 12 hours, checking to see if a new version is available. If it finds one, it will display a standard theme update notification on the Dashboard. Your users will be able to install the new version with a single click.

The ThemeUpdateChecker class

Class constructor
The library is configured by passing a number of arguments to the ThemeUpdateChecker constructor. They are, in order :

  • $theme –  The theme directory name, sometimes called the “slug”.
  • $metadataUrl – The URL of the theme metadata file.
  • $enableAutomaticChecking – Enable/disable automatic update checking. If set to FALSE, you’ll need to explicitly call the checkForUpdates method to, err, check for updates. Defaults to TRUE.

checkForUpdates()
Manually trigger an update check. This is useful if you want to do update checks on your own schedule. checkForUpdates has no parameters and does not return anything. If you want to actually retrieve the latest update, use requestUpdate instead.

requestUpdate()
Retrieve update information from the configured metadata URL. Returns either an instance of ThemeUpdate, or NULL if there is no newer version available or if there’s an error.

deleteStoredData()
The update checker stores various update-related bookkeeping data in a DB option. Call this method to delete that data. This is can be useful is your theme provides some kind of “uninstall” feature.

addQueryArgFilter($callback)
Register a callback for filtering query arguments. Whenever the update checker needs to retrieve the metadata file, it will first run each filter callback and attach the query arguments that they return to the metadata URL. This lets you pass arbitrary data to the server hosting the metadata. For example, commercial themes could use it to implement some kind of authorization scheme where only paying users get automatic updates.

The callback function will be passed an associative array of query arguments and should return a modified array. By default, the update checker will append the following query arguments to the URL:

  • installed_version – the currently installed version of the theme.

This method takes one parameter – the callback function.

addHttpRequestArgFilter($callback)
Register a callback for filtering arguments passed to wp_remote_get. The callback function should take one argument – an associative array of arguments – and return a modified array or arguments. See the WP documentation for details about what arguments are available and how they work. This method takes one parameter – the callback function.

addResultFilter($callback)
Register a callback for filtering theme info retrieved from the metadata URL. The callback function should take two arguments. If a theme update was retrieved successfully, the first argument will be an instance of ThemeUpdate. Otherwise, it will be NULL. The second argument will be the corresponding return value of wp_remote_get (see WP docs for details). The callback function should return an instance of ThemeUpdate, or NULL. This method takes one parameter – the callback function.

Related posts :

306 Responses to “Automatic Updates For Private And Commercial Themes”

  1. Jānis Elsts says:

    If I recall correctly, on Multisite all update checks happen on the main site. So your filter will only be called if your plugin is active on that site.

  2. pingtech says:

    I was putting off having to write this from scratch, so a BIG thank you. You should reference your github repo for the server side on this post too. For those that have not found it yet, it is from the author: https://github.com/YahnisElsts/wp-update-server

  3. corradomatt says:

    Has this class been confirmed to work with WP 3.9? I haven’t been able to get it to work. I have the plugin update class working just fine…but themes is another story.

    Maybe the new theme view changed how this class needs to hook into WP?

  4. Jānis Elsts says:

    I have the example theme that uses this class installed on a WP 4.0-alpha site and it works fine there. I also have a WP 3.9 test site, and I’ve just confirmed that updates show up there as well.

    Have you tried installing the example theme as a test? Do updates show up for this theme, and if they do, what is your theme doing differently?

  5. corradomatt says:

    My json file has some additional details in it besides just “download_url” and “version”. There are also “slug”, “author”, “name” and “sections” in the data being returned. Maybe this is my problem? Or the fact there there is no .json extension on the file name?

  6. Jānis Elsts says:

    The JSON file doesn’t need a .json extension. You can put additional details in the file if you like. The update checker won’t use them, but having them won’t hurt.

    I noticed you didn’t mention “details_url”. This field is required. If you leave it out, your updates will be ignored.

    Also, should I assume that you haven’t tried the example theme, then?

  7. corradomatt says:

    Our json file was missing the “details_url” parameter which caused the check to fail. Thanks for your help!

    Do you know if I can make your class work with a parent and child theme? Are there any issues that I should be aware of in this circumstance?

  8. Jānis Elsts says:

    It should work with child themes as long as the theme slug is set up correctly. You will probably need separate instances of the class for the parent and the child – it can’t handle both at once.

  9. btati says:

    Hi,
    I’m trying to test the “example-theme”. I’m using WP 3.9.1. that has “Twenty Twelve” theme (no twentyten). Therefore, I’ve updated the style.css to work with it as follows:

    Theme Name: Example Theme
    Theme URI: http://w-shadow.com/
    Description: Demonstrates how to use the theme update checker library.
    Version: 1.0
    Author: Janis Elsts
    Author URI: http://w-shadow.com/
    Template: twentytwelve
    */
    /* Nothing of interest here. See functions.php for the example code. */
    @import url(“../twentytwelve/style.css”);

    But, I did not receive any update notification. Am I missing something?

  10. Jānis Elsts says:

    This may sound obvious, but did you activate the theme? The update checker code will only be executed if the theme is active.

  11. btati says:

    Thanks for the prompt response!

    Yes, but still does not work.

    I’m testing locally (localhost with WAMPSEREVER 2.2). But , of course, with internet connection. Does that cause the problem?

  12. Jānis Elsts says:

    No, that shouldn’t matter. I also develop and test this library on a local server.

    Try adding a call to $example_update_checker->requestUpdate() to functions.php and then var_dump() the result somewhere. What does it return, if anything?

  13. btati says:

    Hi,
    First, I would like to thank you for your kind help and support. And for sharing such wonderful piece of code.

    I did two mistakes that caused the problem:
    1) I was using the link (http://1.shadowcdn.com/files/example-theme-updates/info.json) it shows connection problem. Solution: I create my own (local) server to save this file
    2) I wanted to delete the optional filed (download_url) but I did it wrongly as follows:
    {
    “version” : “2.1”,
    “details_url” : “http://localhost/testFonts/update/details.html”,
    }
    The last comma makes the problem. The solution:
    {
    “version” : “2.1”,
    “details_url” : http://localhost/testFonts/update/details.html
    }

    Finally, I found a minor bug (as I think). But I could not resolve it.
    My them version was: 2.1
    My new update (JSON) version is: 3.0
    The code detects there is a new update and shows the notification message. So far so good.

    I changed my them version to: 4.0. Deactivated then reactivated the theme.

    The code detects the new theme revision and the JOSON revision and (after extensive debugging) it decides not to update as it should ($themeUpdate = null) but the notification is still there. Is there any way to delete/clear the notification.

    if ( ($code == 200) && !empty($body) ){ $themeUpdate = ThemeUpdate::fromJson($body);
    //The update should be newer than the currently installed version.
    if ( ($themeUpdate != null) && version_compare($themeUpdate->version, $this->getInstalledVersion(), ‘theme, $themeUpdate, $result); return $themeUpdate;

    Many thanks!

  14. Jānis Elsts says:

    1) I was using the link (http://1.shadowcdn.com/files/example-theme-updates/info.json) it shows connection problem. Solution: I create my own (local) server to save this file

    That’s strange, the link works fine for me. I’m using the Amazon CloudFront CDN. Maybe the CDN server closest to your location was inaccessible for some reason?

    Deactivated then reactivated the theme.

    The code detects the new theme revision and the JOSON revision and (after extensive debugging) it decides not to update as it should ($themeUpdate = null) but the notification is still there. Is there any way to delete/clear the notification.

    Deactivating and reactivating the theme will not automatically make it check for updates. The old update information is probably still in the database.

    Try going to Dashboard -> Updates. If I recall how WP core works, this will make WP check for update if the last time it did that was more than 1 hour ago.

  15. corradomatt says:

    One thing that I noticed recently is that WP-CLI doesn’t work for updates with this or your plugin update checker. Have you ever written your own WP-CLI plugin for this or maybe there’s a way to make the default WP-CLI update command trigger these classes as well?

    WP-CLI doesn’t even see the updates when they’re available – even though inside the WP dashboard the updates are displayed.

  16. btati says:

    Many thank.
    Very useful. Even a paid support cannot provide such wonderful support.

    Considering making a donation is for sure!

  17. Jānis Elsts says:

    @ corradomatt:

    Interesting, I’m looking at WP-CLI source code right now and it seems like it should pick up my external updates… It reads the “update_plugins” transient, which would normally trigger the filter callback that inserts the update into the list. More investigation is required.

    @ btati:

    Sure, glad to help 🙂

  18. Jānis Elsts says:

    Just tested it on a local site and it turns out external updates actually do show up with WP-CLI. Here’s a screenshot:
    http://w-shadow.com/files/screenshots/wp-cli-external-updates.png

    (I’ve highlighted the relevant lines.)

  19. Donavan says:

    Im having problems when i install the update i get back. I am using acustom theme as a framework and a child theme to customize the the framework on a client by client bases. I want to update the parent theme and all my clients that uses the new theme will get the update but when i do a test the update checker updates changes the folder name to mytheme.tmp which breaks the child theme

  20. Jānis Elsts says:

    What is your ZIP directory structure like? All of the theme files should be inside a subdirectory with the same name as the theme slug. If they’re at the root of the ZIP, you will get an incorrect folder name.

Leave a Reply