Show the Enable auto-updates toggle for the self-updater #101
@@ -13,6 +13,9 @@ each change under the current top section as you work.
|
||||
|
||||
## [1.1.1]
|
||||
|
||||
### Fixed
|
||||
- The **Enable auto-updates** toggle now appears for the plugin on the Plugins screen. The self-updater now reports the plugin to WordPress even when it is already current, so core marks it update-supported and shows the toggle; previously the toggle was hidden between releases.
|
||||
|
||||
## [1.1.0]
|
||||
|
||||
### Added
|
||||
|
||||
@@ -50,9 +50,16 @@ update for a same-slug plugin and makes core fire the
|
||||
4. When newer, returns the release's first `.zip` asset as the update
|
||||
package. Core takes over from there: Plugins-screen notice, one-click
|
||||
update, and WP-Cron auto-updates if enabled.
|
||||
5. When not newer — the site is current, or the lookup failed — returns a
|
||||
`no_update` payload (installed version, empty package). This keeps the
|
||||
plugin in core's `update_plugins` transient so core's `update-supported`
|
||||
flag stays set and the **Enable auto-updates** toggle shows on the
|
||||
Plugins screen. Without it, an off-directory plugin is absent from the
|
||||
transient between releases and the toggle never appears.
|
||||
|
||||
Any API failure, malformed response, or asset-less release degrades to
|
||||
"no update available" — never an error surfaced to the site.
|
||||
"no update available" (the `no_update` payload) — never an error surfaced
|
||||
to the site, and never a lost auto-update toggle during a Gitea blip.
|
||||
|
||||
## Cutting a Release
|
||||
1. Bump the version in `unsupervised-schedular.php` (both the `Version:`
|
||||
|
||||
@@ -75,9 +75,18 @@ class UpdateChecker {
|
||||
}
|
||||
|
||||
/**
|
||||
* `update_plugins_{hostname}` filter callback. Returns the incoming
|
||||
* value untouched unless a newer release with a zip asset exists, in
|
||||
* which case it returns the update array core expects.
|
||||
* `update_plugins_{hostname}` filter callback.
|
||||
*
|
||||
* For a newer release with a zip asset, returns the update array core
|
||||
* files under the transient's `response` list (the update offer).
|
||||
* Otherwise — the plugin is current, or the release lookup failed — it
|
||||
* returns a payload with the installed version and no package, which core
|
||||
* files under `no_update`. That `no_update` entry is what sets core's
|
||||
* `update-supported` flag and makes the "Enable auto-updates" toggle
|
||||
* appear on the Plugins screen; without it, an off-directory plugin is
|
||||
* absent from the transient between releases and the toggle never shows.
|
||||
*
|
||||
* The incoming value is only passed through untouched for other plugins.
|
||||
*/
|
||||
public function provideUpdate( mixed $update, mixed $plugin_data, mixed $plugin_file ): mixed {
|
||||
if ( plugin_basename( USC_PLUGIN_FILE ) !== $plugin_file ) {
|
||||
@@ -86,14 +95,8 @@ class UpdateChecker {
|
||||
|
||||
$release = $this->latestRelease();
|
||||
|
||||
if ( '' === $release['version'] || '' === $release['package'] ) {
|
||||
return $update;
|
||||
}
|
||||
|
||||
if ( version_compare( $release['version'], USC_VERSION, '<=' ) ) {
|
||||
return $update;
|
||||
}
|
||||
|
||||
if ( '' !== $release['version'] && '' !== $release['package']
|
||||
&& version_compare( $release['version'], USC_VERSION, '>' ) ) {
|
||||
return [
|
||||
'slug' => 'unsupervised-schedular',
|
||||
'version' => $release['version'],
|
||||
@@ -102,6 +105,17 @@ class UpdateChecker {
|
||||
];
|
||||
}
|
||||
|
||||
// No newer release: answer with a `no_update` payload so core keeps
|
||||
// the plugin in the update transient and shows the auto-update toggle.
|
||||
// The empty package leaves core nothing to auto-install, as intended.
|
||||
return [
|
||||
'slug' => 'unsupervised-schedular',
|
||||
'version' => USC_VERSION,
|
||||
'url' => self::REPO_URL,
|
||||
'package' => '',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* The latest published release, from the transient cache when fresh.
|
||||
*
|
||||
|
||||
@@ -32,6 +32,22 @@ class UpdateCheckerTest extends TestCase
|
||||
return ['name' => $name, 'browser_download_url' => self::PACKAGE_URL];
|
||||
}
|
||||
|
||||
/**
|
||||
* The payload provideUpdate() returns when no newer release is offered.
|
||||
* Core files this under the transient's `no_update` list, which is what
|
||||
* makes the "Enable auto-updates" toggle appear. USC_VERSION is 1.0.0 in
|
||||
* the test bootstrap.
|
||||
*/
|
||||
private function noUpdatePayload(): array
|
||||
{
|
||||
return [
|
||||
'slug' => 'unsupervised-schedular',
|
||||
'version' => '1.0.0',
|
||||
'url' => UpdateChecker::REPO_URL,
|
||||
'package' => '',
|
||||
];
|
||||
}
|
||||
|
||||
public function testRegisterHooksHostnameFilter(): void
|
||||
{
|
||||
Filters\expectAdded('update_plugins_git.unsupervised.ca')->once();
|
||||
@@ -109,7 +125,7 @@ class UpdateCheckerTest extends TestCase
|
||||
self::assertFalse($result);
|
||||
}
|
||||
|
||||
public function testNoUpdateWhenReleaseIsNotNewer(): void
|
||||
public function testNoUpdatePayloadWhenReleaseIsNotNewer(): void
|
||||
{
|
||||
Functions\when('plugin_basename')->justReturn(self::PLUGIN_FILE);
|
||||
Functions\when('get_transient')->justReturn(false);
|
||||
@@ -118,7 +134,9 @@ class UpdateCheckerTest extends TestCase
|
||||
|
||||
$result = (new UpdateChecker())->provideUpdate(false, [], self::PLUGIN_FILE);
|
||||
|
||||
self::assertFalse($result);
|
||||
// Current version → core files this under `no_update` so the
|
||||
// auto-update toggle stays visible; no package to install.
|
||||
self::assertSame($this->noUpdatePayload(), $result);
|
||||
}
|
||||
|
||||
public function testUsesCachedReleaseWithoutHittingApi(): void
|
||||
@@ -134,7 +152,7 @@ class UpdateCheckerTest extends TestCase
|
||||
self::assertSame('2.0.0', $result['version']);
|
||||
}
|
||||
|
||||
public function testApiFailureIsCachedAndReturnsUpdateUnchanged(): void
|
||||
public function testApiFailureIsCachedAndStillReportsUpdateSupport(): void
|
||||
{
|
||||
Functions\when('plugin_basename')->justReturn(self::PLUGIN_FILE);
|
||||
Functions\when('get_transient')->justReturn(false);
|
||||
@@ -150,10 +168,12 @@ class UpdateCheckerTest extends TestCase
|
||||
|
||||
$result = (new UpdateChecker())->provideUpdate(false, [], self::PLUGIN_FILE);
|
||||
|
||||
self::assertFalse($result);
|
||||
// Even with the lookup failed we still return the `no_update` payload,
|
||||
// so the auto-update toggle does not flicker away during a Gitea blip.
|
||||
self::assertSame($this->noUpdatePayload(), $result);
|
||||
}
|
||||
|
||||
public function testNon200ResponseReturnsUpdateUnchanged(): void
|
||||
public function testNon200ResponseReturnsNoUpdatePayload(): void
|
||||
{
|
||||
Functions\when('plugin_basename')->justReturn(self::PLUGIN_FILE);
|
||||
Functions\when('get_transient')->justReturn(false);
|
||||
@@ -162,7 +182,7 @@ class UpdateCheckerTest extends TestCase
|
||||
|
||||
$result = (new UpdateChecker())->provideUpdate(false, [], self::PLUGIN_FILE);
|
||||
|
||||
self::assertFalse($result);
|
||||
self::assertSame($this->noUpdatePayload(), $result);
|
||||
}
|
||||
|
||||
public function testPicksFirstZipAssetAndSkipsOthers(): void
|
||||
@@ -181,7 +201,7 @@ class UpdateCheckerTest extends TestCase
|
||||
self::assertSame(self::PACKAGE_URL, $result['package']);
|
||||
}
|
||||
|
||||
public function testReleaseWithoutZipAssetOffersNoUpdate(): void
|
||||
public function testReleaseWithoutZipAssetOffersNoUpdatePayload(): void
|
||||
{
|
||||
Functions\when('plugin_basename')->justReturn(self::PLUGIN_FILE);
|
||||
Functions\when('get_transient')->justReturn(false);
|
||||
@@ -192,10 +212,12 @@ class UpdateCheckerTest extends TestCase
|
||||
|
||||
$result = (new UpdateChecker())->provideUpdate(false, [], self::PLUGIN_FILE);
|
||||
|
||||
self::assertFalse($result);
|
||||
// No installable package means no update to offer, but we still keep
|
||||
// the plugin in `no_update` so the toggle shows.
|
||||
self::assertSame($this->noUpdatePayload(), $result);
|
||||
}
|
||||
|
||||
public function testMalformedApiBodyOffersNoUpdate(): void
|
||||
public function testMalformedApiBodyOffersNoUpdatePayload(): void
|
||||
{
|
||||
Functions\when('plugin_basename')->justReturn(self::PLUGIN_FILE);
|
||||
Functions\when('get_transient')->justReturn(false);
|
||||
@@ -207,6 +229,6 @@ class UpdateCheckerTest extends TestCase
|
||||
|
||||
$result = (new UpdateChecker())->provideUpdate(false, [], self::PLUGIN_FILE);
|
||||
|
||||
self::assertFalse($result);
|
||||
self::assertSame($this->noUpdatePayload(), $result);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user