- Added experimental GitHub support. The new PucGitHubChecker subclass can check a GitHub repository for plugin updates. Depending on configuration, it will use either the latest release, the latest tag, or the specified branch. It can also automagically extract version details (description, changelog, etc) from a number of different locations - release names, plugin headers, readme.txt, changelog.md and more.
- The "slug" field of the metadata file is no longer used. The update checker will now use the slug passed to the class constructor, or generate a slug based on the plugin file name.
- Other minor changes to slug handling.
- Version bump to 2.0.
Preamble:
When WordPress installs a plugin update, it assumes that the update ZIP will contain a directory that has the same name as the currently installed plugin. For example, if the plugin is installed in `/wp-content/plugins/awesome-plugin/`, WP expects that there'll be an "awesome-plugin" directory in the ZIP archive. If the update doesn't contain that directory, the installation process will either fail with a cryptic error message or produce unexpected results.
The problem:
- Some developers are either unaware of the above, or unable to format their updates accordingly. For example, they might be using GitHub to serve updates. GitHub typically names the directory in the release/branch downloads "repo-branchname" or "repo-tag-hash". WP needs it to be just "repo".
- Users can rename the plugin directory. It's very rare, but I've seen it happen.
Solution:
The `upgrader_source_selection` filter lets you specify the directory that will be used as the "new" version. Using it is a bit tricky because WordPress doesn't actually tell you *which* plugin or theme it's currently upgrading, but with some analysis and heuristics it's possible to figure it out (most of the time). Then you can rename the directory from the update package to match the existing plugin directory.
Usage:
Add a new key named "banners" to the metadata file. It should be a JSON object with two string properties: "low" and "high". "low" must be a fully qualified URL pointing to a 772x250 image (PNG or JPG). "high" must point to a 1544x500 image. Only one of "low" or "high" is required.
Example:
{
"banners" : {
"low" : "//example.com/assets/banner-772x250.png",
"high" : "//example.com/assets/banner-1544x500.png"
}
}
Banners are entirely optional.
Background (i.e. unattended) plugin updates require an additional $update->plugin field to be set to plugin file name relative to the /wp-content/plugins directory. Fixed by adding a $filename property to the PluginInfo and PluginUpdate classes and updating toWpFormat() to return it as "plugin". Apparently WordPress update API introduced the field a couple of minor versions back, but I didn't notice because it's used *only* by the background updater and ignored otherwise.
I named the internal variable "filename" instead of "plugin" because "plugin" is very vague. It doesn't really tell you if the variable contains a plugin slug, plugin name, the full plugin filename, a partial filename, basename or something else. "filename" is a bit less ambiguous, though not perfect.
Bumped version to 1.6.
You can explicitly specify the MU-plugin basename (relative to "/wp-content/mu-plugins") as the last argument to PucFactory:buildUpdateChecker(). If you do, the "Check for updates" link and update notifications will show up in "Plugins -> Installed -> Must-Use". However, automatic update installation still won't work because WordPress does not support it for mu-plugins.
I have a need to receive extra information back from the server. In particular, I want to receive information that says "updates entitlement soon expires" or "updates entitlement already expired".
Currently, the functions in the class PluginUpdate_1_3 strip out anything not in the private $fields.
This change adds a filter to allow the consumer to retain any extra fields they wish.
The WordPress cron implementation does not guarantee that a cron hook will only be run once, or run exactly on schedule. For example, it's possible for an event to be triggered two or three times in quick succession due to race conditions. If we check for updates each time our hook is run, we could end up sending redundant update requests that waste bandwidth and server resources.
Instead, make sure at least $checkPeriod hours have elapsed since the last check before checking again. Allow for a small fuzz-factor (currently 20 minutes) to account for the inherent inaccuracy of WP cron.
This feature is disabled by default. Turn it on by setting `$checker->throttleRedundantChecks` to `true` and use `$checker->throttledCheckPeriod` to set the alternate check period (default = every 72 hours).
Caution: There might be some issues in how/when the cache is cleared. It looks fine in theory, but I may have missed something (bulk upgrades? Multisite side-effects?).
Also removed two unused parameters from addCheckForUpdatesLink(). We can always add them back later if needed.
Version bump: 1.3.2
This was suggested in #8 as a way to make the custom update checker more consistent with how WP handles plugin updates. Arguably, visiting "Dashboard -> Updates" means that the user wants to check for updates, so it's okay to ignore the configured check interval in this case.
You can still disable automatic checks by setting $checkPeriod to 0, which will also disable this additional check.
This makes the library more resilient and less likely to break due to cron-related bugs in other plugins. See this commit for an analysis of one such bug: YahnisElsts/plugin-update-checker@5aee0d7b8b
Analysis:
- PluginUpdateChecker creates a custom cron schedule by using the cron_schedules filter. This schedule is used to run periodic update checks.
- BackupBuddy also creates a number of custom schedules using the same filter. However, its filter callback throws away any schedules defined by other filters/plugins and re-initializes $schedules with an empty array().
- As a result, if the filter that was added by BackupBuddy runs *after* the filter added by PluginUpdateChecker, our custom schedule is destroyed.
- When WordPress tries to re-schedule our event after a successful Cron run, it discovers that the required schedule no longer exists, and fails. On the next page load, the library detects that the event is not scheduled and schedules it again. Hence infinite loop.
- Fixed by moving our cron_schedules filter to a later priority.
Notes:
This is the *second* time I have to add a workaround for some arrogant oversight perpetrated by BackupBuddy developers. (The first one was the "plugins_api" thing, IIRC).
Essentially, the library just switches to using /wp-admin/network/plugins.php instead of /wp-admin/plugins.php as necessary. Also, the "all_admin_notices" action runs in both normal and Network admin, so it's a better choice for displaying the update check result than either "admin_notices" or "network_admin_notices".
Pass the plugin slug in a separate "puc_slug" query parameter when doing a manual update check. This way displayManualCheckResult() can verify that the current "puc_update_check_result" value applies to the right plugin.
If several active plugins include the update checker library we might end up with a bunch of different versions being loaded. In the previous implementation, whichever version was loaded first would take precedence. This is obviously a problem if an old version gets loaded and one of the plugins relies on features that are only available in the latest version.
The best way to fix this would be to delay library loading until all plugins have been loaded, then only load the latest available version. Unfortunately this approach would not be backwards-compatible with previous versions that just load the library right away. Another good way to fix the problem would be to put the entire library in a versioned namespace. Alas, namespaces are only available in PHP 5.3 and WordPress only requires PHP 5.2.
So what I've done is to put the version number in the class name and create a factory that will keep track of available versions and let you instantiate the latest one. Note that internal classes like PluginUpdate and PluginInfo still refer to specific implementations for internal consistency and backwards-compatibility.
This is a significant change from previous behaviour where the library would leave the value of the "update_transients" unmodified if there were no updates available. That worked fine at the time because WP wouldn't re-save the injected update to the DB. So when no updates were available, old cached updates wouldn't show up either. However, this appears to have changed - in some cases, the injected update sticks around even if the plugin is no longer injecting it. This patch counters that.
Drawback: If you use this library in a plugin that's hosted on wordpress.org, it will overwrite any update data from wordpress.org with its own and effectively disable wordpress.org updates for your plugin (doesn't affect other plugins).
Cause: Originally the update check routine was attached to the admin_notices hook. However, the plugin list is populated long before that action.
Fixed by running the update check earlier in the request process and redirecting back to plugins.php to display the success message. As a side-effect, this will also prevent unwanted extra checks when the user reloads the page.
* checkForUpdates() now returns the update or null.
* Added a new getUpdate() method. Use it to retrieve update details (if there is an update available). It uses the internal cache, so use checkForUpdates() instead if you want the most recent info. Also, if no update is available, or if its older than the installed version, it will return null.
* Changed our plugins_api filter priority to 20 to fix a compatibility problem caused by a bug in the WooThemes plugin updater. In short, the WooThemes updater also has a plugins_api filter, and their implementation will throw away the response returned by other filters.