Note that wordpress.org itself reports approximate install stats - e.g. 10000 instead of 11450. It's up to you whether you want to do the same, show the exact number of installs, or omit this field entirely.
The update checker uses class_exists in several ways:
- As a guard clause around `class Whatever` definitions. This ensures we don't try to define a class that has already been loaded by a different plugin. In this case, autoloading is not necessary because we already know how to load the class. Also, we *want to* load our version of that class if possible - the version that gets loaded by somebody else's autoloader might be different and incompatible.
- As a guard clause before `require` statements that include a class. This is conceptually the same as the previous example.
- To enable optional features if Debug Bar is active. The latest compatible version of Debug Bar doesn't use autoloading, so it would again be unnecessary in this case.
As noted in the docs, the update ZIP file needs to have a specific directory structure. All plugin files should be inside a directory named "plugin-slug-here" and not at the root of the ZIP file. The update checker can fix the directory name if necessary, but it can't deal with the files being at the wrong level - at least not yet.
The reason this is necessary is that WordPress decides which directory to copy to /wp-content/plugins based on the contents of the update. If the ZIP contains just a single directory, WP will copy that directory. In any other case, it will copy the entire working directory where it extracted the ZIP file. This directory usually has a name like "zip-file-name.tmp". As a result, the directory name of the update won't match the directory where the old version was installed, which means WordPress will fail to reactivate the plugin.
The update checker can't rename the working directory because that would prevent WP from cleaning up temporary files in that directory (that part of the core is not hook-able and doesn't check for errors).
A potential solution would be to automatically create the requisite subdirectory and copy all update files to it. However, that would be a bit too much "magic", as well as another edge case to worry about and test. It seems better to notify the developer and let them fix their mistake.
- 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.
By convention, there should be a space before and after a string concatenation operator.
Also, both submit buttons should use a dash to separate the static and variable parts of the element ID.
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.
Explanation:
WordPress includes a "quick edit" function that lets users edit certain post properties (title, categories, etc) from the "All Posts" list. WP calculates the width (colspan attribute) of the inline editor based on the number of <th>'s in the header of the first .widefat table on the page. When PUC and Debug Bar are both active, that first table happens to be the debug info table in our Debug Bar panel. This table does not have a <thead>. As a result, WordPress sets the colspan to zero, making the inline editor unusable.
Fixed by removing the "widefat" class from our debug info tables and adding a bunch of new CSS to emulate WordPress table style.
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.