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:

    True, I haven’t tested this on a multisite install recently. My primary test site uses a standard single-site setup (WP 3.5-alpha). I’m not sure what the problem could be.

  2. Eric Schwarz says:

    Well upon further testing, using your Debug, and changing get_themes to wp_get_themes for 3.4.1 WordPress it sort of works. The Debug says it’s doing it thing, and I now get notification for the update in the admin bar for a theme update but when I click on it I taken to update-core.php where I’m told that I have no updates… so… any Idea what might be happening?

  3. Jānis Elsts says:

    Take a look at the injectUpdate() method. It’s responsible for making sure WordPress can see the external update. You could, for example, add the following code just before the “return” statement to see if it’s retrieving the update properly and if it gets added to the internal WP update list:

    var_dump($state, $updates);
    

    If everything is working properly, $updates->response should contain an entry for your theme.

  4. John Nickell says:

    I found a potential fix for a couple of issues mentioned here.
    First, it seems only the theme installed on the main site of a multi-site install has code that is run in the admin updates area. That means if you have a different theme installed on a sub-site, you will see the update notification in that site’s admin area, but when you click to update it will appear everything is up to date. I’m working on some workarounds for that issue.
    Second, the code now uses wp_get_themes, but the value returned for the theme “Template” will always be the parent theme, even if you are using a child theme. Changing the checkForUpdates method to use the theme “stylesheet” property fixed that problem.
    I hope those help, and I will keep checking to see if it works well on a multi-site install for the newest versions of WordPress.

  5. John Nickell says:

    In my previous comment, I should have said the getInstalledVersion method should use the “stylesheet” property rather than the “Template” property.

  6. Jason Newlin says:

    @Jānis – This is AMAZING, BTW. Works great!

    I did update ‘get_themes’ -> ‘wp_get_themes’ so I don’t get the deprecated message.

    Thanks for all of your hard work on this.

  7. Jānis Elsts says:

    @ John:

    Actually, I think wp_get_theme() might work even better. The library knows which theme it needs, so it can just retrieve it directly with wp_get_theme() instead of getting the full list of themes with wp_get_themes() and iterating through it. I’ve updated the code accordingly.

    WP_Theme::is_available() might help with the multisite problem, but I haven’t investigated it in detail yet.

  8. Postholic says:

    Hi, great plugin! I tried it on my localhost it works but there’s something wrong on a production site:

    When I navigate to the Update page, it shows the available update correctly. But when I tried to refresh this page or navigate to *any* other page I got a 500 error:

    Here’s my error log from Apache httpd:

    [error] [client 127.0.0.1] PHP Fatal error: ThemeUpdateChecker::injectUpdate(): The script tried to execute a method or access a property of an incomplete object. Please ensure that the class definition "ThemeUpdate" of the object you are trying to operate on was loaded _before_ unserialize() gets called or provide a __autoload() function to load the class definition in /path/to/wp-content/themes/private-theme/includes/theme-update-checker.php on line 181, referer: http://path.tld/to/wp-admin/network/update-core.php

  9. Jānis Elsts says:

    I’m guessing you have a caching plugin installed and it loads the update checker option before the ThemeUpdate class is loaded, so you end up with an incomplete object.

    Storing the update data as JSON instead of as a serialized object might help. Try this patch and let me know if it fixes the problem.

  10. Peter says:

    Thanks for sharing!

  11. […] de notification de mises à jour et d’installation pour un thème WordPress. Cela ajoute uneSource Lucas Bénard27 octobre 2012BilletsWordpress Articles Relatifs // var disqus_shortname = […]

  12. Valentin says:

    I got the same issue as Postholic and I’m using W3 Total Cache wich causes the problem. Tried your fix Jānis Elsts but unfortunately it doesn’t work.

  13. Jānis Elsts says:

    Do you still get the same error even with the fix, or a different one?

  14. Valentin says:

    I’m sorry! Now its working properly. I first had to disable the plugin, then updating the class on source zip and destination both and finally after re-activating the plugin and flushing cache it worked. My bad!

  15. Jānis Elsts says:

    No problem, I’m glad to hear it works 🙂

  16. Valentin says:

    Thank you very much 🙂

  17. vaibhav says:

    I am getting an 500 Internal server Error the moment i insert that code in functions.php

  18. Jānis Elsts says:

    Check your PHP error log. Also, what version of PHP are you running?

  19. vaibhav says:

    php 5.0 later
    am using ipage.com as my host
    My error Logs
    “PHP Warning: Unknown: open(/var/php_sessions/sess_6a1de8183cdadd1c33bd948414470eac, O_RDWR) failed: No such file or directory (2) in Unknown on line 0 “

  20. Jānis Elsts says:

    That sounds like a general PHP issue, not something related to this library.

    Also, note that the both the current version of WP and this library require PHP 5.2 or later, so if you’re really running some version of PHP 5.0, that might be the problem.

Leave a Reply