Automatic Updates For Private And Commercial Themes

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 :

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

  1. Jānis Elsts says:

    Yes, that seems reasonable.

    However, looking at github-updater, I see a potential problem: it decides whether to rename the folder by checking if the source folder name (e.g. “great-theme-xyz-master”) contains the repository name (e.g. “great-theme-xyz”).

    The trouble is that this would also match repositories like “great-theme” and “theme-xyz”. In general, any theme whose name is a substring (is that the right term?) of another theme’s name risks being installed in the wrong folder.

    How does one unambiguously determine which theme is being updated? The upgrader_source_selection filter only gets the source/remote_source strings, not the theme slug or original folder name.

  2. Jānis Elsts says:

    Oops, I didn’t see your last comment with the gist. That approach looks like it would work, but I just tested it on a local install and $upgrader->skin->theme was NULL in a bulk upgrade.

  3. Jānis Elsts says:

    Update: There are about half a dozen different upgrader skins, and each of them stores the plugin/theme information in a different place and a different format. It is possible to retrieve the original folder name, but it will take work.

  4. Rasmus Jürs says:

    I guess the next best thing would be to define the updated theme foldername along with the updateinfo.json file. Or maby just use the slug.

  5. Jānis Elsts says:

    The problem is figuring out if the theme that’s being upgraded is your theme. Once you know it is, it’s relatively easy to get the current folder name.

  6. […] ダウンロードは、Automatic Updates For Private And Commercial Themesの「Client library」リンクから行ってください。 […]

  7. Rasmus Jürs says:

    Isn’t that determined by the slug defined alongside the json link in functions.php. I use the same name in the slug as i do for the themefolder.

  8. Jānis Elsts says:

    Yes, but the source selection filter only sees the temporary folder names (e.g. /wp-content/updates/whatever.tmp/whatever) and an upgrader instance. WordPress doesn’t pass the slug, download URL, JSON data or anything like that to this particular filter.

  9. Rasmus Jürs says:

    So. Back from christmas vacation. Just tried the new implementation and it seems to be working like a charm. The clients using this theme is part of a chain og physiotherapists and aren’t allowed to have other themes installed. So any bugs it might have with conflicting foldernames shouldn’t be an issue.

    Thank you very much for your help.

  10. Ryan says:

    Thanks for providing this script. It has been a great learning process. In my implementation, any messages returned from the exitWithError function are ignored, and WordPress only display a rather generic ‘Download failed. Forbidden’ if I return a 403 with the message. Is there any way to give a more descriptive message when the download fails due to a license key check failure?

    Thanks!

  11. Jānis Elsts says:

    No, there’s no way to provide a custom error message to WordPress from your update server, at least not directly. You would have to do that client-side (that is, in your plugin).

    For example, maybe you could use the “http_response” filter to parse the error message returned by the server and then pass it on to WordPress as a WP_Error.

  12. Phil says:

    This is awesome Jānis! Works like a charm and was really easy to setup. Thanks for sharing :)

    My only question is related to the info that can be displayed when someone performs the update. I have a few themes that I’d like to set this up with. However, people can quite easily see where the themes are stored once they update and click on “Show details”. It wouldn’t take too much effort to guess the name of the zip file for other themes in the same directory, then download them too.

    To get around this, is the only (easily) way to use a different directory for each theme, and use a random string of characters for the name of each directory? Or could there be another way? I would ideally like to keep all theme updates in the same directory if possible.

    Sorry if this is a stupid question, I’m new to all this!

    Thanks,

    Phil

  13. Jānis Elsts says:

    I haven’t released any commercial themes, but the way that I handle this for my plugins is that each customer gets a unique license key for each plugin they buy. The key is appended to the download link, and the server that provides updates also validates the keys. So the user can see the link, and even download updates manually if they wish, but they can’t download anything else because they don’t have valid license keys for other products.

    If you don’t want to build a full-blown licensing system, then sure, randomizing directory names is an option.

    Manually keeping track of random names could be tedious, though. I would recommend keeping the .zip files one inaccessible directory and writing a small script that maps pseudo-random names to the actual theme names (perhaps based on a simple database) and outputs the appropriate file.

  14. Phil says:

    Hmmm, the licensing option is a little out of scope for me at the minute. I like the idea of using a script to map to “random” strings though.

    Thanks Jānis, if I can get this up and running with my commercial themes I’ll definitely be donating.

  15. Marius says:

    Thank you very much for sharing this Jānis.
    It’s working like a charm for one of my themes.

  16. […] http://w-shadow.com/blog/2011/06/02/automatic-updates-for-commercial-themes/ everything is working fine. However I’ve hit a bit of problem with this method and wondered if there is a solution. […]

  17. Michael says:

    First of all thanks a ton Janis for this great class, works like a charm. :)

    Have on question though: If i change the version in my json file it tooks a lot of time before wordpress actually recognizes that there is a new version, is this normal behavior?

    @Phil: I have a little guide for you:
    For the download link use something like ‘http://www.mydomain.com/download.php’ and now for the download.php on your server just use this download script: http://www.tutorialchip.com/php-download-file-script/

    For me it works like a charm together with this class. The big advantage is that the user cant see the actual download link an on top of that you have to define the real folder of your file which means you can use a folder which isnt accesible via the browser.

    For myself i combined that with a license check. So if a user contacts the server for a updated a check the license and if ok i return the file download.

    Hope that helps!

  18. Jānis Elsts says:

    Usually WordPress checks plugin/theme updates only twice per day, so it’s normal that updates take a while to show up. If you want to force an update check, try manually changing the version number of the installed theme. That will usually make WP check for updates on the next page load.

  19. Michael says:

    Ah nice thanks a lot Janis. :)

Leave a Reply