Automatic Updates for Plugins and Themes Hosted Outside WordPress Extend

Here are two sample scripts along with an API to provide automatic updates for plugins and themes you host on your own server.

Inside /api you’ll find index.php which processes all the update requests. You should place this in something like http://updates.example.com and update $api_url in /plugin/test-plugin-update/test-plugin-update.php and /theme/portfolio-racer/inc/updates.php accordingly. If you activate these sample plugins without changing API URL, updates will be checked against my test server. If you decide to update, both plugin and theme will be replaced with exactly the same version of each.

You may also like

19 Comments

Ivan Novak Jul 13, 5:07

Hey there,
Quite the awesome idea but I can’t seem to get it to work when I change the API url to the correct path on my own server.

I did some investigation and found that if I print_r($raw_response); after line 44 in the test-plugin-update.php file, which is the activated plugin, I’m getting an error on line 41 of the index.php file in the api folder. The error is as follows:

[body] =>
Warning:  array_shift() [function.array-shift]: The argument should be an array in /home/ivannova/public_html/demo/wpdev/api/index.php on line 41
O:8:"stdClass":1:{s:4:"slug";N;}

Any ideas would be welcome. Thanks!

Kaspars Jul 13, 11:25

It looks like $packages[$args->slug]['versions'] is not an array. You could check if $packages[$args->slug] is an array before selecting $latest_package.

Ivan Novak Jul 14, 6:41

Thanks for the reply! I added:

if (is_array($packages[$args->slug]))
    $latest_package = array_shift($packages[$args->slug]['versions']);

And now the plugin reports that it is, in fact, out of date as expected. However, I now receive the message:

There is a new version of Test Plugin Update available. View version Details automatic upgrade unavailable for this plugin.

Thoughts?

Kaspars Jul 15, 0:25

Ivan, I really can’t debug it without direct access to the code. If you are going to implement this into any of your themes/plugins, this might be a good opportunity to take the time and learn how the update procedure works.

Ivan Novak Jul 21, 5:11

Kaspars, thanks for your assistance in getting me on the right path to solving the problem. It is truly appreciated. The problem ended up being that the wp_remote_post(); function actually adds slashes within the $_POST['request'] serialized array. A simple addition of stripslashes() around $_POST['reqeust'] around line 36 does the trick, like so:

$args = unserialize(stripslashes($_POST['request']));

All the best.

David Gwyer Dec 14, 23:22

Just a quick(ish) couple of questions.

Does the api index.php file work by informing WordPress that an update is necessary if the date OR the version are different (or both different)? And does it make a difference what the actual date is of the new version (zip file), i.e. is it just the date specified in the index.php api page that rules whether an update is necessary.

Also, if I wanted to update just by version number and date, is this easy to do?

kaspars Dec 15, 12:06

David, this script does only a version comparison.

David Gwyer Dec 15, 13:09

I’m not sure I follow.

So the actual date of the Plugin/theme zip file, and date entered into the $packages array, never cause an update to occur? And it is purely the version number entered in the api index.php that triggers the update?

If I wanted to trigger an update do I just change the version number in index.php, and that’s it?

Great script by the way.

kaspars Dec 15, 13:22

Correct, David — the date is used only for refference, so that both you and the user know when the update was released. In the API index.php you see a version comparison, which triggers the actual update:

if (version_compare($args->version, $latest_package['version'], '<'))
	$update_info->new_version = $update_info->version;
Robin Dec 16, 0:45

I can’t for the life of me get the theme updater to achknowledge an update. It’s a shame because that is the only part I want. This class does a good job of the plugins. If only it support themes too or if I could get this one to work :P http://w-shadow.com/blog/2010/.....ny-plugin/

Are there any special requirements that might not be obvious?

kaspars Dec 17, 0:00

Robin, did you try enabling the forced update check on every page request? Otherwise it will do it only on cron runs.

Robin Dec 17, 9:28

Hi kaspars,

yep I enabled that. I’ve tried on my local host and on a remote server. I even tried just uploading the unmodified racer theme to one of my sites and it still didn’t do anything even with forced requests on :P

Seth Merrick Feb 21, 22:01

Hey kaspars,

Thank you so much for making this available!

I’m currently using the code in both a theme and a plugin with great success.

I’m working on a new plugin that will auto-update, and it’s raised one question – do you know what I need to add to the $package returned by the API in order to change the message inside WordPress > Updates from “Compatibility with WordPress 3.0.5: Unknown” to “Compatibility with WordPress 3.0.5: 100% (according to its author)”??

My plugin is confirmed to work up through the latest development trunk of WP, and it’s a shame to see the Update page say that compatibility is “Unknown”

Thanks again for everything!

Jason Coleman Mar 27, 20:12

p.s. don’t copy and paste the code from my earlier comment. It looks like WP cropped some of the longer lines.

Don Apr 22, 23:10

Seth Merrick,

Simply add a ‘tested’value to the $packages array, like so:

$packages['your-plugin'] = array(
       'versions' => array(
               'x.x' => array(
                       ...
                       'tested' => '3.1.1', // highest version of WP your plugin works with

And then, reflect that change in the ‘plugin_information’check further down in the document:

if ($action == 'plugin_information') {
               $data = new stdClass;
               ...
               $data->tested = $latest_package['tested']; // pull in the value from $packages

That’s it. Worked for me anyway.

Good luck!

Dimas Jul 5, 11:12

Important: if you want to use this system to update multiple plugins (on the same environment) … the “plugins_api” filter should never at any point return “false”. Instead, based off of the args it receives it needs to lookup and return a result.

Additionally consider using a hash/assoc-array when calling the wp_remote_post() function … something like the following, this will ensure that a slug is directly tied to a api_url.

$request = wp_remote_post($hosts[$args->slug], $request_string);

The plugins_api has a weird implementation, see /wp-admin/includes/plugin-install.php … plugins_api() function … note how when the filters are applied, it needs to return a result. Having an implementation which returns false some of the time and a result other times causes problems here.

Lembit Kivisik Sep 5, 2:38

Thank you Kaspars, thank you others in the comment thread. Useful stuff, got it working for my plugin in no time.

Daniel Oct 4, 0:06

That’s really great. I got it working but I have a question:

I would like to protect the package url. Now it is a direct link to a zip file but I would like to point to a download.php file. So, my question is, how the file content (of the zip file) has to be served from the download.php file that wordpress can install it?

Best regards,
Daniel

Kaspars Oct 4, 1:07

Daniel, here is how I would do it — store all your client domain names and use them as identifiers (set as an HTTP referrer) when an update request is made. Once the request hits your download.php, simply check if that domain is in your database. Yes, this approach is not 100% reliable as that header can be spoofed, but it’s a very simple and fast way of doing it.

Leave a Comment