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.
Download
- Client library (requires WP 3.2 or later, tested up to 4.0-alpha)
- Example theme
- Example metadata file
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:
- Create a publicly accessible “metadata file” that describes the latest version of your theme.
- 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.
I am using zip-7 and at the root of the zip is the theme folder. Now when I update I get this
Downloading update from http://localhost/SmartHomeImprovement/theme/footbridge.zip…
Unpacking the update…
Installing the latest version…
Removing the old version of the theme…
Could not remove the old theme.
Theme update failed.
This may sound obvious, but is there anything in the theme directory that could prevent it from being removed? Like an .svn/.git subdirectory, a file that’s currently open, or something like that.
Also, does the theme folder name that you pass to the ThemeUpdateChecker constructor match the actual name?
I deleted this file .DS_Store.
Now I am getting this error
Unpacking the update…
Installing the latest version…
Removing the old version of the theme…
Could not create directory. C:/xampp/htdocs/SmartHomeImprovement/wp-content/themes/footbridge/
Theme update failed.
If this was a *nix server I’d tell you to check file permissions, but that shouldn’t be a problem in XAMPP running on Windows. More likely, the old theme directory still didn’t get removed.
In any case, it’s probably unrelated to the update checker. It just tells WP that an update is available and where to download it. Installation problems are usually caused by WP configuration issues or incorrect permissions.
I closed all programs and it works great. Thank you for a great library! It works like a charm
[…] 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. […]
Got this working fine. One niggle is that it activates the theme afterwards. When working with a master theme and child theme this isn’t ideal. Is there a quick way to disable auto-activating the theme? Or better yet, only activating it IF the theme being updated was already the active theme?
This library doesn’t handle the installation of updates, so it can’t really enable/disable theme activation. It just checks for updates and then hands WordPress update details (e.g. version, download URL, etc) in the appropriate manner. When the user clicks “update now”, WordPress installs the update without involving the custom update checker.
So if you want to turn off auto-activation, you’ll probably need to look at the WordPress core for clues. Unfortunately, I don’t know the solution off the top of my head.
Hi Jānis,
Firstly just wanted to say that this is a great resource – it’s proved to be very helpful so thanks for sharing. One query though: on a WP network with multiple themes that have this update script, the update checker only flags the theme that is active on the main site. Any idea if it’s possible to get it to check across all themes?
Short answer: No, at least not easily.
Long answer:
The way the update checker works is that it adds the update on the fly. It hooks into the “get available theme updates” function in WordPress and adds your update details to the list of updates that WP gets from wordpress.org.
Obviously, the only way the script can do that – or, well, anything – is if it’s actually running. WordPress only runs the scripts that are part of the active theme, so updates from other themes will never show up.
Also, when you open the network admin, WordPress only loads the active theme for the main site. Again, updates from other themes won’t show up – even if they are active on other sites, their update checkers are never run in the network admin.
Ah, OK. Thanks for the explanation. It’s not too much of an issue, but would be nice. I’m sure I’ve been prompted for updates on non-live themes though. Definitely plugins for instance, are updated without being live on the main site. But no worries – it’s still a great tool to have 🙂
Themes and plugins from the official WordPress.org repository can of course be updated even if they’re not active. What I wrote above only applies to themes/plugins that use custom update checkers.
Theme name must contain ‘-‘ symbol, otherwise it doesn’t work properly.
how to fix this issue?
That is not true. What makes you think it must contain a “-” character? As far as I can tell, a directory name like “mytheme” would work just fine.
I have set this library up to update my theme from a private Github repo. Only thing is that when i update the themes foldername is changed. I guess it is because of the github naming of zip files.
Im not sure how or where in the updating chain i should rename something to get it to work.
I don’t think you can change how GitHub lays out it’s ZIP files, so your options are probably limited:
a) Host the updates yourself and name the ZIP files and theme folders appropriately.
b) Figure out where and how WordPress renames the extracted theme folder and override it somehow. As far as I know, this kind of thing isn’t documented anywhere. You would have to read the WP source.
I was looking though how github-updater does it. I thought one might incorporate something similar to this, i just dont have the php chops to pull it off:
(Line 378)
https://github.com/afragen/github-updater/blob/fda44ca541df4327b6bec7641b51ac08927f7088/includes/class-github-updater.php
That looks like a good solution. I’ll investigate it.
Now the next question would be: what folder name should the updater use? There are at least three options that all seem equally valid at first glance:
– Use the plugin or theme slug.
– Use the repository name, like github-updater does. Only works for GitHub.
– Use the name of the existing folder. In rare cases, users rename the theme/plugin folder so that it no longer matches the slug or repository name. Installing the update under the default name might cause problems.
Any thoughts on this?
One would assume that updating a theme should keep the name of the theme folder intact. I think using the current foldername would be the best solution.
At any rate it should be the only one that doesn’t break the site on update since changing the active themes foldername deactivates it.
Found this gist. It might be a little bit cleaner to incorporate:
https://gist.github.com/scarstens/3219354