No need to check if is_network_admin() and switch between network_admin_url() and admin_url(). Turns out, WordPress already has a utility function that does that.
WordPress has a security feature where the HTTP API will reject all requests that are sent to another site hosted on the same server as the current site (IP match) or a local host/IP, unless the host exactly matches the current site. This feature is opt-in, but apparently some people (or security plugins?) enable it.
This can be a problem when the update metadata is on the same server as your site, but not on the exact same hostname. The aforementioned security restriction will cause updates to fail for no apparent reason.
The patch fixes the issue by using the "http_request_host_is_external" filter to explicitly allow the host that the metadata URL points to.
Now PluginInfo will always trigger a PHP notice if it receives invalid JSON or the input doesn't pass some (basic) validation. In practice, you should never encounter this notice if you've set up your metadata correctly.
Make it a public method: isPluginBeingUpgraded(). This method returns true if the plugin associated with the update checker is currently in the process of being updated.
This can be useful for plugins that hook into WordPress core to change how WordPress installs updates. For example, if you were using the "upgrader_pre_download" filter, you could call this method to verify that the update being downloaded is for your plugin and not another one.
Caution: The method is not guaranteed to be accurate.
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.