Compare commits

..

47 Commits
v5.3 ... master

Author SHA1 Message Date
Yahnis Elsts 68a70bf887
Merge pull request #602 from hisman/add-autoupdate-field-to-plugininfo
Add an optional `autoupdate` field to plugin update info that allows automatically installing updates
2026-01-09 16:38:05 +02:00
Hisman 36f3c2be5b Add default value to the autoupdate field 2026-01-03 18:45:10 +07:00
Hisman 4f87723b6a Add autoupdate field to plugin update info 2025-12-29 22:58:03 +07:00
Yahnis Elsts 07a4c2586f
Merge pull request #601 from seatonjiang/master
Update Simplified Chinese (zh_CN) translations
2025-11-21 16:33:49 +02:00
Seaton Jiang 572bfe0986
feat: update Simplified Chinese (zh_CN) translations 2025-11-21 10:58:21 +08:00
Yahnis Elsts 288f270d8e
Merge pull request #600 from alexclassroom/master
Add translations for zh_TW
2025-09-25 17:42:01 +03:00
Alex Lion 69d30a9c17
Add zh_TW Language Pack based on 5.6 2025-09-19 14:23:24 -07:00
Yahnis Elsts 8add8143a2 Merge branch 'master' of https://github.com/YahnisElsts/plugin-update-checker 2025-07-22 14:38:19 +03:00
Yahnis Elsts 5dfd5e5d1e Replace wp_redirect() with wp_safe_redirect()
This redirect always goes back to the "Plugins" page on the same site, so this should be fine.

Reported in #597
2025-07-22 14:27:13 +03:00
Yahnis Elsts e39f84488e Replace all htmlentities() calls with esc_html()
This avoids the need to explicitly specify the flags and the character set. WordPress will use predefined flags and detect the character set used by the site.

Prompted by #597
2025-07-22 14:23:12 +03:00
Yahnis Elsts dbe1a66274
Merge pull request #596 from aljvdi/upgrade-improve-persian-language
Update Persian translation
2025-06-13 17:57:13 +03:00
Alex Javadi b2cbbd98dd
feat: update and improve Persian translations after 9 years 2025-06-12 23:54:49 +01:00
Yahnis Elsts 60ec73fddc Minor: Update year in license block. 2025-05-22 16:13:04 +03:00
Yahnis Elsts a2db6871de Minor: Update POT file
No new translations, just making sure the file names and line numbers are still correct.
2025-05-20 15:29:01 +03:00
Yahnis Elsts dc93f32388 Bump version number to 5.6 2025-05-20 15:20:21 +03:00
Yahnis Elsts 9f890f5cfd
Merge pull request #593 from todeveni/bc-php84-deprecations
Fix PHP 8.4 deprecation errors pt. 2
2025-04-30 15:48:27 +03:00
Toni Viemerö 46ee454f68 Fix PHP 8.4 deprecation errors
Remove type declarations for parameters that are nullable to get rid of
deprecation errors in a backwards compatible way.
2025-04-29 17:53:45 +03:00
Yahnis Elsts 73e6281e43
Merge pull request #584 from urosevic/master
Add Serbian (Cyrillic) (sr_RS) translation
2024-12-09 17:47:56 +02:00
Aleksandar Urosevic 8f54e73dea
Add Serbian (Cyrillic) language 2024-12-09 12:29:01 +01:00
Yahnis Elsts 845d65da93 Minor: Update POT file.
No new strings, just updated line numbers and such.
2024-10-16 17:25:00 +03:00
Yahnis Elsts c74a3bcde2 Bump version number to 5.5 2024-10-16 17:14:29 +03:00
Yahnis Elsts 70f3c80c81 Minor: Fix grammar in a comment 2024-10-16 17:12:01 +03:00
Yahnis Elsts 8d785d75ac Rename filter that removes plugin/theme from data sent to wp.org API
See #578
2024-10-16 16:35:25 +03:00
Yahnis Elsts dce70db088
Minor: Fix a couple of typos
One out by @robertdevore in #580, the other I noticed myself while fixing the first one.
2024-10-15 12:35:22 +03:00
Yahnis Elsts 93a17f984c Minor: Suppress phpcs warning about changing HTTP request timeouts.
This code doesn't actually modify timeouts.
2024-10-14 19:26:28 +03:00
Yahnis Elsts 7c907e7629 Polish #579 for #578 and improve theme compatibility
Probably needs testing, especially for themes.
2024-10-14 19:23:53 +03:00
Yahnis Elsts ea8de31ebd
Merge pull request #579 from DavidAnderson684/patch-1
Remove plugin/theme data from update requests to api.wordpress.org
2024-10-14 18:08:29 +03:00
David Anderson a622b0047a
Update UpdateChecker.php - add check 2024-10-12 18:21:50 +00:00
David Anderson cc5c8d9eca
Update UpdateChecker.php - filter unwanted data to api.wordpress.org 2024-10-12 15:47:10 +00:00
Yahnis Elsts dfc396c011 Minor: Remove unnecessary doc comment
It seems PhpStorm no longer complains about the "skin" property.
2024-07-06 19:56:43 +03:00
Yahnis Elsts 432e7d5f1a Minor: Fix code style 2024-07-06 19:55:40 +03:00
Yahnis Elsts 668e416c98
Merge pull request #574 from MehbubRashid/master
Fix the remote source directory structure if files are at the archive root instead in a subdirectory
2024-07-06 19:50:07 +03:00
Mehbub Rashid 1dd70e99f0
Fix the remote source folder structure if necessary.
It re-structures and corrects the folder/zip structure of the extracted remote zip folder - if not correct already.
2024-07-06 13:46:47 +06:00
Yahnis Elsts 3b1becb956
Merge pull request #573 from seatonjiang/master
Update Simplified Chinese (zh_CN) translation
2024-05-21 18:36:47 +03:00
Seaton Jiang 1a040ee4bb
feat: update Simplified Chinese (zh_CN) translations 2024-05-18 00:12:19 +08:00
Yahnis Elsts 040a2ef658 Add Greek translation by @theo-gk 2024-05-10 18:24:45 +03:00
Yahnis Elsts 97dfe23d15 Debug Bar: Display "Check Now" and "Request Info" in front end
By default, these buttons are generated using the get_submit_button() API function, but that function is only available in the admin dashboard (unless explicitly loaded). Previously, the buttons were not shown in the front end.

This patch adds a fallback that generates the buttons directly. These won't look exactly the same as admin buttons due to admin styles not being loaded, and WP may change submit button HTML at some point, but the fallback buttons should still work.

Fixes #568
2024-03-20 18:33:33 +02:00
Yahnis Elsts e8e53e6d98 Minor: Update copyright year 2024-02-24 11:56:49 +02:00
Yahnis Elsts 1ee425c37b Bump version number to 5.4 2024-02-24 11:54:53 +02:00
Yahnis Elsts a1445bb8dc Minor: Fix misleading doc-comment and argument name.
Previously, triggerUpdateCheckOnce() was attached to a transient filter, but that's no longer the case. Now it's passed directly to WP_CLI::add_hook().

However, it still takes and returns a value. WP-CLI documentation says that the `before_invoke:<command>` hook takes one argument and acts as a filter.
2024-02-02 16:07:07 +02:00
Yahnis Elsts 36efab0022 Minor: Fix method name in a comment 2024-02-02 15:59:22 +02:00
Yahnis Elsts a211943884 WP-CLI: Maybe check for updates when certain WP-CLI commands run
See #558. Needs testing.
2024-02-02 15:58:59 +02:00
Yahnis Elsts c1bf33e770 Fix a potential autoloading failure due to not using a FQN in class_exists()
Fixes #557
2024-01-20 21:07:24 +02:00
Yahnis Elsts 0a8fd4ad3a
Merge pull request #546 from szepeviktor/fix-exports
Stop exporting development files
2024-01-19 15:40:36 +02:00
Yahnis Elsts 5f251be064 Add more sanity checks to prevent PUC from inadvertently triggering a fatal error if one of its hook callbacks is called while the containing plugin/theme is being deleted.
PUC already used `upgrader_process_complete` to remove hooks when  the plugin version it was part of was deleted during an update. However, that did not catch more obscure situations, such as apparently being called from an unrelated AJAX request while the host plugin version was being deleted (a user sent a stack trace where it seems that was what happened).
2023-11-15 18:49:00 +02:00
Yahnis Elsts 9c1bddcd46 Fix (probably) a conflict with the "WP Last Modified Info" plugin, version 1.8.8.
When a plugin update overwrites PUC with a different version of PUC, the hook callbacks registered by the old version can trigger fatal errors when they try to autoload now-deleted PHP files. Normally, PUC avoids this by using an `upgrader_process_complete` hook to check if one of its files still exists, and removing the hooks if the file is missing.  

