diff --git a/includes/plugin-update-checker/Puc/v5/PucFactory.php b/includes/plugin-update-checker/Puc/v5/PucFactory.php
index 0bc62ce..920248d 100644
--- a/includes/plugin-update-checker/Puc/v5/PucFactory.php
+++ b/includes/plugin-update-checker/Puc/v5/PucFactory.php
@@ -4,7 +4,7 @@ namespace YahnisElsts\PluginUpdateChecker\v5;
if ( !class_exists(PucFactory::class, false) ):
- class PucFactory extends \YahnisElsts\PluginUpdateChecker\v5p4\PucFactory {
+ class PucFactory extends \YahnisElsts\PluginUpdateChecker\v5p7\PucFactory {
}
endif;
diff --git a/includes/plugin-update-checker/Puc/v5p4/Autoloader.php b/includes/plugin-update-checker/Puc/v5p7/Autoloader.php
similarity index 98%
rename from includes/plugin-update-checker/Puc/v5p4/Autoloader.php
rename to includes/plugin-update-checker/Puc/v5p7/Autoloader.php
index 5a98133..9f036c1 100644
--- a/includes/plugin-update-checker/Puc/v5p4/Autoloader.php
+++ b/includes/plugin-update-checker/Puc/v5p7/Autoloader.php
@@ -1,6 +1,6 @@
Configuration';
echo '
';
$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 'Status ';
echo '';
$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(
+ ' ',
+ 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 '
';
}
@@ -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 = '' . htmlentities(print_r($value, true)) . ' ';
+ $value = '' . esc_html(print_r($value, true)) . ' ';
} else if ($value === null) {
$value = 'null';
}
diff --git a/includes/plugin-update-checker/Puc/v5p4/DebugBar/PluginExtension.php b/includes/plugin-update-checker/Puc/v5p7/DebugBar/PluginExtension.php
similarity index 90%
rename from includes/plugin-update-checker/Puc/v5p4/DebugBar/PluginExtension.php
rename to includes/plugin-update-checker/Puc/v5p7/DebugBar/PluginExtension.php
index b30f3ee..17770d0 100644
--- a/includes/plugin-update-checker/Puc/v5p4/DebugBar/PluginExtension.php
+++ b/includes/plugin-update-checker/Puc/v5p7/DebugBar/PluginExtension.php
@@ -1,8 +1,8 @@
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(
+ ' ',
+ esc_attr($buttonId),
+ esc_attr('Request Info')
);
}
return $requestInfoButton;
diff --git a/includes/plugin-update-checker/Puc/v5p4/DebugBar/ThemePanel.php b/includes/plugin-update-checker/Puc/v5p7/DebugBar/ThemePanel.php
similarity index 64%
rename from includes/plugin-update-checker/Puc/v5p4/DebugBar/ThemePanel.php
rename to includes/plugin-update-checker/Puc/v5p7/DebugBar/ThemePanel.php
index 7b9d99a..dd43d25 100644
--- a/includes/plugin-update-checker/Puc/v5p4/DebugBar/ThemePanel.php
+++ b/includes/plugin-update-checker/Puc/v5p7/DebugBar/ThemePanel.php
@@ -1,8 +1,8 @@
row('Theme directory', htmlentities($this->updateChecker->directoryName));
+ $this->row('Theme directory', esc_html($this->updateChecker->directoryName));
parent::displayConfigHeader();
}
diff --git a/includes/plugin-update-checker/Puc/v5p4/InstalledPackage.php b/includes/plugin-update-checker/Puc/v5p7/InstalledPackage.php
similarity index 98%
rename from includes/plugin-update-checker/Puc/v5p4/InstalledPackage.php
rename to includes/plugin-update-checker/Puc/v5p7/InstalledPackage.php
index 341e7a3..894e316 100644
--- a/includes/plugin-update-checker/Puc/v5p4/InstalledPackage.php
+++ b/includes/plugin-update-checker/Puc/v5p7/InstalledPackage.php
@@ -1,5 +1,5 @@
$status,
'puc_slug' => $this->updateChecker->slug,
diff --git a/includes/plugin-update-checker/Puc/v5p4/Plugin/Update.php b/includes/plugin-update-checker/Puc/v5p7/Plugin/Update.php
similarity index 91%
rename from includes/plugin-update-checker/Puc/v5p4/Plugin/Update.php
rename to includes/plugin-update-checker/Puc/v5p7/Plugin/Update.php
index 0fb3137..280bf68 100644
--- a/includes/plugin-update-checker/Puc/v5p4/Plugin/Update.php
+++ b/includes/plugin-update-checker/Puc/v5p7/Plugin/Update.php
@@ -1,7 +1,7 @@
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;
@@ -102,7 +104,7 @@ if ( !class_exists(Update::class, false) ):
$update->icons = $icons;
//It appears that the 'default' icon isn't used anywhere in WordPress 4.9,
- //but lets set it just in case a future release needs it.
+ //but let's set it just in case a future release needs it.
if ( !isset($update->icons['default']) ) {
$update->icons['default'] = current($update->icons);
}
diff --git a/includes/plugin-update-checker/Puc/v5p4/Plugin/UpdateChecker.php b/includes/plugin-update-checker/Puc/v5p7/Plugin/UpdateChecker.php
similarity index 97%
rename from includes/plugin-update-checker/Puc/v5p4/Plugin/UpdateChecker.php
rename to includes/plugin-update-checker/Puc/v5p7/Plugin/UpdateChecker.php
index 6d1aae8..20cf839 100644
--- a/includes/plugin-update-checker/Puc/v5p4/Plugin/UpdateChecker.php
+++ b/includes/plugin-update-checker/Puc/v5p7/Plugin/UpdateChecker.php
@@ -1,10 +1,10 @@
lazyLoad();
$this->update = $update;
return $this;
diff --git a/includes/plugin-update-checker/Puc/v5p4/Theme/Package.php b/includes/plugin-update-checker/Puc/v5p7/Theme/Package.php
similarity index 93%
rename from includes/plugin-update-checker/Puc/v5p4/Theme/Package.php
rename to includes/plugin-update-checker/Puc/v5p7/Theme/Package.php
index 0b20702..90528bb 100644
--- a/includes/plugin-update-checker/Puc/v5p4/Theme/Package.php
+++ b/includes/plugin-update-checker/Puc/v5p7/Theme/Package.php
@@ -1,7 +1,7 @@
maybeInitDebugBar();
@@ -192,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'));
@@ -266,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.
*
@@ -384,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;
}
@@ -557,7 +632,15 @@ if ( !class_exists(UpdateChecker::class, false) ):
if ( !empty($update) ) {
//Let plugins filter the update info before it's passed on to WordPress.
$update = apply_filters($this->getUniqueName('pre_inject_update'), $update);
- $updates = $this->addUpdateToList($updates, $update->toWpFormat());
+ //Convert the update into the format used by WordPress core.
+ $wpUpdate = $update->toWpFormat();
+ //Disable the "autoupdate" flag unless explicitly allowed. This is a safety precaution;
+ //untrusted or compromised update sources could otherwise set this flag to true even
+ //if the plugin/theme developer didn't intend to allow automatic update installation.
+ if ( isset($wpUpdate->autoupdate) && !$this->isAutoupdateFieldAllowed($update) ) {
+ $wpUpdate->autoupdate = false;
+ }
+ $updates = $this->addUpdateToList($updates, $wpUpdate);
} else {
//Clean up any stale update info.
$updates = $this->removeUpdateFromList($updates);
@@ -655,6 +738,38 @@ if ( !class_exists(UpdateChecker::class, false) ):
return true;
}
+ /**
+ * @var bool
+ */
+ protected $autoupdateFieldAllowed = false;
+
+ /**
+ * Allow the "autoupdate" field in incoming plugin updates to be set to `true`.
+ *
+ * By default, the update checker will parse the field (so it will be available in the update
+ * object), but will set it to `false` before passing the update to WordPress.
+ *
+ * @return $this
+ */
+ public function allowAutoupdateField() {
+ $this->autoupdateFieldAllowed = true;
+ return $this;
+ }
+
+ /**
+ * Is the "autoupdate" field for injected updates allowed to be set to true?
+ *
+ * @param object $incomingUpdate
+ * @return bool
+ */
+ protected function isAutoupdateFieldAllowed($incomingUpdate) {
+ return apply_filters(
+ $this->getUniqueName('autoupdate_field_allowed'),
+ $this->autoupdateFieldAllowed,
+ $incomingUpdate
+ );
+ }
+
/* -------------------------------------------------------------------
* JSON-based update API
* -------------------------------------------------------------------
@@ -698,7 +813,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;
@@ -924,25 +1039,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 %s , 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 %s , 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…',
'' . basename($source) . ' ',
diff --git a/includes/plugin-update-checker/Puc/v5p4/UpgraderStatus.php b/includes/plugin-update-checker/Puc/v5p7/UpgraderStatus.php
similarity index 99%
rename from includes/plugin-update-checker/Puc/v5p4/UpgraderStatus.php
rename to includes/plugin-update-checker/Puc/v5p7/UpgraderStatus.php
index e8340fd..9cc364c 100644
--- a/includes/plugin-update-checker/Puc/v5p4/UpgraderStatus.php
+++ b/includes/plugin-update-checker/Puc/v5p7/UpgraderStatus.php
@@ -1,5 +1,5 @@
wp_doing_cron() ? 10 : 3];
+ if ( $this->isAuthenticationEnabled() && !empty($this->authorizationHeader) ) {
+ $options['headers'] = ['Authorization' => $this->authorizationHeader];
+ }
+
+ if ( !empty($this->httpFilterName) ) {
+ $options = apply_filters($this->httpFilterName, $options);
+ }
+
+ return $options;
+ }
+
/**
* Set authentication credentials.
*
@@ -332,7 +358,130 @@ if ( !class_exists(Api::class, false) ):
}
public function isAuthenticationEnabled() {
- return !empty($this->credentials);
+ return !empty($this->credentials) || !empty($this->authorizationHeader);
+ }
+
+ /**
+ * Get the value of the "Authorization" header for API requests, if any.
+ *
+ * @return string
+ */
+ protected function getAuthorizationHeader() {
+ return $this->authorizationHeader ?: '';
+ }
+
+ /**
+ * Enable basic access authentication with the specified username and password.
+ *
+ * @param string $username
+ * @param string $password
+ * @param string|null $downloadUrlPrefix Optionally, add the same Authorization header to update
+ * downloads where the download URL starts with this prefix.
+ */
+ protected function enableBasicAuth($username, $password, $downloadUrlPrefix = null) {
+ $this->authorizationHeader = 'Basic ' . base64_encode($username . ':' . $password);
+
+ $this->downloadUrlPrefixRequiringAuth = $downloadUrlPrefix;
+ if ( !empty($this->downloadUrlPrefixRequiringAuth) ) {
+ $this->enableDownloadRequestFilter();
+ }
+ }
+
+ /**
+ * @var bool Whether the hook that registers download request filter(s) has already been added.
+ */
+ private $preDownloadHookAdded = false;
+
+ /**
+ * Enable the hooks that let you filter HTTP requests for update downloads.
+ */
+ protected function enableDownloadRequestFilter() {
+ if ( $this->preDownloadHookAdded ) {
+ return;
+ }
+ $this->preDownloadHookAdded = true;
+
+ //Optimization: Instead of filtering all HTTP requests, let's do it only when
+ //WordPress is about to download an update. So this is a two-step process;
+ //the actual request arg filter is added in the following hook.
+ add_filter('upgrader_pre_download', [$this, 'addDownloadRequestFilters']); //WP 3.7+
+ }
+
+ /**
+ * @var bool
+ */
+ private $downloadFiltersAdded = false;
+
+ /**
+ * @internal
+ * @param bool $result Pass-through value for the "upgrader_pre_download" filter. Ignored by this callback.
+ * @return bool
+ */
+ public function addDownloadRequestFilters($result) {
+ if ( !$this->downloadFiltersAdded ) {
+ $this->downloadFiltersAdded = true;
+
+ //phpcs:ignore WordPressVIPMinimum.Hooks.RestrictedHooks.http_request_args -- The callback doesn't change the timeout.
+ add_filter('http_request_args', [$this, 'filterUpdateDownloadRequestArgs'], 10, 2);
+
+ $authorizationHeader = $this->getAuthorizationHeader();
+ if ( $this->isAuthenticationEnabled() && !empty($authorizationHeader) ) {
+ add_action('requests-requests.before_redirect', [$this, 'removeAuthHeaderFromRedirects'], 10, 2);
+ }
+ }
+ return $result;
+ }
+
+ /**
+ * Filter request arguments/options for update downloads.
+ *
+ * Note that this callback can potentially be called for *any* update download. You still
+ * need to verify that the URL is one of yours.
+ *
+ * @internal
+ * @param array $requestArgs
+ * @param string $url
+ * @return array
+ */
+ public function filterUpdateDownloadRequestArgs($requestArgs, $url = '') {
+ //Add an authorization header to our downloads if needed.
+ $authHeader = $this->getAuthorizationHeader();
+ if (
+ $this->isAuthenticationEnabled()
+ && !empty($authHeader)
+ && !empty($this->downloadUrlPrefixRequiringAuth)
+ && ((strpos($url, $this->downloadUrlPrefixRequiringAuth)) === 0)
+ ) {
+ $requestArgs['headers']['Authorization'] = $authHeader;
+ }
+ return $requestArgs;
+ }
+
+ /**
+ * At least in older WP versions, when following a redirect, the Requests library will
+ * automatically forward the Authorization header to other hosts. We don't want that
+ * because it breaks AWS downloads and can leak authorization information.
+ *
+ * @param string $location
+ * @param array $headers
+ * @internal
+ */
+ public function removeAuthHeaderFromRedirects(&$location, &$headers) {
+ if (
+ //If there's no download URL prefix configured, we would not have added an auth header,
+ //and there's also no way to check if this URL needs auth or not.
+ empty($this->downloadUrlPrefixRequiringAuth)
+ //If this request goes to our download URL, we can leave the header.
+ || ((strpos($location, $this->downloadUrlPrefixRequiringAuth)) === 0)
+ ) {
+ return;
+ }
+
+ //Remove the header.
+ $authorizationHeader = $this->getAuthorizationHeader();
+ if ( isset($headers['Authorization']) && ($headers['Authorization'] === $authorizationHeader) ) {
+ unset($headers['Authorization']);
+ }
}
/**
diff --git a/includes/plugin-update-checker/Puc/v5p4/Vcs/BaseChecker.php b/includes/plugin-update-checker/Puc/v5p7/Vcs/BaseChecker.php
similarity index 90%
rename from includes/plugin-update-checker/Puc/v5p4/Vcs/BaseChecker.php
rename to includes/plugin-update-checker/Puc/v5p7/Vcs/BaseChecker.php
index 78e0dde..c416370 100644
--- a/includes/plugin-update-checker/Puc/v5p4/Vcs/BaseChecker.php
+++ b/includes/plugin-update-checker/Puc/v5p7/Vcs/BaseChecker.php
@@ -1,5 +1,5 @@
getDownloadBaseUrl() . $ref . '.zip';
+ }
+
+ /**
+ * Get the base URL for ZIP downloads from our repo. Includes the trailing slash.
+ *
+ * @return string
+ */
+ protected function getDownloadBaseUrl() {
return sprintf(
- 'https://bitbucket.org/%s/%s/get/%s.zip',
+ 'https://bitbucket.org/%s/%s/get/',
$this->username,
- $this->repository,
- $ref
+ $this->repository
);
}
@@ -206,15 +208,7 @@ if ( !class_exists(BitBucketApi::class, false) ):
));
$baseUrl = $url;
- if ( $this->oauth ) {
- $url = $this->oauth->sign($url,'GET');
- }
-
- $options = array('timeout' => wp_doing_cron() ? 10 : 3);
- if ( !empty($this->httpFilterName) ) {
- $options = apply_filters($this->httpFilterName, $options);
- }
- $response = wp_remote_get($url, $options);
+ $response = wp_remote_get($url, $this->getApiRequestHttpOptions());
if ( is_wp_error($response) ) {
do_action('puc_api_error', $response, null, $url, $this->slug);
return $response;
@@ -248,25 +242,18 @@ if ( !class_exists(BitBucketApi::class, false) ):
public function setAuthentication($credentials) {
parent::setAuthentication($credentials);
- if ( !empty($credentials) && !empty($credentials['consumer_key']) ) {
- $this->oauth = new OAuthSignature(
- $credentials['consumer_key'],
- $credentials['consumer_secret']
+ if (
+ is_array($credentials)
+ && !empty($credentials['username'])
+ && !empty($credentials['api_token'])
+ ) {
+ $this->enableBasicAuth(
+ $credentials['username'],
+ $credentials['api_token'],
+ $this->getDownloadBaseUrl()
);
- } else {
- $this->oauth = null;
}
}
-
- public function signDownloadUrl($url) {
- //Add authentication data to download URLs. Since OAuth signatures incorporate
- //timestamps, we have to do this immediately before inserting the update. Otherwise,
- //authentication could fail due to a stale timestamp.
- if ( $this->oauth ) {
- $url = $this->oauth->sign($url);
- }
- return $url;
- }
}
endif;
diff --git a/includes/plugin-update-checker/Puc/v5p4/Vcs/GitHubApi.php b/includes/plugin-update-checker/Puc/v5p7/Vcs/GitHubApi.php
similarity index 78%
rename from includes/plugin-update-checker/Puc/v5p4/Vcs/GitHubApi.php
rename to includes/plugin-update-checker/Puc/v5p7/Vcs/GitHubApi.php
index 610d932..782bfc2 100644
--- a/includes/plugin-update-checker/Puc/v5p4/Vcs/GitHubApi.php
+++ b/includes/plugin-update-checker/Puc/v5p7/Vcs/GitHubApi.php
@@ -1,6 +1,6 @@
[^/]+?)/(?P[^/#?&]+?)/?$@', $path, $matches) ) {
@@ -248,15 +243,7 @@ if ( !class_exists(GitHubApi::class, false) ):
$baseUrl = $url;
$url = $this->buildApiUrl($url, $queryParams);
- $options = array('timeout' => wp_doing_cron() ? 10 : 3);
- if ( $this->isAuthenticationEnabled() ) {
- $options['headers'] = array('Authorization' => $this->getAuthorizationHeader());
- }
-
- if ( !empty($this->httpFilterName) ) {
- $options = apply_filters($this->httpFilterName, $options);
- }
- $response = wp_remote_get($url, $options);
+ $response = wp_remote_get($url, $this->getApiRequestHttpOptions());
if ( is_wp_error($response) ) {
do_action('puc_api_error', $response, null, $url, $this->slug);
return $response;
@@ -348,11 +335,17 @@ if ( !class_exists(GitHubApi::class, false) ):
public function setAuthentication($credentials) {
parent::setAuthentication($credentials);
+ //This property is no longer used internally, but is kept for backwards compatibility.
$this->accessToken = is_string($credentials) ? $credentials : null;
- //Optimization: Instead of filtering all HTTP requests, let's do it only when
- //WordPress is about to download an update.
- add_filter('upgrader_pre_download', array($this, 'addHttpRequestFilter'), 10, 1); //WP 3.7+
+ if ( is_string($credentials) && !empty($credentials) ) {
+ $repoApiBaseUrl = $this->buildApiUrl('/repos/:user/:repo/', []);
+ $this->enableBasicAuth($this->userName, $credentials, $repoApiBaseUrl);
+ }
+
+ //Assets sometimes need an Accept header, so we add a request filter even if basic
+ //authentication isn't enabled.
+ $this->enableDownloadRequestFilter();
}
protected function getUpdateDetectionStrategies($configBranch) {
@@ -393,74 +386,15 @@ if ( !class_exists(GitHubApi::class, false) ):
return null;
}
- /**
- * @param bool $result
- * @return bool
- * @internal
- */
- public function addHttpRequestFilter($result) {
- if ( !$this->downloadFilterAdded && $this->isAuthenticationEnabled() ) {
- //phpcs:ignore WordPressVIPMinimum.Hooks.RestrictedHooks.http_request_args -- The callback doesn't change the timeout.
- add_filter('http_request_args', array($this, 'setUpdateDownloadHeaders'), 10, 2);
- add_action('requests-requests.before_redirect', array($this, 'removeAuthHeaderFromRedirects'), 10, 4);
- $this->downloadFilterAdded = true;
- }
- return $result;
- }
-
- /**
- * Set the HTTP headers that are necessary to download updates from private repositories.
- *
- * See GitHub docs:
- *
- * @link https://developer.github.com/v3/repos/releases/#get-a-single-release-asset
- * @link https://developer.github.com/v3/auth/#basic-authentication
- *
- * @internal
- * @param array $requestArgs
- * @param string $url
- * @return array
- */
- public function setUpdateDownloadHeaders($requestArgs, $url = '') {
+ public function filterUpdateDownloadRequestArgs($requestArgs, $url = '') {
+ //Release assets need an "Accept" header. See GitHub Docs:
+ //https://developer.github.com/v3/repos/releases/#get-a-single-release-asset
//Is WordPress trying to download one of our release assets?
if ( $this->releaseAssetsEnabled && (strpos($url, $this->getAssetApiBaseUrl()) !== false) ) {
$requestArgs['headers']['Accept'] = 'application/octet-stream';
}
- //Use Basic authentication, but only if the download is from our repository.
- $repoApiBaseUrl = $this->buildApiUrl('/repos/:user/:repo/', array());
- if ( $this->isAuthenticationEnabled() && (strpos($url, $repoApiBaseUrl)) === 0 ) {
- $requestArgs['headers']['Authorization'] = $this->getAuthorizationHeader();
- }
- return $requestArgs;
- }
- /**
- * When following a redirect, the Requests library will automatically forward
- * the authorization header to other hosts. We don't want that because it breaks
- * AWS downloads and can leak authorization information.
- *
- * @param string $location
- * @param array $headers
- * @internal
- */
- public function removeAuthHeaderFromRedirects(&$location, &$headers) {
- $repoApiBaseUrl = $this->buildApiUrl('/repos/:user/:repo/', array());
- if ( strpos($location, $repoApiBaseUrl) === 0 ) {
- return; //This request is going to GitHub, so it's fine.
- }
- //Remove the header.
- if ( isset($headers['Authorization']) ) {
- unset($headers['Authorization']);
- }
- }
-
- /**
- * Generate the value of the "Authorization" header.
- *
- * @return string
- */
- protected function getAuthorizationHeader() {
- return 'Basic ' . base64_encode($this->userName . ':' . $this->accessToken);
+ return parent::filterUpdateDownloadRequestArgs($requestArgs, $url);
}
}
diff --git a/includes/plugin-update-checker/Puc/v5p4/Vcs/GitLabApi.php b/includes/plugin-update-checker/Puc/v5p7/Vcs/GitLabApi.php
similarity index 97%
rename from includes/plugin-update-checker/Puc/v5p4/Vcs/GitLabApi.php
rename to includes/plugin-update-checker/Puc/v5p7/Vcs/GitLabApi.php
index 2cbd6eb..8b5dcc5 100644
--- a/includes/plugin-update-checker/Puc/v5p4/Vcs/GitLabApi.php
+++ b/includes/plugin-update-checker/Puc/v5p7/Vcs/GitLabApi.php
@@ -1,6 +1,6 @@
buildApiUrl($url, $queryParams);
- $options = array('timeout' => wp_doing_cron() ? 10 : 3);
- if ( !empty($this->httpFilterName) ) {
- $options = apply_filters($this->httpFilterName, $options);
- }
-
- $response = wp_remote_get($url, $options);
+ $response = wp_remote_get($url, $this->getApiRequestHttpOptions());
if ( is_wp_error($response) ) {
do_action('puc_api_error', $response, null, $url, $this->slug);
return $response;
diff --git a/includes/plugin-update-checker/Puc/v5p4/Vcs/PluginUpdateChecker.php b/includes/plugin-update-checker/Puc/v5p7/Vcs/PluginUpdateChecker.php
similarity index 98%
rename from includes/plugin-update-checker/Puc/v5p4/Vcs/PluginUpdateChecker.php
rename to includes/plugin-update-checker/Puc/v5p7/Vcs/PluginUpdateChecker.php
index f00097f..3c4f6b7 100644
--- a/includes/plugin-update-checker/Puc/v5p4/Vcs/PluginUpdateChecker.php
+++ b/includes/plugin-update-checker/Puc/v5p7/Vcs/PluginUpdateChecker.php
@@ -1,8 +1,8 @@
getVcsApi()->enableReleaseAssets();
```
+ By default, PUC will simply use the first available asset. You can pass a regular expression to `enableReleaseAssets()` to filter assets by name. For example:
+ ```php
+ $myUpdateChecker->getVcsApi()->enableReleaseAssets('/\.zip($|[?])/i');
+ ```
+
- **Tags**
To release version 1.2.3, create a new Git tag named `v1.2.3` or `1.2.3`. That's it.
@@ -188,14 +193,12 @@ The library will pull update details from the following parts of a release/tag/b
'unique-plugin-or-theme-slug'
);
- //Optional: If you're using a private repository, create an OAuth consumer
- //and set the authentication credentials like this:
- //Note: For now you need to check "This is a private consumer" when
- //creating the consumer to work around #134:
- // https://github.com/YahnisElsts/plugin-update-checker/issues/134
+ //Optional: If you're using a private repository, create an API token
+ //with the "read:repository:bitbucket" scope and set the authentication
+ //credentials like this:
$myUpdateChecker->setAuthentication(array(
- 'consumer_key' => '...',
- 'consumer_secret' => '...',
+ 'username' => 'example@example.com', //Your BitBucket email address.
+ 'api_token' => '...',
));
//Optional: Set the branch that contains the stable release.
@@ -252,8 +255,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\v5p4\Vcs\PluginUpdateChecker;
- use YahnisElsts\PluginUpdateChecker\v5p4\Vcs\GitLabApi;
+ use YahnisElsts\PluginUpdateChecker\v5p7\Vcs\PluginUpdateChecker;
+ use YahnisElsts\PluginUpdateChecker\v5p7\Vcs\GitLabApi;
$myUpdateChecker = new PluginUpdateChecker(
new GitLabApi('https://myserver.com/user-name/repo-name/'),
@@ -264,8 +267,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\v5p4\Vcs\PluginUpdateChecker;
- use YahnisElsts\PluginUpdateChecker\v5p4\Vcs\GitLabApi;
+ use YahnisElsts\PluginUpdateChecker\v5p7\Vcs\PluginUpdateChecker;
+ use YahnisElsts\PluginUpdateChecker\v5p7\Vcs\GitLabApi;
$myUpdateChecker = new PluginUpdateChecker(
new GitLabApi(
@@ -347,14 +350,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\v5p4\PucFactory` |
-| `Puc_v4p13_Plugin_UpdateChecker` | `YahnisElsts\PluginUpdateChecker\v5p4\Plugin\UpdateChecker` |
-| `Puc_v4p13_Theme_UpdateChecker` | `YahnisElsts\PluginUpdateChecker\v5p4\Theme\UpdateChecker` |
-| `Puc_v4p13_Vcs_PluginUpdateChecker` | `YahnisElsts\PluginUpdateChecker\v5p4\Vcs\PluginUpdateChecker` |
-| `Puc_v4p13_Vcs_ThemeUpdateChecker` | `YahnisElsts\PluginUpdateChecker\v5p4\Vcs\ThemeUpdateChecker` |
-| `Puc_v4p13_Vcs_GitHubApi` | `YahnisElsts\PluginUpdateChecker\v5p4\Vcs\GitHubApi` |
-| `Puc_v4p13_Vcs_GitLabApi` | `YahnisElsts\PluginUpdateChecker\v5p4\Vcs\GitLabApi` |
-| `Puc_v4p13_Vcs_BitBucketApi` | `YahnisElsts\PluginUpdateChecker\v5p4\Vcs\BitBucketApi` |
+| `Puc_v4p13_Factory` | `YahnisElsts\PluginUpdateChecker\v5p7\PucFactory` |
+| `Puc_v4p13_Plugin_UpdateChecker` | `YahnisElsts\PluginUpdateChecker\v5p7\Plugin\UpdateChecker` |
+| `Puc_v4p13_Theme_UpdateChecker` | `YahnisElsts\PluginUpdateChecker\v5p7\Theme\UpdateChecker` |
+| `Puc_v4p13_Vcs_PluginUpdateChecker` | `YahnisElsts\PluginUpdateChecker\v5p7\Vcs\PluginUpdateChecker` |
+| `Puc_v4p13_Vcs_ThemeUpdateChecker` | `YahnisElsts\PluginUpdateChecker\v5p7\Vcs\ThemeUpdateChecker` |
+| `Puc_v4p13_Vcs_GitHubApi` | `YahnisElsts\PluginUpdateChecker\v5p7\Vcs\GitHubApi` |
+| `Puc_v4p13_Vcs_GitLabApi` | `YahnisElsts\PluginUpdateChecker\v5p7\Vcs\GitLabApi` |
+| `Puc_v4p13_Vcs_BitBucketApi` | `YahnisElsts\PluginUpdateChecker\v5p7\Vcs\BitBucketApi` |
License Management
------------------
diff --git a/includes/plugin-update-checker/composer.json b/includes/plugin-update-checker/composer.json
index f7af7eb..85b1f35 100644
--- a/includes/plugin-update-checker/composer.json
+++ b/includes/plugin-update-checker/composer.json
@@ -18,6 +18,6 @@
"ext-json": "*"
},
"autoload": {
- "files": ["load-v5p4.php"]
+ "files": ["load-v5p7.php"]
}
}
diff --git a/includes/plugin-update-checker/languages/plugin-update-checker-el.mo b/includes/plugin-update-checker/languages/plugin-update-checker-el.mo
new file mode 100644
index 0000000..08306de
Binary files /dev/null and b/includes/plugin-update-checker/languages/plugin-update-checker-el.mo differ
diff --git a/includes/plugin-update-checker/languages/plugin-update-checker-el.po b/includes/plugin-update-checker/languages/plugin-update-checker-el.po
new file mode 100644
index 0000000..6b1ba21
--- /dev/null
+++ b/includes/plugin-update-checker/languages/plugin-update-checker-el.po
@@ -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 "Δεν υπάρχει διαθέσιμο αρχείο αλλαγών."
diff --git a/includes/plugin-update-checker/languages/plugin-update-checker-fa_IR.mo b/includes/plugin-update-checker/languages/plugin-update-checker-fa_IR.mo
index a68f100..4a9e948 100644
Binary files a/includes/plugin-update-checker/languages/plugin-update-checker-fa_IR.mo and b/includes/plugin-update-checker/languages/plugin-update-checker-fa_IR.mo differ
diff --git a/includes/plugin-update-checker/languages/plugin-update-checker-fa_IR.po b/includes/plugin-update-checker/languages/plugin-update-checker-fa_IR.po
index 20b6938..4c6ef4d 100644
--- a/includes/plugin-update-checker/languages/plugin-update-checker-fa_IR.po
+++ b/includes/plugin-update-checker/languages/plugin-update-checker-fa_IR.po
@@ -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 \n"
-"Language-Team: Pro Style \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 \n"
+"Language-Team: Alex Javadi \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 didn’t 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 didn’t 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 "نسخه جدیدی برای افزونه ارائه شده است ."
diff --git a/includes/plugin-update-checker/languages/plugin-update-checker-sr_RS.mo b/includes/plugin-update-checker/languages/plugin-update-checker-sr_RS.mo
new file mode 100644
index 0000000..af67f9e
Binary files /dev/null and b/includes/plugin-update-checker/languages/plugin-update-checker-sr_RS.mo differ
diff --git a/includes/plugin-update-checker/languages/plugin-update-checker-sr_RS.po b/includes/plugin-update-checker/languages/plugin-update-checker-sr_RS.po
new file mode 100644
index 0000000..144a2d9
--- /dev/null
+++ b/includes/plugin-update-checker/languages/plugin-update-checker-sr_RS.po
@@ -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ć \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 "Белешке о изменама нису доступне."
diff --git a/includes/plugin-update-checker/languages/plugin-update-checker-zh_CN.mo b/includes/plugin-update-checker/languages/plugin-update-checker-zh_CN.mo
index 86d1144..4e8ce4a 100644
Binary files a/includes/plugin-update-checker/languages/plugin-update-checker-zh_CN.mo and b/includes/plugin-update-checker/languages/plugin-update-checker-zh_CN.mo differ
diff --git a/includes/plugin-update-checker/languages/plugin-update-checker-zh_CN.po b/includes/plugin-update-checker/languages/plugin-update-checker-zh_CN.po
index d4f7056..d78ba5e 100644
--- a/includes/plugin-update-checker/languages/plugin-update-checker-zh_CN.po
+++ b/includes/plugin-update-checker/languages/plugin-update-checker-zh_CN.po
@@ -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 \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 "没有可用的更新日志。"
diff --git a/includes/plugin-update-checker/languages/plugin-update-checker-zh_TW.mo b/includes/plugin-update-checker/languages/plugin-update-checker-zh_TW.mo
new file mode 100644
index 0000000..a7d2511
Binary files /dev/null and b/includes/plugin-update-checker/languages/plugin-update-checker-zh_TW.mo differ
diff --git a/includes/plugin-update-checker/languages/plugin-update-checker-zh_TW.po b/includes/plugin-update-checker/languages/plugin-update-checker-zh_TW.po
new file mode 100644
index 0000000..e3ab5ee
--- /dev/null
+++ b/includes/plugin-update-checker/languages/plugin-update-checker-zh_TW.po
@@ -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 \n"
+"POT-Creation-Date: 2025-09-19 14:05-0700\n"
+"PO-Revision-Date: \n"
+"Last-Translator: Alex Lion \n"
+"Language-Team: Alex Lion \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 "目前沒有可供檢閱的變更記錄。"
diff --git a/includes/plugin-update-checker/languages/plugin-update-checker.pot b/includes/plugin-update-checker/languages/plugin-update-checker.pot
index 5b6319c..abe04a4 100644
--- a/includes/plugin-update-checker/languages/plugin-update-checker.pot
+++ b/includes/plugin-update-checker/languages/plugin-update-checker.pot
@@ -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/v5p4/Plugin/Ui.php:128
+#: Puc/v5p7/Plugin/Ui.php:130
msgid "Check for updates"
msgstr ""
-#: Puc/v5p4/Plugin/Ui.php:214
+#: Puc/v5p7/Plugin/Ui.php:217
#, php-format
msgctxt "the plugin title"
msgid "The %s plugin is up to date."
msgstr ""
-#: Puc/v5p4/Plugin/Ui.php:216
+#: Puc/v5p7/Plugin/Ui.php:219
#, php-format
msgctxt "the plugin title"
msgid "A new version of the %s plugin is available."
msgstr ""
-#: Puc/v5p4/Plugin/Ui.php:218
+#: Puc/v5p7/Plugin/Ui.php:221
#, php-format
msgctxt "the plugin title"
msgid "Could not determine if updates are available for %s."
msgstr ""
-#: Puc/v5p4/Plugin/Ui.php:224
+#: Puc/v5p7/Plugin/Ui.php:227
#, php-format
msgid "Unknown update checker status \"%s\""
msgstr ""
-#: Puc/v5p4/Vcs/PluginUpdateChecker.php:100
+#: Puc/v5p7/Vcs/PluginUpdateChecker.php:113
msgid "There is no changelog available."
msgstr ""
diff --git a/includes/plugin-update-checker/load-v5p4.php b/includes/plugin-update-checker/load-v5p7.php
similarity index 80%
rename from includes/plugin-update-checker/load-v5p4.php
rename to includes/plugin-update-checker/load-v5p7.php
index 2cd9580..be2b1b6 100644
--- a/includes/plugin-update-checker/load-v5p4.php
+++ b/includes/plugin-update-checker/load-v5p7.php
@@ -1,14 +1,14 @@
$pucVersionedClass
) {
- MajorFactory::addVersion($pucGeneralClass, $pucVersionedClass, '5.4');
+ MajorFactory::addVersion($pucGeneralClass, $pucVersionedClass, '5.7');
//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.4');
+ MinorFactory::addVersion($pucGeneralClass, $pucVersionedClass, '5.7');
}
diff --git a/includes/plugin-update-checker/plugin-update-checker.php b/includes/plugin-update-checker/plugin-update-checker.php
index da4cb1e..e5b3753 100644
--- a/includes/plugin-update-checker/plugin-update-checker.php
+++ b/includes/plugin-update-checker/plugin-update-checker.php
@@ -1,10 +1,10 @@
DefinitionData = array();
-
- # standardize line breaks
- $text = str_replace(array("\r\n", "\r"), "\n", $text);
-
- # remove surrounding line breaks
- $text = trim($text, "\n");
-
- # split text into lines
- $lines = explode("\n", $text);
-
- # iterate through lines to identify blocks
- $markup = $this->lines($lines);
-
- # trim line breaks
- $markup = trim($markup, "\n");
-
- return $markup;
- }
-
- #
- # Setters
- #
-
- function setBreaksEnabled($breaksEnabled)
- {
- $this->breaksEnabled = $breaksEnabled;
-
- return $this;
- }
-
- protected $breaksEnabled;
-
- function setMarkupEscaped($markupEscaped)
- {
- $this->markupEscaped = $markupEscaped;
-
- return $this;
- }
-
- protected $markupEscaped;
-
- function setUrlsLinked($urlsLinked)
- {
- $this->urlsLinked = $urlsLinked;
-
- return $this;
- }
-
- protected $urlsLinked = true;
-
- #
- # Lines
- #
-
- protected $BlockTypes = array(
- '#' => array('Header'),
- '*' => array('Rule', 'List'),
- '+' => array('List'),
- '-' => array('SetextHeader', 'Table', 'Rule', 'List'),
- '0' => array('List'),
- '1' => array('List'),
- '2' => array('List'),
- '3' => array('List'),
- '4' => array('List'),
- '5' => array('List'),
- '6' => array('List'),
- '7' => array('List'),
- '8' => array('List'),
- '9' => array('List'),
- ':' => array('Table'),
- '<' => array('Comment', 'Markup'),
- '=' => array('SetextHeader'),
- '>' => array('Quote'),
- '[' => array('Reference'),
- '_' => array('Rule'),
- '`' => array('FencedCode'),
- '|' => array('Table'),
- '~' => array('FencedCode'),
- );
-
- # ~
-
- protected $unmarkedBlockTypes = array(
- 'Code',
- );
-
- #
- # Blocks
- #
-
- protected function lines(array $lines)
- {
- $CurrentBlock = null;
-
- foreach ($lines as $line)
- {
- if (chop($line) === '')
- {
- if (isset($CurrentBlock))
- {
- $CurrentBlock['interrupted'] = true;
- }
-
- continue;
- }
-
- if (strpos($line, "\t") !== false)
- {
- $parts = explode("\t", $line);
-
- $line = $parts[0];
-
- unset($parts[0]);
-
- foreach ($parts as $part)
- {
- $shortage = 4 - mb_strlen($line, 'utf-8') % 4;
-
- $line .= str_repeat(' ', $shortage);
- $line .= $part;
- }
- }
-
- $indent = 0;
-
- while (isset($line[$indent]) and $line[$indent] === ' ')
- {
- $indent ++;
- }
-
- $text = $indent > 0 ? substr($line, $indent) : $line;
-
- # ~
-
- $Line = array('body' => $line, 'indent' => $indent, 'text' => $text);
-
- # ~
-
- if (isset($CurrentBlock['continuable']))
- {
- $Block = $this->{'block'.$CurrentBlock['type'].'Continue'}($Line, $CurrentBlock);
-
- if (isset($Block))
- {
- $CurrentBlock = $Block;
-
- continue;
- }
- else
- {
- if ($this->isBlockCompletable($CurrentBlock['type']))
- {
- $CurrentBlock = $this->{'block'.$CurrentBlock['type'].'Complete'}($CurrentBlock);
- }
- }
- }
-
- # ~
-
- $marker = $text[0];
-
- # ~
-
- $blockTypes = $this->unmarkedBlockTypes;
-
- if (isset($this->BlockTypes[$marker]))
- {
- foreach ($this->BlockTypes[$marker] as $blockType)
- {
- $blockTypes []= $blockType;
- }
- }
-
- #
- # ~
-
- foreach ($blockTypes as $blockType)
- {
- $Block = $this->{'block'.$blockType}($Line, $CurrentBlock);
-
- if (isset($Block))
- {
- $Block['type'] = $blockType;
-
- if ( ! isset($Block['identified']))
- {
- $Blocks []= $CurrentBlock;
-
- $Block['identified'] = true;
- }
-
- if ($this->isBlockContinuable($blockType))
- {
- $Block['continuable'] = true;
- }
-
- $CurrentBlock = $Block;
-
- continue 2;
- }
- }
-
- # ~
-
- if (isset($CurrentBlock) and ! isset($CurrentBlock['type']) and ! isset($CurrentBlock['interrupted']))
- {
- $CurrentBlock['element']['text'] .= "\n".$text;
- }
- else
- {
- $Blocks []= $CurrentBlock;
-
- $CurrentBlock = $this->paragraph($Line);
-
- $CurrentBlock['identified'] = true;
- }
- }
-
- # ~
-
- if (isset($CurrentBlock['continuable']) and $this->isBlockCompletable($CurrentBlock['type']))
- {
- $CurrentBlock = $this->{'block'.$CurrentBlock['type'].'Complete'}($CurrentBlock);
- }
-
- # ~
-
- $Blocks []= $CurrentBlock;
-
- unset($Blocks[0]);
-
- # ~
-
- $markup = '';
-
- foreach ($Blocks as $Block)
- {
- if (isset($Block['hidden']))
- {
- continue;
- }
-
- $markup .= "\n";
- $markup .= isset($Block['markup']) ? $Block['markup'] : $this->element($Block['element']);
- }
-
- $markup .= "\n";
-
- # ~
-
- return $markup;
- }
-
- protected function isBlockContinuable($Type)
- {
- return method_exists($this, 'block'.$Type.'Continue');
- }
-
- protected function isBlockCompletable($Type)
- {
- return method_exists($this, 'block'.$Type.'Complete');
- }
-
- #
- # Code
-
- protected function blockCode($Line, $Block = null)
- {
- if (isset($Block) and ! isset($Block['type']) and ! isset($Block['interrupted']))
- {
- return;
- }
-
- if ($Line['indent'] >= 4)
- {
- $text = substr($Line['body'], 4);
-
- $Block = array(
- 'element' => array(
- 'name' => 'pre',
- 'handler' => 'element',
- 'text' => array(
- 'name' => 'code',
- 'text' => $text,
- ),
- ),
- );
-
- return $Block;
- }
- }
-
- protected function blockCodeContinue($Line, $Block)
- {
- if ($Line['indent'] >= 4)
- {
- if (isset($Block['interrupted']))
- {
- $Block['element']['text']['text'] .= "\n";
-
- unset($Block['interrupted']);
- }
-
- $Block['element']['text']['text'] .= "\n";
-
- $text = substr($Line['body'], 4);
-
- $Block['element']['text']['text'] .= $text;
-
- return $Block;
- }
- }
-
- protected function blockCodeComplete($Block)
- {
- $text = $Block['element']['text']['text'];
-
- $text = htmlspecialchars($text, ENT_NOQUOTES, 'UTF-8');
-
- $Block['element']['text']['text'] = $text;
-
- return $Block;
- }
-
- #
- # Comment
-
- protected function blockComment($Line)
- {
- if ($this->markupEscaped)
- {
- return;
- }
-
- if (isset($Line['text'][3]) and $Line['text'][3] === '-' and $Line['text'][2] === '-' and $Line['text'][1] === '!')
- {
- $Block = array(
- 'markup' => $Line['body'],
- );
-
- if (preg_match('/-->$/', $Line['text']))
- {
- $Block['closed'] = true;
- }
-
- return $Block;
- }
- }
-
- protected function blockCommentContinue($Line, array $Block)
- {
- if (isset($Block['closed']))
- {
- return;
- }
-
- $Block['markup'] .= "\n" . $Line['body'];
-
- if (preg_match('/-->$/', $Line['text']))
- {
- $Block['closed'] = true;
- }
-
- return $Block;
- }
-
- #
- # Fenced Code
-
- protected function blockFencedCode($Line)
- {
- if (preg_match('/^['.$Line['text'][0].']{3,}[ ]*([\w-]+)?[ ]*$/', $Line['text'], $matches))
- {
- $Element = array(
- 'name' => 'code',
- 'text' => '',
- );
-
- if (isset($matches[1]))
- {
- $class = 'language-'.$matches[1];
-
- $Element['attributes'] = array(
- 'class' => $class,
- );
- }
-
- $Block = array(
- 'char' => $Line['text'][0],
- 'element' => array(
- 'name' => 'pre',
- 'handler' => 'element',
- 'text' => $Element,
- ),
- );
-
- return $Block;
- }
- }
-
- protected function blockFencedCodeContinue($Line, $Block)
- {
- if (isset($Block['complete']))
- {
- return;
- }
-
- if (isset($Block['interrupted']))
- {
- $Block['element']['text']['text'] .= "\n";
-
- unset($Block['interrupted']);
- }
-
- if (preg_match('/^'.$Block['char'].'{3,}[ ]*$/', $Line['text']))
- {
- $Block['element']['text']['text'] = substr($Block['element']['text']['text'], 1);
-
- $Block['complete'] = true;
-
- return $Block;
- }
-
- $Block['element']['text']['text'] .= "\n".$Line['body'];;
-
- return $Block;
- }
-
- protected function blockFencedCodeComplete($Block)
- {
- $text = $Block['element']['text']['text'];
-
- $text = htmlspecialchars($text, ENT_NOQUOTES, 'UTF-8');
-
- $Block['element']['text']['text'] = $text;
-
- return $Block;
- }
-
- #
- # Header
-
- protected function blockHeader($Line)
- {
- if (isset($Line['text'][1]))
- {
- $level = 1;
-
- while (isset($Line['text'][$level]) and $Line['text'][$level] === '#')
- {
- $level ++;
- }
-
- if ($level > 6)
- {
- return;
- }
-
- $text = trim($Line['text'], '# ');
-
- $Block = array(
- 'element' => array(
- 'name' => 'h' . min(6, $level),
- 'text' => $text,
- 'handler' => 'line',
- ),
- );
-
- return $Block;
- }
- }
-
- #
- # List
-
- protected function blockList($Line)
- {
- list($name, $pattern) = $Line['text'][0] <= '-' ? array('ul', '[*+-]') : array('ol', '[0-9]+[.]');
-
- if (preg_match('/^('.$pattern.'[ ]+)(.*)/', $Line['text'], $matches))
- {
- $Block = array(
- 'indent' => $Line['indent'],
- 'pattern' => $pattern,
- 'element' => array(
- 'name' => $name,
- 'handler' => 'elements',
- ),
- );
-
- $Block['li'] = array(
- 'name' => 'li',
- 'handler' => 'li',
- 'text' => array(
- $matches[2],
- ),
- );
-
- $Block['element']['text'] []= & $Block['li'];
-
- return $Block;
- }
- }
-
- protected function blockListContinue($Line, array $Block)
- {
- if ($Block['indent'] === $Line['indent'] and preg_match('/^'.$Block['pattern'].'(?:[ ]+(.*)|$)/', $Line['text'], $matches))
- {
- if (isset($Block['interrupted']))
- {
- $Block['li']['text'] []= '';
-
- unset($Block['interrupted']);
- }
-
- unset($Block['li']);
-
- $text = isset($matches[1]) ? $matches[1] : '';
-
- $Block['li'] = array(
- 'name' => 'li',
- 'handler' => 'li',
- 'text' => array(
- $text,
- ),
- );
-
- $Block['element']['text'] []= & $Block['li'];
-
- return $Block;
- }
-
- if ($Line['text'][0] === '[' and $this->blockReference($Line))
- {
- return $Block;
- }
-
- if ( ! isset($Block['interrupted']))
- {
- $text = preg_replace('/^[ ]{0,4}/', '', $Line['body']);
-
- $Block['li']['text'] []= $text;
-
- return $Block;
- }
-
- if ($Line['indent'] > 0)
- {
- $Block['li']['text'] []= '';
-
- $text = preg_replace('/^[ ]{0,4}/', '', $Line['body']);
-
- $Block['li']['text'] []= $text;
-
- unset($Block['interrupted']);
-
- return $Block;
- }
- }
-
- #
- # Quote
-
- protected function blockQuote($Line)
- {
- if (preg_match('/^>[ ]?(.*)/', $Line['text'], $matches))
- {
- $Block = array(
- 'element' => array(
- 'name' => 'blockquote',
- 'handler' => 'lines',
- 'text' => (array) $matches[1],
- ),
- );
-
- return $Block;
- }
- }
-
- protected function blockQuoteContinue($Line, array $Block)
- {
- if ($Line['text'][0] === '>' and preg_match('/^>[ ]?(.*)/', $Line['text'], $matches))
- {
- if (isset($Block['interrupted']))
- {
- $Block['element']['text'] []= '';
-
- unset($Block['interrupted']);
- }
-
- $Block['element']['text'] []= $matches[1];
-
- return $Block;
- }
-
- if ( ! isset($Block['interrupted']))
- {
- $Block['element']['text'] []= $Line['text'];
-
- return $Block;
- }
- }
-
- #
- # Rule
-
- protected function blockRule($Line)
- {
- if (preg_match('/^(['.$Line['text'][0].'])([ ]*\1){2,}[ ]*$/', $Line['text']))
- {
- $Block = array(
- 'element' => array(
- 'name' => 'hr'
- ),
- );
-
- return $Block;
- }
- }
-
- #
- # Setext
-
- protected function blockSetextHeader($Line, array $Block = null)
- {
- if ( ! isset($Block) or isset($Block['type']) or isset($Block['interrupted']))
- {
- return;
- }
-
- if (chop($Line['text'], $Line['text'][0]) === '')
- {
- $Block['element']['name'] = $Line['text'][0] === '=' ? 'h1' : 'h2';
-
- return $Block;
- }
- }
-
- #
- # Markup
-
- protected function blockMarkup($Line)
- {
- if ($this->markupEscaped)
- {
- return;
- }
-
- if (preg_match('/^<(\w*)(?:[ ]*'.$this->regexHtmlAttribute.')*[ ]*(\/)?>/', $Line['text'], $matches))
- {
- $element = strtolower($matches[1]);
-
- if (in_array($element, $this->textLevelElements))
- {
- return;
- }
-
- $Block = array(
- 'name' => $matches[1],
- 'depth' => 0,
- 'markup' => $Line['text'],
- );
-
- $length = strlen($matches[0]);
-
- $remainder = substr($Line['text'], $length);
-
- if (trim($remainder) === '')
- {
- if (isset($matches[2]) or in_array($matches[1], $this->voidElements))
- {
- $Block['closed'] = true;
-
- $Block['void'] = true;
- }
- }
- else
- {
- if (isset($matches[2]) or in_array($matches[1], $this->voidElements))
- {
- return;
- }
-
- if (preg_match('/<\/'.$matches[1].'>[ ]*$/i', $remainder))
- {
- $Block['closed'] = true;
- }
- }
-
- return $Block;
- }
- }
-
- protected function blockMarkupContinue($Line, array $Block)
- {
- if (isset($Block['closed']))
- {
- return;
- }
-
- if (preg_match('/^<'.$Block['name'].'(?:[ ]*'.$this->regexHtmlAttribute.')*[ ]*>/i', $Line['text'])) # open
- {
- $Block['depth'] ++;
- }
-
- if (preg_match('/(.*?)<\/'.$Block['name'].'>[ ]*$/i', $Line['text'], $matches)) # close
- {
- if ($Block['depth'] > 0)
- {
- $Block['depth'] --;
- }
- else
- {
- $Block['closed'] = true;
- }
- }
-
- if (isset($Block['interrupted']))
- {
- $Block['markup'] .= "\n";
-
- unset($Block['interrupted']);
- }
-
- $Block['markup'] .= "\n".$Line['body'];
-
- return $Block;
- }
-
- #
- # Reference
-
- protected function blockReference($Line)
- {
- if (preg_match('/^\[(.+?)\]:[ ]*(\S+?)>?(?:[ ]+["\'(](.+)["\')])?[ ]*$/', $Line['text'], $matches))
- {
- $id = strtolower($matches[1]);
-
- $Data = array(
- 'url' => $matches[2],
- 'title' => null,
- );
-
- if (isset($matches[3]))
- {
- $Data['title'] = $matches[3];
- }
-
- $this->DefinitionData['Reference'][$id] = $Data;
-
- $Block = array(
- 'hidden' => true,
- );
-
- return $Block;
- }
- }
-
- #
- # Table
-
- protected function blockTable($Line, array $Block = null)
- {
- if ( ! isset($Block) or isset($Block['type']) or isset($Block['interrupted']))
- {
- return;
- }
-
- if (strpos($Block['element']['text'], '|') !== false and chop($Line['text'], ' -:|') === '')
- {
- $alignments = array();
-
- $divider = $Line['text'];
-
- $divider = trim($divider);
- $divider = trim($divider, '|');
-
- $dividerCells = explode('|', $divider);
-
- foreach ($dividerCells as $dividerCell)
- {
- $dividerCell = trim($dividerCell);
-
- if ($dividerCell === '')
- {
- continue;
- }
-
- $alignment = null;
-
- if ($dividerCell[0] === ':')
- {
- $alignment = 'left';
- }
-
- if (substr($dividerCell, - 1) === ':')
- {
- $alignment = $alignment === 'left' ? 'center' : 'right';
- }
-
- $alignments []= $alignment;
- }
-
- # ~
-
- $HeaderElements = array();
-
- $header = $Block['element']['text'];
-
- $header = trim($header);
- $header = trim($header, '|');
-
- $headerCells = explode('|', $header);
-
- foreach ($headerCells as $index => $headerCell)
- {
- $headerCell = trim($headerCell);
-
- $HeaderElement = array(
- 'name' => 'th',
- 'text' => $headerCell,
- 'handler' => 'line',
- );
-
- if (isset($alignments[$index]))
- {
- $alignment = $alignments[$index];
-
- $HeaderElement['attributes'] = array(
- 'style' => 'text-align: '.$alignment.';',
- );
- }
-
- $HeaderElements []= $HeaderElement;
- }
-
- # ~
-
- $Block = array(
- 'alignments' => $alignments,
- 'identified' => true,
- 'element' => array(
- 'name' => 'table',
- 'handler' => 'elements',
- ),
- );
-
- $Block['element']['text'] []= array(
- 'name' => 'thead',
- 'handler' => 'elements',
- );
-
- $Block['element']['text'] []= array(
- 'name' => 'tbody',
- 'handler' => 'elements',
- 'text' => array(),
- );
-
- $Block['element']['text'][0]['text'] []= array(
- 'name' => 'tr',
- 'handler' => 'elements',
- 'text' => $HeaderElements,
- );
-
- return $Block;
- }
- }
-
- protected function blockTableContinue($Line, array $Block)
- {
- if (isset($Block['interrupted']))
- {
- return;
- }
-
- if ($Line['text'][0] === '|' or strpos($Line['text'], '|'))
- {
- $Elements = array();
-
- $row = $Line['text'];
-
- $row = trim($row);
- $row = trim($row, '|');
-
- preg_match_all('/(?:(\\\\[|])|[^|`]|`[^`]+`|`)+/', $row, $matches);
-
- foreach ($matches[0] as $index => $cell)
- {
- $cell = trim($cell);
-
- $Element = array(
- 'name' => 'td',
- 'handler' => 'line',
- 'text' => $cell,
- );
-
- if (isset($Block['alignments'][$index]))
- {
- $Element['attributes'] = array(
- 'style' => 'text-align: '.$Block['alignments'][$index].';',
- );
- }
-
- $Elements []= $Element;
- }
-
- $Element = array(
- 'name' => 'tr',
- 'handler' => 'elements',
- 'text' => $Elements,
- );
-
- $Block['element']['text'][1]['text'] []= $Element;
-
- return $Block;
- }
- }
-
- #
- # ~
- #
-
- protected function paragraph($Line)
- {
- $Block = array(
- 'element' => array(
- 'name' => 'p',
- 'text' => $Line['text'],
- 'handler' => 'line',
- ),
- );
-
- return $Block;
- }
-
- #
- # Inline Elements
- #
-
- protected $InlineTypes = array(
- '"' => array('SpecialCharacter'),
- '!' => array('Image'),
- '&' => array('SpecialCharacter'),
- '*' => array('Emphasis'),
- ':' => array('Url'),
- '<' => array('UrlTag', 'EmailTag', 'Markup', 'SpecialCharacter'),
- '>' => array('SpecialCharacter'),
- '[' => array('Link'),
- '_' => array('Emphasis'),
- '`' => array('Code'),
- '~' => array('Strikethrough'),
- '\\' => array('EscapeSequence'),
- );
-
- # ~
-
- protected $inlineMarkerList = '!"*_&[:<>`~\\';
-
- #
- # ~
- #
-
- public function line($text)
- {
- $markup = '';
-
- # $excerpt is based on the first occurrence of a marker
-
- while ($excerpt = strpbrk($text, $this->inlineMarkerList))
- {
- $marker = $excerpt[0];
-
- $markerPosition = strpos($text, $marker);
-
- $Excerpt = array('text' => $excerpt, 'context' => $text);
-
- foreach ($this->InlineTypes[$marker] as $inlineType)
- {
- $Inline = $this->{'inline'.$inlineType}($Excerpt);
-
- if ( ! isset($Inline))
- {
- continue;
- }
-
- # makes sure that the inline belongs to "our" marker
-
- if (isset($Inline['position']) and $Inline['position'] > $markerPosition)
- {
- continue;
- }
-
- # sets a default inline position
-
- if ( ! isset($Inline['position']))
- {
- $Inline['position'] = $markerPosition;
- }
-
- # the text that comes before the inline
- $unmarkedText = substr($text, 0, $Inline['position']);
-
- # compile the unmarked text
- $markup .= $this->unmarkedText($unmarkedText);
-
- # compile the inline
- $markup .= isset($Inline['markup']) ? $Inline['markup'] : $this->element($Inline['element']);
-
- # remove the examined text
- $text = substr($text, $Inline['position'] + $Inline['extent']);
-
- continue 2;
- }
-
- # the marker does not belong to an inline
-
- $unmarkedText = substr($text, 0, $markerPosition + 1);
-
- $markup .= $this->unmarkedText($unmarkedText);
-
- $text = substr($text, $markerPosition + 1);
- }
-
- $markup .= $this->unmarkedText($text);
-
- return $markup;
- }
-
- #
- # ~
- #
-
- protected function inlineCode($Excerpt)
- {
- $marker = $Excerpt['text'][0];
-
- if (preg_match('/^('.$marker.'+)[ ]*(.+?)[ ]*(? strlen($matches[0]),
- 'element' => array(
- 'name' => 'code',
- 'text' => $text,
- ),
- );
- }
- }
-
- protected function inlineEmailTag($Excerpt)
- {
- if (strpos($Excerpt['text'], '>') !== false and preg_match('/^<((mailto:)?\S+?@\S+?)>/i', $Excerpt['text'], $matches))
- {
- $url = $matches[1];
-
- if ( ! isset($matches[2]))
- {
- $url = 'mailto:' . $url;
- }
-
- return array(
- 'extent' => strlen($matches[0]),
- 'element' => array(
- 'name' => 'a',
- 'text' => $matches[1],
- 'attributes' => array(
- 'href' => $url,
- ),
- ),
- );
- }
- }
-
- protected function inlineEmphasis($Excerpt)
- {
- if ( ! isset($Excerpt['text'][1]))
- {
- return;
- }
-
- $marker = $Excerpt['text'][0];
-
- if ($Excerpt['text'][1] === $marker and preg_match($this->StrongRegex[$marker], $Excerpt['text'], $matches))
- {
- $emphasis = 'strong';
- }
- elseif (preg_match($this->EmRegex[$marker], $Excerpt['text'], $matches))
- {
- $emphasis = 'em';
- }
- else
- {
- return;
- }
-
- return array(
- 'extent' => strlen($matches[0]),
- 'element' => array(
- 'name' => $emphasis,
- 'handler' => 'line',
- 'text' => $matches[1],
- ),
- );
- }
-
- protected function inlineEscapeSequence($Excerpt)
- {
- if (isset($Excerpt['text'][1]) and in_array($Excerpt['text'][1], $this->specialCharacters))
- {
- return array(
- 'markup' => $Excerpt['text'][1],
- 'extent' => 2,
- );
- }
- }
-
- protected function inlineImage($Excerpt)
- {
- if ( ! isset($Excerpt['text'][1]) or $Excerpt['text'][1] !== '[')
- {
- return;
- }
-
- $Excerpt['text']= substr($Excerpt['text'], 1);
-
- $Link = $this->inlineLink($Excerpt);
-
- if ($Link === null)
- {
- return;
- }
-
- $Inline = array(
- 'extent' => $Link['extent'] + 1,
- 'element' => array(
- 'name' => 'img',
- 'attributes' => array(
- 'src' => $Link['element']['attributes']['href'],
- 'alt' => $Link['element']['text'],
- ),
- ),
- );
-
- $Inline['element']['attributes'] += $Link['element']['attributes'];
-
- unset($Inline['element']['attributes']['href']);
-
- return $Inline;
- }
-
- protected function inlineLink($Excerpt)
- {
- $Element = array(
- 'name' => 'a',
- 'handler' => 'line',
- 'text' => null,
- 'attributes' => array(
- 'href' => null,
- 'title' => null,
- ),
- );
-
- $extent = 0;
-
- $remainder = $Excerpt['text'];
-
- if (preg_match('/\[((?:[^][]|(?R))*)\]/', $remainder, $matches))
- {
- $Element['text'] = $matches[1];
-
- $extent += strlen($matches[0]);
-
- $remainder = substr($remainder, $extent);
- }
- else
- {
- return;
- }
-
- if (preg_match('/^[(]((?:[^ ()]|[(][^ )]+[)])+)(?:[ ]+("[^"]*"|\'[^\']*\'))?[)]/', $remainder, $matches))
- {
- $Element['attributes']['href'] = $matches[1];
-
- if (isset($matches[2]))
- {
- $Element['attributes']['title'] = substr($matches[2], 1, - 1);
- }
-
- $extent += strlen($matches[0]);
- }
- else
- {
- if (preg_match('/^\s*\[(.*?)\]/', $remainder, $matches))
- {
- $definition = strlen($matches[1]) ? $matches[1] : $Element['text'];
- $definition = strtolower($definition);
-
- $extent += strlen($matches[0]);
- }
- else
- {
- $definition = strtolower($Element['text']);
- }
-
- if ( ! isset($this->DefinitionData['Reference'][$definition]))
- {
- return;
- }
-
- $Definition = $this->DefinitionData['Reference'][$definition];
-
- $Element['attributes']['href'] = $Definition['url'];
- $Element['attributes']['title'] = $Definition['title'];
- }
-
- $Element['attributes']['href'] = str_replace(array('&', '<'), array('&', '<'), $Element['attributes']['href']);
-
- return array(
- 'extent' => $extent,
- 'element' => $Element,
- );
- }
-
- protected function inlineMarkup($Excerpt)
- {
- if ($this->markupEscaped or strpos($Excerpt['text'], '>') === false)
- {
- return;
- }
-
- if ($Excerpt['text'][1] === '/' and preg_match('/^<\/\w*[ ]*>/s', $Excerpt['text'], $matches))
- {
- return array(
- 'markup' => $matches[0],
- 'extent' => strlen($matches[0]),
- );
- }
-
- if ($Excerpt['text'][1] === '!' and preg_match('/^/s', $Excerpt['text'], $matches))
- {
- return array(
- 'markup' => $matches[0],
- 'extent' => strlen($matches[0]),
- );
- }
-
- if ($Excerpt['text'][1] !== ' ' and preg_match('/^<\w*(?:[ ]*'.$this->regexHtmlAttribute.')*[ ]*\/?>/s', $Excerpt['text'], $matches))
- {
- return array(
- 'markup' => $matches[0],
- 'extent' => strlen($matches[0]),
- );
- }
- }
-
- protected function inlineSpecialCharacter($Excerpt)
- {
- if ($Excerpt['text'][0] === '&' and ! preg_match('/^?\w+;/', $Excerpt['text']))
- {
- return array(
- 'markup' => '&',
- 'extent' => 1,
- );
- }
-
- $SpecialCharacter = array('>' => 'gt', '<' => 'lt', '"' => 'quot');
-
- if (isset($SpecialCharacter[$Excerpt['text'][0]]))
- {
- return array(
- 'markup' => '&'.$SpecialCharacter[$Excerpt['text'][0]].';',
- 'extent' => 1,
- );
- }
- }
-
- protected function inlineStrikethrough($Excerpt)
- {
- if ( ! isset($Excerpt['text'][1]))
- {
- return;
- }
-
- if ($Excerpt['text'][1] === '~' and preg_match('/^~~(?=\S)(.+?)(?<=\S)~~/', $Excerpt['text'], $matches))
- {
- return array(
- 'extent' => strlen($matches[0]),
- 'element' => array(
- 'name' => 'del',
- 'text' => $matches[1],
- 'handler' => 'line',
- ),
- );
- }
- }
-
- protected function inlineUrl($Excerpt)
- {
- if ($this->urlsLinked !== true or ! isset($Excerpt['text'][2]) or $Excerpt['text'][2] !== '/')
- {
- return;
- }
-
- if (preg_match('/\bhttps?:[\/]{2}[^\s<]+\b\/*/ui', $Excerpt['context'], $matches, PREG_OFFSET_CAPTURE))
- {
- $Inline = array(
- 'extent' => strlen($matches[0][0]),
- 'position' => $matches[0][1],
- 'element' => array(
- 'name' => 'a',
- 'text' => $matches[0][0],
- 'attributes' => array(
- 'href' => $matches[0][0],
- ),
- ),
- );
-
- return $Inline;
- }
- }
-
- protected function inlineUrlTag($Excerpt)
- {
- if (strpos($Excerpt['text'], '>') !== false and preg_match('/^<(\w+:\/{2}[^ >]+)>/i', $Excerpt['text'], $matches))
- {
- $url = str_replace(array('&', '<'), array('&', '<'), $matches[1]);
-
- return array(
- 'extent' => strlen($matches[0]),
- 'element' => array(
- 'name' => 'a',
- 'text' => $url,
- 'attributes' => array(
- 'href' => $url,
- ),
- ),
- );
- }
- }
-
- # ~
-
- protected function unmarkedText($text)
- {
- if ($this->breaksEnabled)
- {
- $text = preg_replace('/[ ]*\n/', " \n", $text);
- }
- else
- {
- $text = preg_replace('/(?:[ ][ ]+|[ ]*\\\\)\n/', " \n", $text);
- $text = str_replace(" \n", "\n", $text);
- }
-
- return $text;
- }
-
- #
- # Handlers
- #
-
- protected function element(array $Element)
- {
- $markup = '<'.$Element['name'];
-
- if (isset($Element['attributes']))
- {
- foreach ($Element['attributes'] as $name => $value)
- {
- if ($value === null)
- {
- continue;
- }
-
- $markup .= ' '.$name.'="'.$value.'"';
- }
- }
-
- if (isset($Element['text']))
- {
- $markup .= '>';
-
- if (isset($Element['handler']))
- {
- $markup .= $this->{$Element['handler']}($Element['text']);
- }
- else
- {
- $markup .= $Element['text'];
- }
-
- $markup .= ''.$Element['name'].'>';
- }
- else
- {
- $markup .= ' />';
- }
-
- return $markup;
- }
-
- protected function elements(array $Elements)
- {
- $markup = '';
-
- foreach ($Elements as $Element)
- {
- $markup .= "\n" . $this->element($Element);
- }
-
- $markup .= "\n";
-
- return $markup;
- }
-
- # ~
-
- protected function li($lines)
- {
- $markup = $this->lines($lines);
-
- $trimmedMarkup = trim($markup);
-
- if ( ! in_array('', $lines) and substr($trimmedMarkup, 0, 3) === '')
- {
- $markup = $trimmedMarkup;
- $markup = substr($markup, 3);
-
- $position = strpos($markup, "
");
-
- $markup = substr_replace($markup, '', $position, 4);
- }
-
- return $markup;
- }
-
- #
- # Deprecated Methods
- #
-
- function parse($text)
- {
- $markup = $this->text($text);
-
- return $markup;
- }
-
- #
- # Static Methods
- #
-
- static function instance($name = 'default')
- {
- if (isset(self::$instances[$name]))
- {
- return self::$instances[$name];
- }
-
- $instance = new static();
-
- self::$instances[$name] = $instance;
-
- return $instance;
- }
-
- private static $instances = array();
-
- #
- # Fields
- #
-
- protected $DefinitionData;
-
- #
- # Read-Only
-
- protected $specialCharacters = array(
- '\\', '`', '*', '_', '{', '}', '[', ']', '(', ')', '>', '#', '+', '-', '.', '!', '|',
- );
-
- protected $StrongRegex = array(
- '*' => '/^[*]{2}((?:\\\\\*|[^*]|[*][^*]*[*])+?)[*]{2}(?![*])/s',
- '_' => '/^__((?:\\\\_|[^_]|_[^_]*_)+?)__(?!_)/us',
- );
-
- protected $EmRegex = array(
- '*' => '/^[*]((?:\\\\\*|[^*]|[*][*][^*]+?[*][*])+?)[*](?![*])/s',
- '_' => '/^_((?:\\\\_|[^_]|__[^_]*__)+?)_(?!_)\b/us',
- );
-
- protected $regexHtmlAttribute = '[a-zA-Z_:][\w:.-]*(?:\s*=\s*(?:[^"\'=<>`\s]+|"[^"]*"|\'[^\']*\'))?';
-
- protected $voidElements = array(
- 'area', 'base', 'br', 'col', 'command', 'embed', 'hr', 'img', 'input', 'link', 'meta', 'param', 'source',
- );
-
- protected $textLevelElements = array(
- 'a', 'br', 'bdo', 'abbr', 'blink', 'nextid', 'acronym', 'basefont',
- 'b', 'em', 'big', 'cite', 'small', 'spacer', 'listing',
- 'i', 'rp', 'del', 'code', 'strike', 'marquee',
- 'q', 'rt', 'ins', 'font', 'strong',
- 's', 'tt', 'sub', 'mark',
- 'u', 'xm', 'sup', 'nobr',
- 'var', 'ruby',
- 'wbr', 'span',
- 'time',
- );
-}
\ No newline at end of file
diff --git a/includes/plugin-update-checker/vendor/PucReadmeParser.php b/includes/plugin-update-checker/vendor/PucReadmeParser.php
deleted file mode 100644
index a794c49..0000000
--- a/includes/plugin-update-checker/vendor/PucReadmeParser.php
+++ /dev/null
@@ -1,352 +0,0 @@
-parse_readme_contents( $file_contents );
- }
-
- function parse_readme_contents( $file_contents ) {
- $file_contents = str_replace(array("\r\n", "\r"), "\n", $file_contents);
- $file_contents = trim($file_contents);
- if ( 0 === strpos( $file_contents, "\xEF\xBB\xBF" ) )
- $file_contents = substr( $file_contents, 3 );
-
- // Markdown transformations
- $file_contents = preg_replace( "|^###([^#]+)#*?\s*?\n|im", '=$1='."\n", $file_contents );
- $file_contents = preg_replace( "|^##([^#]+)#*?\s*?\n|im", '==$1=='."\n", $file_contents );
- $file_contents = preg_replace( "|^#([^#]+)#*?\s*?\n|im", '===$1==='."\n", $file_contents );
-
- // === Plugin Name ===
- // Must be the very first thing.
- if ( !preg_match('|^===(.*)===|', $file_contents, $_name) )
- return array(); // require a name
- $name = trim($_name[1], '=');
- $name = $this->sanitize_text( $name );
-
- $file_contents = $this->chop_string( $file_contents, $_name[0] );
-
-
- // Requires at least: 1.5
- if ( preg_match('|Requires at least:(.*)|i', $file_contents, $_requires_at_least) )
- $requires_at_least = $this->sanitize_text($_requires_at_least[1]);
- else
- $requires_at_least = NULL;
-
-
- // Tested up to: 2.1
- if ( preg_match('|Tested up to:(.*)|i', $file_contents, $_tested_up_to) )
- $tested_up_to = $this->sanitize_text( $_tested_up_to[1] );
- else
- $tested_up_to = NULL;
-
- // Requires PHP: 5.2.4
- if ( preg_match('|Requires PHP:(.*)|i', $file_contents, $_requires_php) ) {
- $requires_php = $this->sanitize_text( $_requires_php[1] );
- } else {
- $requires_php = null;
- }
-
- // Stable tag: 10.4-ride-the-fire-eagle-danger-day
- if ( preg_match('|Stable tag:(.*)|i', $file_contents, $_stable_tag) )
- $stable_tag = $this->sanitize_text( $_stable_tag[1] );
- else
- $stable_tag = NULL; // we assume trunk, but don't set it here to tell the difference between specified trunk and default trunk
-
-
- // Tags: some tag, another tag, we like tags
- if ( preg_match('|Tags:(.*)|i', $file_contents, $_tags) ) {
- $tags = preg_split('|,[\s]*?|', trim($_tags[1]));
- foreach ( array_keys($tags) as $t )
- $tags[$t] = $this->sanitize_text( $tags[$t] );
- } else {
- $tags = array();
- }
-
-
- // Contributors: markjaquith, mdawaffe, zefrank
- $contributors = array();
- if ( preg_match('|Contributors:(.*)|i', $file_contents, $_contributors) ) {
- $temp_contributors = preg_split('|,[\s]*|', trim($_contributors[1]));
- foreach ( array_keys($temp_contributors) as $c ) {
- $tmp_sanitized = $this->user_sanitize( $temp_contributors[$c] );
- if ( strlen(trim($tmp_sanitized)) > 0 )
- $contributors[$c] = $tmp_sanitized;
- unset($tmp_sanitized);
- }
- }
-
-
- // Donate Link: URL
- if ( preg_match('|Donate link:(.*)|i', $file_contents, $_donate_link) )
- $donate_link = esc_url( $_donate_link[1] );
- else
- $donate_link = NULL;
-
-
- // togs, conts, etc are optional and order shouldn't matter. So we chop them only after we've grabbed their values.
- foreach ( array('tags', 'contributors', 'requires_at_least', 'tested_up_to', 'stable_tag', 'donate_link') as $chop ) {
- if ( $$chop ) {
- $_chop = '_' . $chop;
- $file_contents = $this->chop_string( $file_contents, ${$_chop}[0] );
- }
- }
-
- $file_contents = trim($file_contents);
-
-
- // short-description fu
- if ( !preg_match('/(^(.*?))^[\s]*=+?[\s]*.+?[\s]*=+?/ms', $file_contents, $_short_description) )
- $_short_description = array( 1 => &$file_contents, 2 => &$file_contents );
- $short_desc_filtered = $this->sanitize_text( $_short_description[2] );
- $short_desc_length = strlen($short_desc_filtered);
- $short_description = substr($short_desc_filtered, 0, 150);
- if ( $short_desc_length > strlen($short_description) )
- $truncated = true;
- else
- $truncated = false;
- if ( $_short_description[1] )
- $file_contents = $this->chop_string( $file_contents, $_short_description[1] ); // yes, the [1] is intentional
-
- // == Section ==
- // Break into sections
- // $_sections[0] will be the title of the first section, $_sections[1] will be the content of the first section
- // the array alternates from there: title2, content2, title3, content3... and so forth
- $_sections = preg_split('/^[\s]*==[\s]*(.+?)[\s]*==/m', $file_contents, -1, PREG_SPLIT_DELIM_CAPTURE|PREG_SPLIT_NO_EMPTY);
-
- $sections = array();
- for ( $i=0; $i < count($_sections); $i +=2 ) {
- $title = $this->sanitize_text( $_sections[$i] );
- if ( isset($_sections[$i+1]) ) {
- $content = preg_replace('/(^[\s]*)=[\s]+(.+?)[\s]+=/m', '$1$2 ', $_sections[$i+1]);
- $content = $this->filter_text( $content, true );
- } else {
- $content = '';
- }
- $sections[str_replace(' ', '_', strtolower($title))] = array('title' => $title, 'content' => $content);
- }
-
-
- // Special sections
- // This is where we nab our special sections, so we can enforce their order and treat them differently, if needed
- // upgrade_notice is not a section, but parse it like it is for now
- $final_sections = array();
- foreach ( array('description', 'installation', 'frequently_asked_questions', 'screenshots', 'changelog', 'change_log', 'upgrade_notice') as $special_section ) {
- if ( isset($sections[$special_section]) ) {
- $final_sections[$special_section] = $sections[$special_section]['content'];
- unset($sections[$special_section]);
- }
- }
- if ( isset($final_sections['change_log']) && empty($final_sections['changelog']) )
- $final_sections['changelog'] = $final_sections['change_log'];
-
-
- $final_screenshots = array();
- if ( isset($final_sections['screenshots']) ) {
- preg_match_all('|(.*?) |s', $final_sections['screenshots'], $screenshots, PREG_SET_ORDER);
- if ( $screenshots ) {
- foreach ( (array) $screenshots as $ss )
- $final_screenshots[] = $ss[1];
- }
- }
-
- // Parse the upgrade_notice section specially:
- // 1.0 => blah, 1.1 => fnord
- $upgrade_notice = array();
- if ( isset($final_sections['upgrade_notice']) ) {
- $split = preg_split( '#(.*?) #', $final_sections['upgrade_notice'], -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY );
- if ( count($split) >= 2 ) {
- for ( $i = 0; $i < count( $split ); $i += 2 ) {
- $upgrade_notice[$this->sanitize_text( $split[$i] )] = substr( $this->sanitize_text( $split[$i + 1] ), 0, 300 );
- }
- }
- unset( $final_sections['upgrade_notice'] );
- }
-
- // No description?
- // No problem... we'll just fall back to the old style of description
- // We'll even let you use markup this time!
- $excerpt = false;
- if ( !isset($final_sections['description']) ) {
- $final_sections = array_merge(array('description' => $this->filter_text( $_short_description[2], true )), $final_sections);
- $excerpt = true;
- }
-
-
- // dump the non-special sections into $remaining_content
- // their order will be determined by their original order in the readme.txt
- $remaining_content = '';
- foreach ( $sections as $s_name => $s_data ) {
- $remaining_content .= "\n{$s_data['title']} \n{$s_data['content']}";
- }
- $remaining_content = trim($remaining_content);
-
-
- // All done!
- // $r['tags'] and $r['contributors'] are simple arrays
- // $r['sections'] is an array with named elements
- $r = array(
- 'name' => $name,
- 'tags' => $tags,
- 'requires_at_least' => $requires_at_least,
- 'tested_up_to' => $tested_up_to,
- 'requires_php' => $requires_php,
- 'stable_tag' => $stable_tag,
- 'contributors' => $contributors,
- 'donate_link' => $donate_link,
- 'short_description' => $short_description,
- 'screenshots' => $final_screenshots,
- 'is_excerpt' => $excerpt,
- 'is_truncated' => $truncated,
- 'sections' => $final_sections,
- 'remaining_content' => $remaining_content,
- 'upgrade_notice' => $upgrade_notice
- );
-
- return $r;
- }
-
- function chop_string( $string, $chop ) { // chop a "prefix" from a string: Agressive! uses strstr not 0 === strpos
- if ( $_string = strstr($string, $chop) ) {
- $_string = substr($_string, strlen($chop));
- return trim($_string);
- } else {
- return trim($string);
- }
- }
-
- function user_sanitize( $text, $strict = false ) { // whitelisted chars
- if ( function_exists('user_sanitize') ) // bbPress native
- return user_sanitize( $text, $strict );
-
- if ( $strict ) {
- $text = preg_replace('/[^a-z0-9-]/i', '', $text);
- $text = preg_replace('|-+|', '-', $text);
- } else {
- $text = preg_replace('/[^a-z0-9_-]/i', '', $text);
- }
- return $text;
- }
-
- function sanitize_text( $text ) { // not fancy
- $text = function_exists('wp_strip_all_tags')
- ? wp_strip_all_tags($text)
- //phpcs:ignore WordPressVIPMinimum.Functions.StripTags.StripTagsOneParameter -- Using wp_strip_all_tags() if available
- : strip_tags($text);
-
- $text = esc_html($text);
- $text = trim($text);
- return $text;
- }
-
- function filter_text( $text, $markdown = false ) { // fancy, Markdown
- $text = trim($text);
-
- $text = call_user_func( array( __CLASS__, 'code_trick' ), $text, $markdown ); // A better parser than Markdown's for: backticks -> CODE
-
- if ( $markdown ) { // Parse markdown.
- if ( !class_exists('Parsedown', false) ) {
- /** @noinspection PhpIncludeInspection */
- require_once(dirname(__FILE__) . '/Parsedown' . (version_compare(PHP_VERSION, '5.3.0', '>=') ? '' : 'Legacy') . '.php');
- }
- $instance = Parsedown::instance();
- $text = $instance->text($text);
- }
-
- $allowed = array(
- 'a' => array(
- 'href' => array(),
- 'title' => array(),
- 'rel' => array()),
- 'blockquote' => array('cite' => array()),
- 'br' => array(),
- 'p' => array(),
- 'code' => array(),
- 'pre' => array(),
- 'em' => array(),
- 'strong' => array(),
- 'ul' => array(),
- 'ol' => array(),
- 'li' => array(),
- 'h3' => array(),
- 'h4' => array()
- );
-
- $text = balanceTags($text);
-
- $text = wp_kses( $text, $allowed );
- $text = trim($text);
- return $text;
- }
-
- function code_trick( $text, $markdown ) { // Don't use bbPress native function - it's incompatible with Markdown
- // If doing markdown, first take any user formatted code blocks and turn them into backticks so that
- // markdown will preserve things like underscores in code blocks
- if ( $markdown )
- $text = preg_replace_callback("!(|)(.*?)( |)!s", array( __CLASS__,'decodeit'), $text);
-
- $text = str_replace(array("\r\n", "\r"), "\n", $text);
- if ( !$markdown ) {
- // This gets the "inline" code blocks, but can't be used with Markdown.
- $text = preg_replace_callback("|(`)(.*?)`|", array( __CLASS__, 'encodeit'), $text);
- // This gets the "block level" code blocks and converts them to PRE CODE
- $text = preg_replace_callback("!(^|\n)`(.*?)`!s", array( __CLASS__, 'encodeit'), $text);
- } else {
- // Markdown can do inline code, we convert bbPress style block level code to Markdown style
- $text = preg_replace_callback("!(^|\n)([ \t]*?)`(.*?)`!s", array( __CLASS__, 'indent'), $text);
- }
- return $text;
- }
-
- function indent( $matches ) {
- $text = $matches[3];
- $text = preg_replace('|^|m', $matches[2] . ' ', $text);
- return $matches[1] . $text;
- }
-
- function encodeit( $matches ) {
- if ( function_exists('encodeit') ) // bbPress native
- return encodeit( $matches );
-
- $text = trim($matches[2]);
- $text = htmlspecialchars($text, ENT_QUOTES);
- $text = str_replace(array("\r\n", "\r"), "\n", $text);
- $text = preg_replace("|\n\n\n+|", "\n\n", $text);
- $text = str_replace('<', '<', $text);
- $text = str_replace('>', '>', $text);
- $text = "$text";
- if ( "`" != $matches[1] )
- $text = "$text ";
- return $text;
- }
-
- function decodeit( $matches ) {
- if ( function_exists('decodeit') ) // bbPress native
- return decodeit( $matches );
-
- $text = $matches[2];
- $trans_table = array_flip(get_html_translation_table(HTML_ENTITIES));
- $text = strtr($text, $trans_table);
- $text = str_replace(' ', '', $text);
- $text = str_replace('&', '&', $text);
- $text = str_replace(''', "'", $text);
- if ( '' == $matches[1] )
- $text = "\n$text\n";
- return "`$text`";
- }
-
-} // end class
-
-endif;
diff --git a/projects-portfolio.php b/projects-portfolio.php
index d9dc47e..eb43cbe 100644
--- a/projects-portfolio.php
+++ b/projects-portfolio.php
@@ -34,9 +34,9 @@ $myUpdateChecker = PucFactory::buildUpdateChecker(
__FILE__,
'projects-portfolio'
);
-
-// Set the branch that contains the stable release.
-$myUpdateChecker->setBranch( 'main' );
+// Note: PUC's setBranch() is only available when the host is recognized as a
+// VCS provider (GitHub, GitLab, Bitbucket). The Gitea URL here falls back to
+// PUC's plain JSON metadata mode, so we skip the call rather than throw.
// Current plugin version.
define( 'PROJECTS_PORTFOLIO_VERSION', '1.1.1' );