However, it appears that WP Last Modified Info has its own `upgrader_process_complete` callback that runs earlier. That callback tries to download plugin metadata, which indirectly triggers some PUC hooks, and leads to the fatal error(s) mentioned earlier.

Fixed by extracting the relevant part of  `upgraderProcessComplete` to a separate method and registering that method as a callback for the same hook, but with an earlier priority (1 instead of 11). It appears that WP Last Modified Info uses the default priority: 10.
2023-11-14 11:52:30 +02:00
Viktor Szépe 90e055ecd3 Stop exporting development files 2023-10-27 13:47:19 +00:00
54 changed files with 637 additions and 183 deletions

7
.gitattributes vendored
View File

@ -1 +1,6 @@
/build export-ignore
/.editorconfig export-ignore
/.gitattributes export-ignore
/.gitignore export-ignore
/build/ export-ignore
/examples/ export-ignore
/phpcs.xml export-ignore

View File

@ -4,7 +4,7 @@ namespace YahnisElsts\PluginUpdateChecker\v5;
if ( !class_exists(PucFactory::class, false) ):
class PucFactory extends \YahnisElsts\PluginUpdateChecker\v5p3\PucFactory {
class PucFactory extends \YahnisElsts\PluginUpdateChecker\v5p6\PucFactory {
}
endif;

View File

@ -1,6 +1,6 @@
<?php
namespace YahnisElsts\PluginUpdateChecker\v5p3;
namespace YahnisElsts\PluginUpdateChecker\v5p6;
if ( !class_exists(Autoloader::class, false) ):

View File

@ -1,8 +1,8 @@
<?php
namespace YahnisElsts\PluginUpdateChecker\v5p3\DebugBar;
namespace YahnisElsts\PluginUpdateChecker\v5p6\DebugBar;
use YahnisElsts\PluginUpdateChecker\v5p3\PucFactory;
use YahnisElsts\PluginUpdateChecker\v5p3\UpdateChecker;
use YahnisElsts\PluginUpdateChecker\v5p6\PucFactory;
use YahnisElsts\PluginUpdateChecker\v5p6\UpdateChecker;
if ( !class_exists(Extension::class, false) ):

View File

@ -1,7 +1,7 @@
<?php
namespace YahnisElsts\PluginUpdateChecker\v5p3\DebugBar;
namespace YahnisElsts\PluginUpdateChecker\v5p6\DebugBar;
use YahnisElsts\PluginUpdateChecker\v5p3\UpdateChecker;
use YahnisElsts\PluginUpdateChecker\v5p6\UpdateChecker;
if ( !class_exists(Panel::class, false) && class_exists('Debug_Bar_Panel', false) ):
@ -41,11 +41,11 @@ if ( !class_exists(Panel::class, false) && class_exists('Debug_Bar_Panel', false
echo '<h3>Configuration</h3>';
echo '<table class="puc-debug-data">';
$this->displayConfigHeader();
$this->row('Slug', htmlentities($this->updateChecker->slug));
$this->row('DB option', htmlentities($this->updateChecker->optionName));
$this->row('Slug', esc_html($this->updateChecker->slug));
$this->row('DB option', esc_html($this->updateChecker->optionName));
$requestInfoButton = $this->getMetadataButton();
$this->row('Metadata URL', htmlentities($this->updateChecker->metadataUrl) . ' ' . $requestInfoButton . $this->responseBox);
$this->row('Metadata URL', esc_html($this->updateChecker->metadataUrl) . ' ' . $requestInfoButton . $this->responseBox);
$scheduler = $this->updateChecker->scheduler;
if ( $scheduler->checkPeriod > 0 ) {
@ -86,14 +86,22 @@ if ( !class_exists(Panel::class, false) && class_exists('Debug_Bar_Panel', false
echo '<h3>Status</h3>';
echo '<table class="puc-debug-data">';
$state = $this->updateChecker->getUpdateState();
$checkNowButton = '';
$checkButtonId = $this->updateChecker->getUniqueName('check-now-button');
if ( function_exists('get_submit_button') ) {
$checkNowButton = get_submit_button(
'Check Now',
'secondary',
'puc-check-now-button',
false,
array('id' => $this->updateChecker->getUniqueName('check-now-button'))
array('id' => $checkButtonId)
);
} else {
//get_submit_button() is not available in the frontend. Make a button directly.
//It won't look the same without admin styles, but it should still work.
$checkNowButton = sprintf(
'<input type="button" id="%1$s" name="puc-check-now-button" value="%2$s" class="button button-secondary" />',
esc_attr($checkButtonId),
esc_attr('Check Now')
);
}
@ -107,10 +115,10 @@ if ( !class_exists(Panel::class, false) && class_exists('Debug_Bar_Panel', false
$this->row('Next automatic check', $this->formatTimeWithDelta($nextCheck));
if ( $state->getCheckedVersion() !== '' ) {
$this->row('Checked version', htmlentities($state->getCheckedVersion()));
$this->row('Checked version', esc_html($state->getCheckedVersion()));
$this->row('Cached update', $state->getUpdate());
}
$this->row('Update checker class', htmlentities(get_class($this->updateChecker)));
$this->row('Update checker class', esc_html(get_class($this->updateChecker)));
echo '</table>';
}
@ -124,7 +132,7 @@ if ( !class_exists(Panel::class, false) && class_exists('Debug_Bar_Panel', false
if ( property_exists($update, $field) ) {
$this->row(
ucwords(str_replace('_', ' ', $field)),
isset($update->$field) ? htmlentities($update->$field) : null
isset($update->$field) ? esc_html($update->$field) : null
);
}
}
@ -162,7 +170,7 @@ if ( !class_exists(Panel::class, false) && class_exists('Debug_Bar_Panel', false
if ( is_object($value) || is_array($value) ) {
//This is specifically for debugging, so print_r() is fine.
//phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_print_r
$value = '<pre>' . htmlentities(print_r($value, true)) . '</pre>';
$value = '<pre>' . esc_html(print_r($value, true)) . '</pre>';
} else if ($value === null) {
$value = '<code>null</code>';
}

View File

@ -1,8 +1,8 @@
<?php
namespace YahnisElsts\PluginUpdateChecker\v5p3\DebugBar;
namespace YahnisElsts\PluginUpdateChecker\v5p6\DebugBar;
use YahnisElsts\PluginUpdateChecker\v5p3\Plugin\UpdateChecker;
use YahnisElsts\PluginUpdateChecker\v5p6\Plugin\UpdateChecker;
if ( !class_exists(PluginExtension::class, false) ):

View File

@ -1,7 +1,7 @@
<?php
namespace YahnisElsts\PluginUpdateChecker\v5p3\DebugBar;
namespace YahnisElsts\PluginUpdateChecker\v5p6\DebugBar;
use YahnisElsts\PluginUpdateChecker\v5p3\Plugin\UpdateChecker;
use YahnisElsts\PluginUpdateChecker\v5p6\Plugin\UpdateChecker;
if ( !class_exists(PluginPanel::class, false) ):
@ -12,19 +12,25 @@ if ( !class_exists(PluginPanel::class, false) ):
protected $updateChecker;
protected function displayConfigHeader() {
$this->row('Plugin file', htmlentities($this->updateChecker->pluginFile));
$this->row('Plugin file', esc_html($this->updateChecker->pluginFile));
parent::displayConfigHeader();
}
protected function getMetadataButton() {
$requestInfoButton = '';
$buttonId = $this->updateChecker->getUniqueName('request-info-button');
if ( function_exists('get_submit_button') ) {
$requestInfoButton = get_submit_button(
'Request Info',
'secondary',
'puc-request-info-button',
false,
array('id' => $this->updateChecker->getUniqueName('request-info-button'))
array('id' => $buttonId)
);
} else {
$requestInfoButton = sprintf(
'<input type="button" name="puc-request-info-button" id="%1$s" value="%2$s" class="button button-secondary" />',
esc_attr($buttonId),
esc_attr('Request Info')
);
}
return $requestInfoButton;

View File

@ -1,8 +1,8 @@
<?php
namespace YahnisElsts\PluginUpdateChecker\v5p3\DebugBar;
namespace YahnisElsts\PluginUpdateChecker\v5p6\DebugBar;
use YahnisElsts\PluginUpdateChecker\v5p3\Theme\UpdateChecker;
use YahnisElsts\PluginUpdateChecker\v5p6\Theme\UpdateChecker;
if ( !class_exists(ThemePanel::class, false) ):
@ -13,7 +13,7 @@ if ( !class_exists(ThemePanel::class, false) ):
protected $updateChecker;
protected function displayConfigHeader() {
$this->row('Theme directory', htmlentities($this->updateChecker->directoryName));
$this->row('Theme directory', esc_html($this->updateChecker->directoryName));
parent::displayConfigHeader();
}

View File

@ -1,5 +1,5 @@
<?php
namespace YahnisElsts\PluginUpdateChecker\v5p3;
namespace YahnisElsts\PluginUpdateChecker\v5p6;
if ( !class_exists(InstalledPackage::class, false) ):

View File

@ -1,5 +1,5 @@
<?php
namespace YahnisElsts\PluginUpdateChecker\v5p3;
namespace YahnisElsts\PluginUpdateChecker\v5p6;
use LogicException;
use stdClass;

View File

@ -1,5 +1,5 @@
<?php
namespace YahnisElsts\PluginUpdateChecker\v5p3;
namespace YahnisElsts\PluginUpdateChecker\v5p6;
if ( !class_exists(OAuthSignature::class, false) ):

View File

@ -1,8 +1,8 @@
<?php
namespace YahnisElsts\PluginUpdateChecker\v5p3\Plugin;
namespace YahnisElsts\PluginUpdateChecker\v5p6\Plugin;
use YahnisElsts\PluginUpdateChecker\v5p3\InstalledPackage;
use YahnisElsts\PluginUpdateChecker\v5p3\PucFactory;
use YahnisElsts\PluginUpdateChecker\v5p6\InstalledPackage;
use YahnisElsts\PluginUpdateChecker\v5p6\PucFactory;
if ( !class_exists(Package::class, false) ):

View File

@ -1,7 +1,7 @@
<?php
namespace YahnisElsts\PluginUpdateChecker\v5p3\Plugin;
namespace YahnisElsts\PluginUpdateChecker\v5p6\Plugin;
use YahnisElsts\PluginUpdateChecker\v5p3\Metadata;
use YahnisElsts\PluginUpdateChecker\v5p6\Metadata;
if ( !class_exists(PluginInfo::class, false) ):
@ -39,6 +39,7 @@ if ( !class_exists(PluginInfo::class, false) ):
public $downloaded;
public $active_installs;
public $last_updated;
public $autoupdate = false;
public $id = 0; //The native WP.org API returns numeric plugin IDs, but they're not used for anything.

View File

@ -1,7 +1,7 @@
<?php
namespace YahnisElsts\PluginUpdateChecker\v5p3\Plugin;
namespace YahnisElsts\PluginUpdateChecker\v5p6\Plugin;
if ( !class_exists('Ui', false) ):
if ( !class_exists(Ui::class, false) ):
/**
* Additional UI elements for plugins.
*/
@ -187,7 +187,7 @@ if ( !class_exists('Ui', false) ):
}
}
wp_redirect(add_query_arg(
wp_safe_redirect(add_query_arg(
array(
'puc_update_check_result' => $status,
'puc_slug' => $this->updateChecker->slug,

View File

@ -1,7 +1,7 @@
<?php
namespace YahnisElsts\PluginUpdateChecker\v5p3\Plugin;
namespace YahnisElsts\PluginUpdateChecker\v5p6\Plugin;
use YahnisElsts\PluginUpdateChecker\v5p3\Update as BaseUpdate;
use YahnisElsts\PluginUpdateChecker\v5p6\Update as BaseUpdate;
if ( !class_exists(Update::class, false) ):
@ -20,9 +20,10 @@ if ( !class_exists(Update::class, false) ):
public $requires_php = false;
public $icons = array();
public $filename; //Plugin filename relative to the plugins directory.
public $autoupdate = false;
protected static $extraFields = array(
'id', 'homepage', 'tested', 'requires_php', 'upgrade_notice', 'icons', 'filename',
'id', 'homepage', 'tested', 'requires_php', 'upgrade_notice', 'icons', 'filename', 'autoupdate',
);
/**
@ -86,6 +87,7 @@ if ( !class_exists(Update::class, false) ):
$update->tested = $this->tested;
$update->requires_php = $this->requires_php;
$update->plugin = $this->filename;
$update->autoupdate = $this->autoupdate;
if ( !empty($this->upgrade_notice) ) {
$update->upgrade_notice = $this->upgrade_notice;

View File

@ -1,10 +1,10 @@
<?php
namespace YahnisElsts\PluginUpdateChecker\v5p3\Plugin;
namespace YahnisElsts\PluginUpdateChecker\v5p6\Plugin;
use YahnisElsts\PluginUpdateChecker\v5p3\InstalledPackage;
use YahnisElsts\PluginUpdateChecker\v5p3\UpdateChecker as BaseUpdateChecker;
use YahnisElsts\PluginUpdateChecker\v5p3\Scheduler;
use YahnisElsts\PluginUpdateChecker\v5p3\DebugBar;
use YahnisElsts\PluginUpdateChecker\v5p6\InstalledPackage;
use YahnisElsts\PluginUpdateChecker\v5p6\UpdateChecker as BaseUpdateChecker;
use YahnisElsts\PluginUpdateChecker\v5p6\Scheduler;
use YahnisElsts\PluginUpdateChecker\v5p6\DebugBar;
if ( !class_exists(UpdateChecker::class, false) ):
@ -17,7 +17,7 @@ if ( !class_exists(UpdateChecker::class, false) ):
*/
class UpdateChecker extends BaseUpdateChecker {
protected $updateTransient = 'update_plugins';
protected $translationType = 'plugin';
protected $componentType = 'plugin';
public $pluginAbsolutePath = ''; //Full path of the main plugin file.
public $pluginFile = ''; //Plugin filename relative to the plugins directory. Many WP APIs use this to identify plugins.

View File

@ -1,10 +1,10 @@
<?php
namespace YahnisElsts\PluginUpdateChecker\v5p3;
namespace YahnisElsts\PluginUpdateChecker\v5p6;
use YahnisElsts\PluginUpdateChecker\v5p3\Plugin;
use YahnisElsts\PluginUpdateChecker\v5p3\Theme;
use YahnisElsts\PluginUpdateChecker\v5p3\Vcs;
use YahnisElsts\PluginUpdateChecker\v5p6\Plugin;
use YahnisElsts\PluginUpdateChecker\v5p6\Theme;
use YahnisElsts\PluginUpdateChecker\v5p6\Vcs;
if ( !class_exists(PucFactory::class, false) ):
@ -86,7 +86,7 @@ if ( !class_exists(PucFactory::class, false) ):
throw new \RuntimeException(sprintf(
'The update checker cannot determine if "%s" is a plugin or a theme. ' .
'This is a bug. Please contact the PUC developer.',
htmlentities($fullPath)
esc_html($fullPath)
));
}
@ -147,7 +147,7 @@ if ( !class_exists(PucFactory::class, false) ):
*
* Normalize a filesystem path. Introduced in WP 3.9.
* Copying here allows use of the class on earlier versions.
* This version adapted from WP 4.8.2 (unchanged since 4.5.3)
* This version adapted from WP 4.8.2 (unchanged since 4.5.6)
*
* @param string $path Path to normalize.
* @return string Normalized path.
@ -239,7 +239,7 @@ if ( !class_exists(PucFactory::class, false) ):
//URI was not found so throw an error.
throw new \RuntimeException(
sprintf('Unable to locate URI in header of "%s"', htmlentities($fullPath))
sprintf('Unable to locate URI in header of "%s"', esc_html($fullPath))
);
}

View File

@ -1,5 +1,5 @@
<?php
namespace YahnisElsts\PluginUpdateChecker\v5p3;
namespace YahnisElsts\PluginUpdateChecker\v5p6;
if ( !class_exists(Scheduler::class, false) ):
@ -87,6 +87,7 @@ if ( !class_exists(Scheduler::class, false) ):
add_action($hook, array($this, 'maybeCheckForUpdates'));
}
//This hook fires after a bulk update is complete.
add_action('upgrader_process_complete', array($this, 'removeHooksIfLibraryGone'), 1, 0);
add_action('upgrader_process_complete', array($this, 'upgraderProcessComplete'), 11, 2);
} else {
@ -95,6 +96,22 @@ if ( !class_exists(Scheduler::class, false) ):
}
}
/**
* Remove all hooks if this version of PUC has been deleted or overwritten.
*
* Callback for the "upgrader_process_complete" action.
*/
public function removeHooksIfLibraryGone() {
//Cancel all further actions if the current version of PUC has been deleted or overwritten
//by a different version during the upgrade. If we try to do anything more in that situation,
//we could trigger a fatal error by trying to autoload a deleted class.
clearstatcache();
if ( !file_exists(__FILE__) ) {
$this->removeHooks();
$this->updateChecker->removeHooks();
}
}
/**
* Runs upon the WP action upgrader_process_complete.
*
@ -108,16 +125,6 @@ if ( !class_exists(Scheduler::class, false) ):
/** @noinspection PhpUnusedParameterInspection */
$upgrader, $upgradeInfo
) {
//Cancel all further actions if the current version of PUC has been deleted or overwritten
//by a different version during the upgrade. If we try to do anything more in that situation,
//we could trigger a fatal error by trying to autoload a deleted class.
clearstatcache();
if ( !file_exists(__FILE__) ) {
$this->removeHooks();
$this->updateChecker->removeHooks();
return;
}
//Sanity check and limitation to relevant types.
if (
!is_array($upgradeInfo) || !isset($upgradeInfo['type'], $upgradeInfo['action'])
@ -180,6 +187,21 @@ if ( !class_exists(Scheduler::class, false) ):
$state = $this->updateChecker->getUpdateState();
$shouldCheck = ($state->timeSinceLastCheck() >= $this->getEffectiveCheckPeriod());
if ( $shouldCheck ) {
//Sanity check: Do not proceed if one of the critical classes is missing.
//That can happen - theoretically and extremely rarely - if maybeCheckForUpdates()
//is called before the old version of our plugin has been fully deleted, or
//called from an independent AJAX request during deletion.
if ( !(
class_exists(Utils::class)
&& class_exists(Metadata::class)
&& class_exists(Plugin\Update::class)
&& class_exists(Theme\Update::class)
) ) {
return;
}
}
//Let plugin authors substitute their own algorithm.
$shouldCheck = apply_filters(
$this->updateChecker->getUniqueName('check_now'),

View File

@ -1,5 +1,5 @@
<?php
namespace YahnisElsts\PluginUpdateChecker\v5p3;
namespace YahnisElsts\PluginUpdateChecker\v5p6;
if ( !class_exists(StateStore::class, false) ):
@ -77,7 +77,7 @@ if ( !class_exists(StateStore::class, false) ):
* @param Update|null $update
* @return $this
*/
public function setUpdate(Update $update = null) {
public function setUpdate($update = null) {
$this->lazyLoad();
$this->update = $update;
return $this;
@ -163,7 +163,12 @@ if ( !class_exists(StateStore::class, false) ):
$state = get_site_option($this->optionName, null);
if ( !is_object($state) ) {
if (
!is_object($state)
//Sanity check: If the Utils class is missing, the plugin is probably in the process
//of being deleted (e.g. the old version gets deleted during an update).
|| !class_exists(Utils::class)
) {
$this->lastCheck = 0;
$this->checkedVersion = '';
$this->update = null;

View File

@ -1,7 +1,7 @@
<?php
namespace YahnisElsts\PluginUpdateChecker\v5p3\Theme;
namespace YahnisElsts\PluginUpdateChecker\v5p6\Theme;
use YahnisElsts\PluginUpdateChecker\v5p3\InstalledPackage;
use YahnisElsts\PluginUpdateChecker\v5p6\InstalledPackage;
if ( !class_exists(Package::class, false) ):

View File

@ -1,8 +1,8 @@
<?php
namespace YahnisElsts\PluginUpdateChecker\v5p3\Theme;
namespace YahnisElsts\PluginUpdateChecker\v5p6\Theme;
use YahnisElsts\PluginUpdateChecker\v5p3\Update as BaseUpdate;
use YahnisElsts\PluginUpdateChecker\v5p6\Update as BaseUpdate;
if ( !class_exists(Update::class, false) ):

View File

@ -1,18 +1,18 @@
<?php
namespace YahnisElsts\PluginUpdateChecker\v5p3\Theme;
namespace YahnisElsts\PluginUpdateChecker\v5p6\Theme;
use YahnisElsts\PluginUpdateChecker\v5p3\UpdateChecker as BaseUpdateChecker;
use YahnisElsts\PluginUpdateChecker\v5p3\InstalledPackage;
use YahnisElsts\PluginUpdateChecker\v5p3\Scheduler;
use YahnisElsts\PluginUpdateChecker\v5p3\DebugBar;
use YahnisElsts\PluginUpdateChecker\v5p6\UpdateChecker as BaseUpdateChecker;
use YahnisElsts\PluginUpdateChecker\v5p6\InstalledPackage;
use YahnisElsts\PluginUpdateChecker\v5p6\Scheduler;
use YahnisElsts\PluginUpdateChecker\v5p6\DebugBar;
if ( !class_exists(UpdateChecker::class, false) ):
class UpdateChecker extends BaseUpdateChecker {
protected $filterSuffix = 'theme';
protected $updateTransient = 'update_themes';
protected $translationType = 'theme';
protected $componentType = 'theme';
/**
* @var string Theme directory name.

View File

@ -1,5 +1,5 @@
<?php
namespace YahnisElsts\PluginUpdateChecker\v5p3;
namespace YahnisElsts\PluginUpdateChecker\v5p6;
use stdClass;

View File

@ -1,5 +1,5 @@
<?php
namespace YahnisElsts\PluginUpdateChecker\v5p3;
namespace YahnisElsts\PluginUpdateChecker\v5p6;
use stdClass;
use WP_Error;
@ -9,7 +9,16 @@ if ( !class_exists(UpdateChecker::class, false) ):
abstract class UpdateChecker {
protected $filterSuffix = '';
protected $updateTransient = '';
protected $translationType = ''; //This can be "plugin" or "theme".
/**
* @var string This can be "plugin" or "theme".
*/
protected $componentType = '';
/**
* @var string Currently the same as $componentType, but this is an implementation detail that
* depends on how WP works internally, and could therefore change.
*/
protected $translationType = '';
/**
* Set to TRUE to enable error reporting. Errors are raised using trigger_error()
@ -74,6 +83,11 @@ if ( !class_exists(UpdateChecker::class, false) ):
*/
protected $debugBarExtension = null;
/**
* @var WpCliCheckTrigger|null
*/
protected $wpCliCheckTrigger = null;
public function __construct($metadataUrl, $directoryName, $slug = null, $checkPeriod = 12, $optionName = '') {
$this->debugMode = (bool)(constant('WP_DEBUG'));
$this->metadataUrl = $metadataUrl;
@ -91,6 +105,10 @@ if ( !class_exists(UpdateChecker::class, false) ):
}
}
if ( empty($this->translationType) ) {
$this->translationType = $this->componentType;
}
$this->package = $this->createInstalledPackage();
$this->scheduler = $this->createScheduler($checkPeriod);
$this->upgraderStatus = new UpgraderStatus();
@ -103,6 +121,10 @@ if ( !class_exists(UpdateChecker::class, false) ):
}
$this->installHooks();
if ( ($this->wpCliCheckTrigger === null) && defined('WP_CLI') ) {
$this->wpCliCheckTrigger = new WpCliCheckTrigger($this->componentType, $this->scheduler);
}
}
/**
@ -149,6 +171,10 @@ if ( !class_exists(UpdateChecker::class, false) ):
//Allow HTTP requests to the metadata URL even if it's on a local host.
add_filter('http_request_host_is_external', array($this, 'allowMetadataHost'), 10, 2);
//Potentially exclude information about this entity from core update check requests to api.wordpress.org.
//phpcs:ignore WordPressVIPMinimum.Hooks.RestrictedHooks.http_request_args -- Doesn't modify timeouts.
add_filter('http_request_args', array($this, 'excludeEntityFromWordPressAPI'), 10, 2);
//DebugBar integration.
if ( did_action('plugins_loaded') ) {
$this->maybeInitDebugBar();
@ -170,6 +196,7 @@ if ( !class_exists(UpdateChecker::class, false) ):
remove_filter('upgrader_source_selection', array($this, 'fixDirectoryName'), 10);
remove_filter('http_request_host_is_external', array($this, 'allowMetadataHost'), 10);
remove_filter('http_request_args', array($this, 'excludeEntityFromWordPressAPI'));
remove_action('plugins_loaded', array($this, 'maybeInitDebugBar'));
remove_action('init', array($this, 'loadTextDomain'));
@ -244,6 +271,76 @@ if ( !class_exists(UpdateChecker::class, false) ):
*/
abstract protected function createScheduler($checkPeriod);
/**
* Remove information about this plugin or theme from the requests that WordPress core sends
* to api.wordpress.org when checking for updates.
*
* @param array $args
* @param string $url
* @return array
*/
public function excludeEntityFromWordPressAPI($args, $url) {
//Is this an api.wordpress.org update check request?
$parsedUrl = wp_parse_url($url);
if ( !isset($parsedUrl['host']) || (strtolower($parsedUrl['host']) !== 'api.wordpress.org') ) {
return $args;
}
$typePluralised = $this->componentType . 's';
$expectedPathPrefix = '/' . $typePluralised . '/update-check/1.'; //e.g. "/plugins/update-check/1.1/"
if ( !isset($parsedUrl['path']) || !Utils::startsWith($parsedUrl['path'], $expectedPathPrefix) ) {
return $args;
}
//Plugins and themes can disable this feature by using the filter below.
if ( !apply_filters(
$this->getUniqueName('remove_from_default_update_checks'),
true, $this, $args, $url
) ) {
return $args;
}
if ( empty($args['body'][$typePluralised]) ) {
return $args;
}
$reportingItems = json_decode($args['body'][$typePluralised], true);
if ( $reportingItems === null ) {
return $args;
}
//The list of installed items uses different key formats for plugins and themes.
//Luckily, we can reuse the getUpdateListKey() method here.
$updateListKey = $this->getUpdateListKey();
if ( isset($reportingItems[$typePluralised][$updateListKey]) ) {
unset($reportingItems[$typePluralised][$updateListKey]);
}
if ( !empty($reportingItems['active']) ) {
if ( is_array($reportingItems['active']) ) {
foreach ($reportingItems['active'] as $index => $relativePath) {
if ( $relativePath === $updateListKey ) {
unset($reportingItems['active'][$index]);
}
}
//Re-index the array.
$reportingItems['active'] = array_values($reportingItems['active']);
} else if ( $reportingItems['active'] === $updateListKey ) {
//For themes, the "active" field is a string that contains the theme's directory name.
//Pretend that the default theme is active so that we don't reveal the actual theme.
if ( defined('WP_DEFAULT_THEME') ) {
$reportingItems['active'] = WP_DEFAULT_THEME;
}
//Unfortunately, it doesn't seem to be documented if we can safely remove the "active"
//key. So when we don't know the default theme, we'll just leave it as is.
}
}
$args['body'][$typePluralised] = wp_json_encode($reportingItems);
return $args;
}
/**
* Check for updates. The results are stored in the DB option specified in $optionName.
*
@ -362,7 +459,7 @@ if ( !class_exists(UpdateChecker::class, false) ):
*
* @param Metadata|null $update
*/
protected function fixSupportedWordpressVersion(Metadata $update = null) {
protected function fixSupportedWordpressVersion($update = null) {
if ( !isset($update->tested) || !preg_match('/^\d++\.\d++$/', $update->tested) ) {
return;
}
@ -676,7 +773,7 @@ if ( !class_exists(UpdateChecker::class, false) ):
$result = wp_remote_get($url, $options);
$result = apply_filters($this->getUniqueName('request_metadata_http_result'), $result, $url, $options);
//Try to parse the response
$status = $this->validateApiResponse($result);
$metadata = null;
@ -902,25 +999,62 @@ if ( !class_exists(UpdateChecker::class, false) ):
return $source;
}
//Fix the remote source structure if necessary.
//The update archive should contain a single directory that contains the rest of plugin/theme files.
//Otherwise, WordPress will try to copy the entire working directory ($source == $remoteSource).
//We can't rename $remoteSource because that would break WordPress code that cleans up temporary files
//after update.
if ( $this->isBadDirectoryStructure($remoteSource) ) {
//Create a new directory using the plugin slug.
$newDirectory = trailingslashit($remoteSource) . $this->slug . '/';
if ( !$wp_filesystem->is_dir($newDirectory) ) {
$wp_filesystem->mkdir($newDirectory);
//Move all files to the newly created directory.
$sourceFiles = $wp_filesystem->dirlist($remoteSource);
if ( is_array($sourceFiles) ) {
$sourceFiles = array_keys($sourceFiles);
$allMoved = true;
foreach ($sourceFiles as $filename) {
//Skip our newly created folder.
if ( $filename === $this->slug ) {
continue;
}
$previousSource = trailingslashit($remoteSource) . $filename;
$newSource = trailingslashit($newDirectory) . $filename;
if ( !$wp_filesystem->move($previousSource, $newSource, true) ) {
$allMoved = false;
break;
}
}
if ( $allMoved ) {
//Rename source.
$source = $newDirectory;
} else {
//Delete our newly created folder including all files in it.
$wp_filesystem->rmdir($newDirectory, true);
//And return a relevant error.
return new WP_Error(
'puc-incorrect-directory-structure',
sprintf(
'The directory structure of the update was incorrect. All files should be inside ' .
'a directory named <span class="code">%s</span>, not at the root of the ZIP archive. Plugin Update Checker tried to fix the directory structure, but failed.',
esc_html($this->slug)
)
);
}
}
}
}
//Rename the source to match the existing directory.
$correctedSource = trailingslashit($remoteSource) . $this->directoryName . '/';
if ( $source !== $correctedSource ) {
//The update archive should contain a single directory that contains the rest of plugin/theme files.
//Otherwise, WordPress will try to copy the entire working directory ($source == $remoteSource).
//We can't rename $remoteSource because that would break WordPress code that cleans up temporary files
//after update.
if ( $this->isBadDirectoryStructure($remoteSource) ) {
return new WP_Error(
'puc-incorrect-directory-structure',
sprintf(
'The directory structure of the update is incorrect. All files should be inside ' .
'a directory named <span class="code">%s</span>, not at the root of the ZIP archive.',
htmlentities($this->slug)
)
);
}
/** @var \WP_Upgrader_Skin $upgrader ->skin */
$upgrader->skin->feedback(sprintf(
'Renaming %s to %s&#8230;',
'<span class="code">' . basename($source) . '</span>',

View File

@ -1,5 +1,5 @@
<?php
namespace YahnisElsts\PluginUpdateChecker\v5p3;
namespace YahnisElsts\PluginUpdateChecker\v5p6;
if ( !class_exists(UpgraderStatus::class, false) ):

View File

@ -1,5 +1,5 @@
<?php
namespace YahnisElsts\PluginUpdateChecker\v5p3;
namespace YahnisElsts\PluginUpdateChecker\v5p6;
if ( !class_exists(Utils::class, false) ):

View File

@ -1,6 +1,6 @@
<?php
namespace YahnisElsts\PluginUpdateChecker\v5p3\Vcs;
namespace YahnisElsts\PluginUpdateChecker\v5p6\Vcs;
use Parsedown;
use PucReadmeParser;

View File

@ -1,5 +1,5 @@
<?php
namespace YahnisElsts\PluginUpdateChecker\v5p3\Vcs;
namespace YahnisElsts\PluginUpdateChecker\v5p6\Vcs;
if ( !interface_exists(BaseChecker::class, false) ):

View File

@ -1,9 +1,9 @@
<?php
namespace YahnisElsts\PluginUpdateChecker\v5p3\Vcs;
namespace YahnisElsts\PluginUpdateChecker\v5p6\Vcs;
use YahnisElsts\PluginUpdateChecker\v5p3\OAuthSignature;
use YahnisElsts\PluginUpdateChecker\v5p3\Utils;
use YahnisElsts\PluginUpdateChecker\v5p6\OAuthSignature;
use YahnisElsts\PluginUpdateChecker\v5p6\Utils;
if ( !class_exists(BitBucketApi::class, false) ):

View File

@ -1,6 +1,6 @@
<?php
namespace YahnisElsts\PluginUpdateChecker\v5p3\Vcs;
namespace YahnisElsts\PluginUpdateChecker\v5p6\Vcs;
use Parsedown;

View File

@ -1,6 +1,6 @@
<?php
namespace YahnisElsts\PluginUpdateChecker\v5p3\Vcs;
namespace YahnisElsts\PluginUpdateChecker\v5p6\Vcs;
if ( !class_exists(GitLabApi::class, false) ):

View File

@ -1,8 +1,8 @@
<?php
namespace YahnisElsts\PluginUpdateChecker\v5p3\Vcs;
namespace YahnisElsts\PluginUpdateChecker\v5p6\Vcs;
use YahnisElsts\PluginUpdateChecker\v5p3\Plugin;
use YahnisElsts\PluginUpdateChecker\v5p6\Plugin;
if ( !class_exists(PluginUpdateChecker::class, false) ):

View File

@ -1,5 +1,5 @@
<?php
namespace YahnisElsts\PluginUpdateChecker\v5p3\Vcs;
namespace YahnisElsts\PluginUpdateChecker\v5p6\Vcs;
if ( !class_exists(Reference::class, false) ):

View File

@ -1,6 +1,6 @@
<?php
namespace YahnisElsts\PluginUpdateChecker\v5p3\Vcs;
namespace YahnisElsts\PluginUpdateChecker\v5p6\Vcs;
if ( !trait_exists(ReleaseAssetSupport::class, false) ) :

View File

@ -1,6 +1,6 @@
<?php
namespace YahnisElsts\PluginUpdateChecker\v5p3\Vcs;
namespace YahnisElsts\PluginUpdateChecker\v5p6\Vcs;
if ( !trait_exists(ReleaseFilteringFeature::class, false) ) :

View File

@ -1,9 +1,9 @@
<?php
namespace YahnisElsts\PluginUpdateChecker\v5p3\Vcs;
namespace YahnisElsts\PluginUpdateChecker\v5p6\Vcs;
use YahnisElsts\PluginUpdateChecker\v5p3\Theme;
use YahnisElsts\PluginUpdateChecker\v5p3\Utils;
use YahnisElsts\PluginUpdateChecker\v5p6\Theme;
use YahnisElsts\PluginUpdateChecker\v5p6\Utils;
if ( !class_exists(ThemeUpdateChecker::class, false) ):

View File

@ -1,6 +1,6 @@
<?php
namespace YahnisElsts\PluginUpdateChecker\v5p3\Vcs;
namespace YahnisElsts\PluginUpdateChecker\v5p6\Vcs;
if ( !trait_exists(VcsCheckerMethods::class, false) ) :

View File

@ -0,0 +1,84 @@
<?php
namespace YahnisElsts\PluginUpdateChecker\v5p6;
use WP_CLI;
/**
* Triggers an update check when relevant WP-CLI commands are executed.
*
* When WP-CLI runs certain commands like "wp plugin status" or "wp theme list", it calls
* wp_update_plugins() and wp_update_themes() to refresh update information. This class hooks into
* the same commands and triggers an update check when they are executed.
*
* Note that wp_update_plugins() and wp_update_themes() do not perform an update check *every* time
* they are called. They use a context-dependent delay between update checks. Similarly, this class
* calls Scheduler::maybeCheckForUpdates(), which also dynamically decides whether to actually
* run a check. If you want to force an update check, call UpdateChecker::checkForUpdates() instead.
*/
class WpCliCheckTrigger {
/**
* @var Scheduler
*/
private $scheduler;
/**
* @var string 'plugin' or 'theme'
*/
private $componentType;
/**
* @var bool Whether an update check was already triggered during the current request
* or script execution.
*/
private $wasCheckTriggered = false;
public function __construct($componentType, Scheduler $scheduler) {
if ( !in_array($componentType, ['plugin', 'theme']) ) {
throw new \InvalidArgumentException('Invalid component type. Must be "plugin" or "theme".');
}
$this->componentType = $componentType;
$this->scheduler = $scheduler;
if ( !defined('WP_CLI') || !class_exists(WP_CLI::class, false) ) {
return; //Nothing to do if WP-CLI is not available.
}
/*
* We can't hook directly into wp_update_plugins(), but we can hook into the WP-CLI
* commands that call it. We'll use the "before_invoke:xyz" hook to trigger update checks.
*/
foreach ($this->getRelevantCommands() as $command) {
WP_CLI::add_hook('before_invoke:' . $command, [$this, 'triggerUpdateCheckOnce']);
}
}
private function getRelevantCommands() {
$result = [];
foreach (['status', 'list', 'update'] as $subcommand) {
$result[] = $this->componentType . ' ' . $subcommand;
}
return $result;
}
/**
* Trigger a potential update check once.
*
* @param mixed $input
* @return mixed The input value, unchanged.
* @internal This method is public so that it can be used as a WP-CLI hook callback.
* It should not be called directly.
*
*/
public function triggerUpdateCheckOnce($input = null) {
if ( $this->wasCheckTriggered ) {
return $input;
}
$this->wasCheckTriggered = true;
$this->scheduler->maybeCheckForUpdates();
return $input;
}
}

View File

@ -29,7 +29,7 @@ From the users' perspective, it works just like with plugins and themes hosted o
Getting Started
---------------
*Note:* In each of the below examples, part of the instructions are to create an instance of the update checker class. It's recommended to do this either during the `plugins_loaded` action or outside of any hooks. If you do it only during an `admin_*` action, then updates will not be visible to a wide variety of WordPress maanagement tools; they will only be visible to logged-in users on dashboard pages.
*Note:* In each of the below examples, part of the instructions is to create an instance of the update checker class. It's recommended to do this either during the `plugins_loaded` action or outside of any hooks. If you do it only during an `admin_*` action, then updates will not be visible to a wide variety of WordPress management tools; they will only be visible to logged-in users on dashboard pages.
### Self-hosted Plugins and Themes
@ -252,8 +252,8 @@ BitBucket doesn't have an equivalent to GitHub's releases, so the process is sli
Alternatively, if you're using a self-hosted GitLab instance, initialize the update checker like this:
```php
use YahnisElsts\PluginUpdateChecker\v5p3\Vcs\PluginUpdateChecker;
use YahnisElsts\PluginUpdateChecker\v5p3\Vcs\GitLabApi;
use YahnisElsts\PluginUpdateChecker\v5p6\Vcs\PluginUpdateChecker;
use YahnisElsts\PluginUpdateChecker\v5p6\Vcs\GitLabApi;
$myUpdateChecker = new PluginUpdateChecker(
new GitLabApi('https://myserver.com/user-name/repo-name/'),
@ -264,8 +264,8 @@ BitBucket doesn't have an equivalent to GitHub's releases, so the process is sli
```
If you're using a self-hosted GitLab instance and [subgroups or nested groups](https://docs.gitlab.com/ce/user/group/subgroups/index.html), you have to tell the update checker which parts of the URL are subgroups:
```php
use YahnisElsts\PluginUpdateChecker\v5p3\Vcs\PluginUpdateChecker;
use YahnisElsts\PluginUpdateChecker\v5p3\Vcs\GitLabApi;
use YahnisElsts\PluginUpdateChecker\v5p6\Vcs\PluginUpdateChecker;
use YahnisElsts\PluginUpdateChecker\v5p6\Vcs\GitLabApi;
$myUpdateChecker = new PluginUpdateChecker(
new GitLabApi(
@ -347,14 +347,14 @@ Other classes have also been renamed, usually by simply removing the `Puc_vXpY_`
| Old class name | New class name |
|-------------------------------------|----------------------------------------------------------------|
| `Puc_v4_Factory` | `YahnisElsts\PluginUpdateChecker\v5\PucFactory` |
| `Puc_v4p13_Factory` | `YahnisElsts\PluginUpdateChecker\v5p3\PucFactory` |
| `Puc_v4p13_Plugin_UpdateChecker` | `YahnisElsts\PluginUpdateChecker\v5p3\Plugin\UpdateChecker` |
| `Puc_v4p13_Theme_UpdateChecker` | `YahnisElsts\PluginUpdateChecker\v5p3\Theme\UpdateChecker` |
| `Puc_v4p13_Vcs_PluginUpdateChecker` | `YahnisElsts\PluginUpdateChecker\v5p3\Vcs\PluginUpdateChecker` |
| `Puc_v4p13_Vcs_ThemeUpdateChecker` | `YahnisElsts\PluginUpdateChecker\v5p3\Vcs\ThemeUpdateChecker` |
| `Puc_v4p13_Vcs_GitHubApi` | `YahnisElsts\PluginUpdateChecker\v5p3\Vcs\GitHubApi` |
| `Puc_v4p13_Vcs_GitLabApi` | `YahnisElsts\PluginUpdateChecker\v5p3\Vcs\GitLabApi` |
| `Puc_v4p13_Vcs_BitBucketApi` | `YahnisElsts\PluginUpdateChecker\v5p3\Vcs\BitBucketApi` |
| `Puc_v4p13_Factory` | `YahnisElsts\PluginUpdateChecker\v5p6\PucFactory` |
| `Puc_v4p13_Plugin_UpdateChecker` | `YahnisElsts\PluginUpdateChecker\v5p6\Plugin\UpdateChecker` |
| `Puc_v4p13_Theme_UpdateChecker` | `YahnisElsts\PluginUpdateChecker\v5p6\Theme\UpdateChecker` |
| `Puc_v4p13_Vcs_PluginUpdateChecker` | `YahnisElsts\PluginUpdateChecker\v5p6\Vcs\PluginUpdateChecker` |
| `Puc_v4p13_Vcs_ThemeUpdateChecker` | `YahnisElsts\PluginUpdateChecker\v5p6\Vcs\ThemeUpdateChecker` |
| `Puc_v4p13_Vcs_GitHubApi` | `YahnisElsts\PluginUpdateChecker\v5p6\Vcs\GitHubApi` |
| `Puc_v4p13_Vcs_GitLabApi` | `YahnisElsts\PluginUpdateChecker\v5p6\Vcs\GitLabApi` |
| `Puc_v4p13_Vcs_BitBucketApi` | `YahnisElsts\PluginUpdateChecker\v5p6\Vcs\BitBucketApi` |
License Management
------------------

View File

@ -18,6 +18,6 @@
"ext-json": "*"
},
"autoload": {
"files": ["load-v5p3.php"]
"files": ["load-v5p6.php"]
}
}

Binary file not shown.

View File

@ -0,0 +1,52 @@
msgid ""
msgstr ""
"Project-Id-Version: plugin-update-checker\n"
"POT-Creation-Date: 2022-07-29 15:34+0300\n"
"PO-Revision-Date: 2024-05-09 22:22+0000\n"
"Last-Translator: theogk\n"
"Language-Team: Ελληνικά\n"
"Language: el\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=n != 1;\n"
"X-Generator: Loco https://localise.biz/\n"
"X-Poedit-Basepath: ..\n"
"X-Poedit-SourceCharset: UTF-8\n"
"X-Poedit-KeywordsList: __;_e;_x:1,2c;_x\n"
"X-Poedit-SearchPath-0: .\n"
"Report-Msgid-Bugs-To: \n"
"X-Loco-Version: 2.6.9; wp-6.5.3"
#: Puc/v5p4/Plugin/Ui.php:128
msgid "Check for updates"
msgstr "Έλεγχος για ενημερώσεις"
#: Puc/v5p4/Plugin/Ui.php:214
#, php-format
msgctxt "the plugin title"
msgid "The %s plugin is up to date."
msgstr "Το πρόσθετο %s είναι ενημερωμένο."
#: Puc/v5p4/Plugin/Ui.php:216
#, php-format
msgctxt "the plugin title"
msgid "A new version of the %s plugin is available."
msgstr "Μία νέα έκδοση είναι διαθέσιμη για το πρόσθετο %s."
#: Puc/v5p4/Plugin/Ui.php:218
#, php-format
msgctxt "the plugin title"
msgid "Could not determine if updates are available for %s."
msgstr ""
"Δεν ήταν εφικτό να εκτελεστεί ο έλεγχος για νέες ενημερώσεις για το πρόσθετο "
"%s."
#: Puc/v5p4/Plugin/Ui.php:224
#, php-format
msgid "Unknown update checker status \"%s\""
msgstr "Άγνωστο πρόβλημα του ενημερωτή προσθέτων \"%s\""
#: Puc/v5p4/Vcs/PluginUpdateChecker.php:100
msgid "There is no changelog available."
msgstr "Δεν υπάρχει διαθέσιμο αρχείο αλλαγών."

View File

@ -1,38 +1,50 @@
msgid ""
msgstr ""
"Project-Id-Version: plugin-update-checker\n"
"POT-Creation-Date: 2016-02-17 14:21+0100\n"
"PO-Revision-Date: 2016-10-28 14:30+0330\n"
"Last-Translator: studio RVOLA <hello@rvola.com>\n"
"Language-Team: Pro Style <info@prostyle.ir>\n"
"POT-Creation-Date: 2025-06-12 23:40+0100\n"
"PO-Revision-Date: 2025-06-12 23:49+0100\n"
"Last-Translator: Pro Style <info@prostyle.ir>\n"
"Language-Team: Alex Javadi <alex@aljm.org>\n"
"Language: fa_IR\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Generator: Poedit 1.8.8\n"
"X-Poedit-Basepath: ..\n"
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
"X-Generator: Poedit 3.6\n"
"X-Poedit-Basepath: ..\n"
"X-Poedit-SourceCharset: UTF-8\n"
"X-Poedit-KeywordsList: __;_e\n"
"X-Poedit-SearchPath-0: .\n"
#: github-checker.php:120
msgid "There is no changelog available."
msgstr "شرحی برای تغییرات یافت نشد"
#: Puc/v5p6/Plugin/Ui.php:56
msgid "View details"
msgstr "مشاهده جزئیات"
#: plugin-update-checker.php:637
#: Puc/v5p6/Plugin/Ui.php:79
#, php-format
msgid "More information about %s"
msgstr "اطلاعات بیشتر درباره %s"
# It had some “potential” grammar issues and also didnt sound native.
# P.S. I know the current translation is literally “Checking for new updates”, however, I thought it might sound more natural and known to others.
#: Puc/v5p6/Plugin/Ui.php:130
msgid "Check for updates"
msgstr "بررسی برای بروزرسانی "
msgstr "بررسی بروزرسانی جدید"
#: plugin-update-checker.php:681
msgid "This plugin is up to date."
msgstr "شما از آخرین نسخه استفاده میکنید . به‌روز باشید"
#: plugin-update-checker.php:683
msgid "A new version of this plugin is available."
msgstr "نسخه جدیدی برای افزونه ارائه شده است ."
#: plugin-update-checker.php:685
# The word “ناشناخته” is seems to be translated directly from the word (Un-known), rather than checking for the context.
# I think “نامشخص” (unknown) might be a suitable version in this scenario.
#: Puc/v5p6/Plugin/Ui.php:227
#, php-format
msgid "Unknown update checker status \"%s\""
msgstr "وضعیت ناشناخته برای بروزرسانی \"%s\""
msgstr "وضعیت نامشخص برای بروزرسانی \"%s\""
# The previous translation was okay, however, it didnt sound native to me.
#: Puc/v5p6/Vcs/PluginUpdateChecker.php:113
msgid "There is no changelog available."
msgstr "آخرین تغییراتی یافت نشد."
#~ msgid "This plugin is up to date."
#~ msgstr "شما از آخرین نسخه استفاده میکنید . به‌روز باشید"
#~ msgid "A new version of this plugin is available."
#~ msgstr "نسخه جدیدی برای افزونه ارائه شده است ."

Binary file not shown.

View File

@ -0,0 +1,57 @@
msgid ""
msgstr ""
"Project-Id-Version: plugin-update-checker\n"
"POT-Creation-Date: 2024-12-09 11:45+0100\n"
"PO-Revision-Date: 2024-12-09 12:20+0100\n"
"Last-Translator: Aleksandar Urošević <urke.kg@gmail.com>\n"
"Language-Team: \n"
"Language: sr_RS\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
"X-Generator: Poedit 2.4.3\n"
"X-Poedit-Basepath: ..\n"
"X-Poedit-SourceCharset: UTF-8\n"
"X-Poedit-KeywordsList: __;_e;_x:1,2c;_x\n"
"X-Poedit-SearchPath-0: .\n"
#: Puc/v5p5/Plugin/Ui.php:56
msgid "View details"
msgstr "Види детаље"
#: Puc/v5p5/Plugin/Ui.php:79
#, php-format
msgid "More information about %s"
msgstr "Више информација о %s"
#: Puc/v5p5/Plugin/Ui.php:130
msgid "Check for updates"
msgstr "Провера ажурирања"
#: Puc/v5p5/Plugin/Ui.php:217
#, php-format
msgctxt "the plugin title"
msgid "The %s plugin is up to date."
msgstr "Додатак %s је у најновијем издању."
#: Puc/v5p5/Plugin/Ui.php:219
#, php-format
msgctxt "the plugin title"
msgid "A new version of the %s plugin is available."
msgstr "Доступно је ново издање за %s."
#: Puc/v5p5/Plugin/Ui.php:221
#, php-format
msgctxt "the plugin title"
msgid "Could not determine if updates are available for %s."
msgstr "Није могуће утврдити да ли су доступне исправке за %s."
#: Puc/v5p5/Plugin/Ui.php:227
#, php-format
msgid "Unknown update checker status \"%s\""
msgstr "Непознат статус провере ажурирања \"%s\""
#: Puc/v5p5/Vcs/PluginUpdateChecker.php:113
msgid "There is no changelog available."
msgstr "Белешке о изменама нису доступне."

View File

@ -1,57 +1,57 @@
msgid ""
msgstr ""
"Project-Id-Version: plugin-update-checker\n"
"POT-Creation-Date: 2022-01-29 12:09+0800\n"
"PO-Revision-Date: 2022-01-29 12:10+0800\n"
"POT-Creation-Date: 2025-11-21 10:40+0800\n"
"PO-Revision-Date: 2025-11-21 10:40+0800\n"
"Last-Translator: Seaton Jiang <hi@seatonjiang.com>\n"
"Language-Team: \n"
"Language: zh_CN\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Generator: Poedit 2.4.3\n"
"X-Poedit-Basepath: ..\n"
"Plural-Forms: nplurals=1; plural=0;\n"
"X-Generator: Poedit 3.8\n"
"X-Poedit-Basepath: ..\n"
"X-Poedit-SourceCharset: UTF-8\n"
"X-Poedit-KeywordsList: __;_e;_x:1,2c;_x\n"
"X-Poedit-SearchPath-0: .\n"
#: Puc/v4p11/Plugin/Ui.php:54
#: Puc/v5p6/Plugin/Ui.php:56
msgid "View details"
msgstr "查看详情"
#: Puc/v4p11/Plugin/Ui.php:77
#: Puc/v5p6/Plugin/Ui.php:79
#, php-format
msgid "More information about %s"
msgstr "%s 的更多信息"
#: Puc/v4p11/Plugin/Ui.php:128
#: Puc/v5p6/Plugin/Ui.php:130
msgid "Check for updates"
msgstr "检查更新"
#: Puc/v4p11/Plugin/Ui.php:214
#: Puc/v5p6/Plugin/Ui.php:217
#, php-format
msgctxt "the plugin title"
msgid "The %s plugin is up to date."
msgstr "%s 目前是最新版本。"
#: Puc/v4p11/Plugin/Ui.php:216
#: Puc/v5p6/Plugin/Ui.php:219
#, php-format
msgctxt "the plugin title"
msgid "A new version of the %s plugin is available."
msgstr "%s 当前有可用的更新。"
#: Puc/v4p11/Plugin/Ui.php:218
#: Puc/v5p6/Plugin/Ui.php:221
#, php-format
msgctxt "the plugin title"
msgid "Could not determine if updates are available for %s."
msgstr "%s 无法确定是否有可用的更新。"
#: Puc/v4p11/Plugin/Ui.php:224
#: Puc/v5p6/Plugin/Ui.php:227
#, php-format
msgid "Unknown update checker status \"%s\""
msgstr "未知的更新检查状态:%s"
#: Puc/v4p11/Vcs/PluginUpdateChecker.php:100
#: Puc/v5p6/Vcs/PluginUpdateChecker.php:113
msgid "There is no changelog available."
msgstr "没有可用的更新日志。"

Binary file not shown.

View File

@ -0,0 +1,66 @@
# Blank Plugin POT Template
# Copyright 2025 ...
# This file is distributed under the GNU General Public License v3 or later.
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: Plugin Update Checker\n"
"Report-Msgid-Bugs-To: Alex Lion <learnwithalex@gmail.com>\n"
"POT-Creation-Date: 2025-09-19 14:05-0700\n"
"PO-Revision-Date: \n"
"Last-Translator: Alex Lion <learnwithalex@gmail.com>\n"
"Language-Team: Alex Lion <learnwithalex@gmail.com>\n"
"Language: zh_TW\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=1; plural=0;\n"
"X-Poedit-WPHeader: plugin-update-checker.php\n"
"X-Textdomain-Support: yesX-Generator: Poedit 1.6.4\n"
"X-Poedit-SourceCharset: UTF-8\n"
"X-Poedit-KeywordsList: "
"__;_e;esc_html_e;esc_html_x:1,2c;esc_html__;esc_attr_e;esc_attr_x:1,2c;esc_attr__;_ex:1,2c;_nx:4c,1,2;_nx_noop:4c,1,2;_x:1,2c;_n:1,2;_n_noop:1,2;__ngettext:1,2;__ngettext_noop:1,2;_c,_nc:4c,1,2\n"
"X-Poedit-Basepath: ..\n"
"X-Poedit-Bookmarks: \n"
"X-Generator: Poedit 3.7\n"
"X-Poedit-SearchPath-0: .\n"
#: Puc/v5p6/Plugin/Ui.php:56
msgid "View details"
msgstr "檢視詳細資料"
#: Puc/v5p6/Plugin/Ui.php:79
#, php-format
msgid "More information about %s"
msgstr "進一步了解 %s 的相關資訊"
#: Puc/v5p6/Plugin/Ui.php:130
msgid "Check for updates"
msgstr "檢查更新"
#: Puc/v5p6/Plugin/Ui.php:217
#, php-format
msgctxt "the plugin title"
msgid "The %s plugin is up to date."
msgstr "%s 外掛已為最新版本。"
#: Puc/v5p6/Plugin/Ui.php:219
#, php-format
msgctxt "the plugin title"
msgid "A new version of the %s plugin is available."
msgstr "%s 外掛已有新版本可供更新。"
#: Puc/v5p6/Plugin/Ui.php:221
#, php-format
msgctxt "the plugin title"
msgid "Could not determine if updates are available for %s."
msgstr "無法確定 %s 是否有可用的更新。"
#: Puc/v5p6/Plugin/Ui.php:227
#, php-format
msgid "Unknown update checker status \"%s\""
msgstr "未知的更新檢查程式狀態: %s"
#: Puc/v5p6/Vcs/PluginUpdateChecker.php:113
msgid "There is no changelog available."
msgstr "目前沒有可供檢閱的變更記錄。"

View File

@ -2,7 +2,7 @@
msgid ""
msgstr ""
"Project-Id-Version: plugin-update-checker\n"
"POT-Creation-Date: 2022-07-29 15:34+0300\n"
"POT-Creation-Date: 2025-05-20 15:27+0300\n"
"PO-Revision-Date: 2016-01-10 20:59+0100\n"
"Last-Translator: \n"
"Language-Team: \n"
@ -11,39 +11,39 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
"X-Generator: Poedit 3.1.1\n"
"X-Generator: Poedit 3.6\n"
"X-Poedit-Basepath: ..\n"
"X-Poedit-SourceCharset: UTF-8\n"
"X-Poedit-KeywordsList: __;_e;_x:1,2c;_x\n"
"X-Poedit-SearchPath-0: .\n"
#: Puc/v5p3/Plugin/Ui.php:128
#: Puc/v5p6/Plugin/Ui.php:130
msgid "Check for updates"
msgstr ""
#: Puc/v5p3/Plugin/Ui.php:214
#: Puc/v5p6/Plugin/Ui.php:217
#, php-format
msgctxt "the plugin title"
msgid "The %s plugin is up to date."
msgstr ""
#: Puc/v5p3/Plugin/Ui.php:216
#: Puc/v5p6/Plugin/Ui.php:219
#, php-format
msgctxt "the plugin title"
msgid "A new version of the %s plugin is available."
msgstr ""
#: Puc/v5p3/Plugin/Ui.php:218
#: Puc/v5p6/Plugin/Ui.php:221
#, php-format
msgctxt "the plugin title"
msgid "Could not determine if updates are available for %s."
msgstr ""
#: Puc/v5p3/Plugin/Ui.php:224
#: Puc/v5p6/Plugin/Ui.php:227
#, php-format
msgid "Unknown update checker status \"%s\""
msgstr ""
#: Puc/v5p3/Vcs/PluginUpdateChecker.php:100
#: Puc/v5p6/Vcs/PluginUpdateChecker.php:113
msgid "There is no changelog available."
msgstr ""

View File

@ -1,14 +1,14 @@
<?php
namespace YahnisElsts\PluginUpdateChecker\v5p3;
namespace YahnisElsts\PluginUpdateChecker\v5p6;
use YahnisElsts\PluginUpdateChecker\v5\PucFactory as MajorFactory;
use YahnisElsts\PluginUpdateChecker\v5p3\PucFactory as MinorFactory;
use YahnisElsts\PluginUpdateChecker\v5p6\PucFactory as MinorFactory;
require __DIR__ . '/Puc/v5p3/Autoloader.php';
require __DIR__ . '/Puc/v5p6/Autoloader.php';
new Autoloader();
require __DIR__ . '/Puc/v5p3/PucFactory.php';
require __DIR__ . '/Puc/v5p6/PucFactory.php';
require __DIR__ . '/Puc/v5/PucFactory.php';
//Register classes defined in this version with the factory.
@ -26,9 +26,9 @@ foreach (
)
as $pucGeneralClass => $pucVersionedClass
) {
MajorFactory::addVersion($pucGeneralClass, $pucVersionedClass, '5.3');
MajorFactory::addVersion($pucGeneralClass, $pucVersionedClass, '5.6');
//Also add it to the minor-version factory in case the major-version factory
//was already defined by another, older version of the update checker.
MinorFactory::addVersion($pucGeneralClass, $pucVersionedClass, '5.3');
MinorFactory::addVersion($pucGeneralClass, $pucVersionedClass, '5.6');
}

View File

@ -1,10 +1,10 @@
<?php
/**
* Plugin Update Checker Library 5.3
* Plugin Update Checker Library 5.6
* http://w-shadow.com/
*
* Copyright 2022 Janis Elsts
* Copyright 2025 Janis Elsts
* Released under the MIT license. See license.txt for details.
*/
require dirname(__FILE__) . '/load-v5p3.php';
require dirname(__FILE__) . '/load-v5p6.php';

View File

@ -648,7 +648,7 @@ class Parsedown
#
# Setext
protected function blockSetextHeader($Line, array $Block = null)
protected function blockSetextHeader($Line, $Block = null)
{
if ( ! isset($Block) or isset($Block['type']) or isset($Block['interrupted']))
{
@ -786,7 +786,7 @@ class Parsedown
#
# Table
protected function blockTable($Line, array $Block = null)
protected function blockTable($Line, $Block = null)
{
if ( ! isset($Block) or isset($Block['type']) or isset($Block['interrupted']))
{