Initial Commit
This commit is contained in:
63
dependencies/yahnis-elsts/plugin-update-checker/Puc/v4p11/Autoloader.php
vendored
Normal file
63
dependencies/yahnis-elsts/plugin-update-checker/Puc/v4p11/Autoloader.php
vendored
Normal file
@ -0,0 +1,63 @@
|
||||
<?php
|
||||
|
||||
if ( !class_exists('Puc_v4p11_Autoloader', false) ):
|
||||
|
||||
class Puc_v4p11_Autoloader {
|
||||
private $prefix = '';
|
||||
private $rootDir = '';
|
||||
private $libraryDir = '';
|
||||
|
||||
private $staticMap;
|
||||
|
||||
public function __construct() {
|
||||
$this->rootDir = dirname(__FILE__) . '/';
|
||||
$nameParts = explode('_', __CLASS__, 3);
|
||||
$this->prefix = $nameParts[0] . '_' . $nameParts[1] . '_';
|
||||
|
||||
$this->libraryDir = $this->rootDir . '../..';
|
||||
if ( !self::isPhar() ) {
|
||||
$this->libraryDir = realpath($this->libraryDir);
|
||||
}
|
||||
$this->libraryDir = $this->libraryDir . '/';
|
||||
|
||||
$this->staticMap = array(
|
||||
'PucReadmeParser' => 'vendor/PucReadmeParser.php',
|
||||
'Parsedown' => 'vendor/Parsedown.php',
|
||||
'Puc_v4_Factory' => 'Puc/v4/Factory.php',
|
||||
);
|
||||
|
||||
spl_autoload_register(array($this, 'autoload'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if this file is running as part of a Phar archive.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private static function isPhar() {
|
||||
//Check if the current file path starts with "phar://".
|
||||
static $pharProtocol = 'phar://';
|
||||
return (substr(__FILE__, 0, strlen($pharProtocol)) === $pharProtocol);
|
||||
}
|
||||
|
||||
public function autoload($className) {
|
||||
if ( isset($this->staticMap[$className]) && file_exists($this->libraryDir . $this->staticMap[$className]) ) {
|
||||
/** @noinspection PhpIncludeInspection */
|
||||
include ($this->libraryDir . $this->staticMap[$className]);
|
||||
return;
|
||||
}
|
||||
|
||||
if (strpos($className, $this->prefix) === 0) {
|
||||
$path = substr($className, strlen($this->prefix));
|
||||
$path = str_replace('_', '/', $path);
|
||||
$path = $this->rootDir . $path . '.php';
|
||||
|
||||
if (file_exists($path)) {
|
||||
/** @noinspection PhpIncludeInspection */
|
||||
include $path;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
endif;
|
190
dependencies/yahnis-elsts/plugin-update-checker/Puc/v4p11/DebugBar/Extension.php
vendored
Normal file
190
dependencies/yahnis-elsts/plugin-update-checker/Puc/v4p11/DebugBar/Extension.php
vendored
Normal file
@ -0,0 +1,190 @@
|
||||
<?php
|
||||
if ( !class_exists('Puc_v4p11_DebugBar_Extension', false) ):
|
||||
|
||||
class Puc_v4p11_DebugBar_Extension {
|
||||
const RESPONSE_BODY_LENGTH_LIMIT = 4000;
|
||||
|
||||
/** @var Puc_v4p11_UpdateChecker */
|
||||
protected $updateChecker;
|
||||
protected $panelClass = 'Puc_v4p11_DebugBar_Panel';
|
||||
|
||||
public function __construct($updateChecker, $panelClass = null) {
|
||||
$this->updateChecker = $updateChecker;
|
||||
if ( isset($panelClass) ) {
|
||||
$this->panelClass = $panelClass;
|
||||
}
|
||||
|
||||
if ( version_compare(PHP_VERSION, '5.3', '>=') && (strpos($this->panelClass, '\\') === false) ) {
|
||||
$this->panelClass = __NAMESPACE__ . '\\' . $this->panelClass;
|
||||
}
|
||||
|
||||
add_filter('debug_bar_panels', array($this, 'addDebugBarPanel'));
|
||||
add_action('debug_bar_enqueue_scripts', array($this, 'enqueuePanelDependencies'));
|
||||
|
||||
add_action('wp_ajax_puc_v4_debug_check_now', array($this, 'ajaxCheckNow'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the PUC Debug Bar panel.
|
||||
*
|
||||
* @param array $panels
|
||||
* @return array
|
||||
*/
|
||||
public function addDebugBarPanel($panels) {
|
||||
if ( $this->updateChecker->userCanInstallUpdates() ) {
|
||||
$panels[] = new $this->panelClass($this->updateChecker);
|
||||
}
|
||||
return $panels;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enqueue our Debug Bar scripts and styles.
|
||||
*/
|
||||
public function enqueuePanelDependencies() {
|
||||
wp_enqueue_style(
|
||||
'puc-debug-bar-style-v4',
|
||||
$this->getLibraryUrl("/css/puc-debug-bar.css"),
|
||||
array('debug-bar'),
|
||||
'20171124'
|
||||
);
|
||||
|
||||
wp_enqueue_script(
|
||||
'puc-debug-bar-js-v4',
|
||||
$this->getLibraryUrl("/js/debug-bar.js"),
|
||||
array('jquery'),
|
||||
'20201209'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Run an update check and output the result. Useful for making sure that
|
||||
* the update checking process works as expected.
|
||||
*/
|
||||
public function ajaxCheckNow() {
|
||||
if ( $_POST['uid'] !== $this->updateChecker->getUniqueName('uid') ) {
|
||||
return;
|
||||
}
|
||||
$this->preAjaxRequest();
|
||||
$update = $this->updateChecker->checkForUpdates();
|
||||
if ( $update !== null ) {
|
||||
echo "An update is available:";
|
||||
echo '<pre>', htmlentities(print_r($update, true)), '</pre>';
|
||||
} else {
|
||||
echo 'No updates found.';
|
||||
}
|
||||
|
||||
$errors = $this->updateChecker->getLastRequestApiErrors();
|
||||
if ( !empty($errors) ) {
|
||||
printf('<p>The update checker encountered %d API error%s.</p>', count($errors), (count($errors) > 1) ? 's' : '');
|
||||
|
||||
foreach (array_values($errors) as $num => $item) {
|
||||
$wpError = $item['error'];
|
||||
/** @var WP_Error $wpError */
|
||||
printf('<h4>%d) %s</h4>', $num + 1, esc_html($wpError->get_error_message()));
|
||||
|
||||
echo '<dl>';
|
||||
printf('<dt>Error code:</dt><dd><code>%s</code></dd>', esc_html($wpError->get_error_code()));
|
||||
|
||||
if ( isset($item['url']) ) {
|
||||
printf('<dt>Requested URL:</dt><dd><code>%s</code></dd>', esc_html($item['url']));
|
||||
}
|
||||
|
||||
if ( isset($item['httpResponse']) ) {
|
||||
if ( is_wp_error($item['httpResponse']) ) {
|
||||
$httpError = $item['httpResponse'];
|
||||
/** @var WP_Error $httpError */
|
||||
printf(
|
||||
'<dt>WordPress HTTP API error:</dt><dd>%s (<code>%s</code>)</dd>',
|
||||
esc_html($httpError->get_error_message()),
|
||||
esc_html($httpError->get_error_code())
|
||||
);
|
||||
} else {
|
||||
//Status code.
|
||||
printf(
|
||||
'<dt>HTTP status:</dt><dd><code>%d %s</code></dd>',
|
||||
wp_remote_retrieve_response_code($item['httpResponse']),
|
||||
wp_remote_retrieve_response_message($item['httpResponse'])
|
||||
);
|
||||
|
||||
//Headers.
|
||||
echo '<dt>Response headers:</dt><dd><pre>';
|
||||
foreach (wp_remote_retrieve_headers($item['httpResponse']) as $name => $value) {
|
||||
printf("%s: %s\n", esc_html($name), esc_html($value));
|
||||
}
|
||||
echo '</pre></dd>';
|
||||
|
||||
//Body.
|
||||
$body = wp_remote_retrieve_body($item['httpResponse']);
|
||||
if ( $body === '' ) {
|
||||
$body = '(Empty response.)';
|
||||
} else if ( strlen($body) > self::RESPONSE_BODY_LENGTH_LIMIT ) {
|
||||
$length = strlen($body);
|
||||
$body = substr($body, 0, self::RESPONSE_BODY_LENGTH_LIMIT)
|
||||
. sprintf("\n(Long string truncated. Total length: %d bytes.)", $length);
|
||||
}
|
||||
|
||||
printf('<dt>Response body:</dt><dd><pre>%s</pre></dd>', esc_html($body));
|
||||
}
|
||||
}
|
||||
echo '<dl>';
|
||||
}
|
||||
}
|
||||
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check access permissions and enable error display (for debugging).
|
||||
*/
|
||||
protected function preAjaxRequest() {
|
||||
if ( !$this->updateChecker->userCanInstallUpdates() ) {
|
||||
die('Access denied');
|
||||
}
|
||||
check_ajax_referer('puc-ajax');
|
||||
|
||||
error_reporting(E_ALL);
|
||||
@ini_set('display_errors', 'On');
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove hooks that were added by this extension.
|
||||
*/
|
||||
public function removeHooks() {
|
||||
remove_filter('debug_bar_panels', array($this, 'addDebugBarPanel'));
|
||||
remove_action('debug_bar_enqueue_scripts', array($this, 'enqueuePanelDependencies'));
|
||||
remove_action('wp_ajax_puc_v4_debug_check_now', array($this, 'ajaxCheckNow'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $filePath
|
||||
* @return string
|
||||
*/
|
||||
private function getLibraryUrl($filePath) {
|
||||
$absolutePath = realpath(dirname(__FILE__) . '/../../../' . ltrim($filePath, '/'));
|
||||
|
||||
//Where is the library located inside the WordPress directory structure?
|
||||
$absolutePath = Puc_v4p11_Factory::normalizePath($absolutePath);
|
||||
|
||||
$pluginDir = Puc_v4p11_Factory::normalizePath(WP_PLUGIN_DIR);
|
||||
$muPluginDir = Puc_v4p11_Factory::normalizePath(WPMU_PLUGIN_DIR);
|
||||
$themeDir = Puc_v4p11_Factory::normalizePath(get_theme_root());
|
||||
|
||||
if ( (strpos($absolutePath, $pluginDir) === 0) || (strpos($absolutePath, $muPluginDir) === 0) ) {
|
||||
//It's part of a plugin.
|
||||
return plugins_url(basename($absolutePath), $absolutePath);
|
||||
} else if ( strpos($absolutePath, $themeDir) === 0 ) {
|
||||
//It's part of a theme.
|
||||
$relativePath = substr($absolutePath, strlen($themeDir) + 1);
|
||||
$template = substr($relativePath, 0, strpos($relativePath, '/'));
|
||||
$baseUrl = get_theme_root_uri($template);
|
||||
|
||||
if ( !empty($baseUrl) && $relativePath ) {
|
||||
return $baseUrl . '/' . $relativePath;
|
||||
}
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
endif;
|
165
dependencies/yahnis-elsts/plugin-update-checker/Puc/v4p11/DebugBar/Panel.php
vendored
Normal file
165
dependencies/yahnis-elsts/plugin-update-checker/Puc/v4p11/DebugBar/Panel.php
vendored
Normal file
@ -0,0 +1,165 @@
|
||||
<?php
|
||||
|
||||
if ( !class_exists('Puc_v4p11_DebugBar_Panel', false) && class_exists('Debug_Bar_Panel', false) ):
|
||||
|
||||
class Puc_v4p11_DebugBar_Panel extends Debug_Bar_Panel {
|
||||
/** @var Puc_v4p11_UpdateChecker */
|
||||
protected $updateChecker;
|
||||
|
||||
private $responseBox = '<div class="puc-ajax-response" style="display: none;"></div>';
|
||||
|
||||
public function __construct($updateChecker) {
|
||||
$this->updateChecker = $updateChecker;
|
||||
$title = sprintf(
|
||||
'<span class="puc-debug-menu-link-%s">PUC (%s)</span>',
|
||||
esc_attr($this->updateChecker->getUniqueName('uid')),
|
||||
$this->updateChecker->slug
|
||||
);
|
||||
parent::__construct($title);
|
||||
}
|
||||
|
||||
public function render() {
|
||||
printf(
|
||||
'<div class="puc-debug-bar-panel-v4" id="%1$s" data-slug="%2$s" data-uid="%3$s" data-nonce="%4$s">',
|
||||
esc_attr($this->updateChecker->getUniqueName('debug-bar-panel')),
|
||||
esc_attr($this->updateChecker->slug),
|
||||
esc_attr($this->updateChecker->getUniqueName('uid')),
|
||||
esc_attr(wp_create_nonce('puc-ajax'))
|
||||
);
|
||||
|
||||
$this->displayConfiguration();
|
||||
$this->displayStatus();
|
||||
$this->displayCurrentUpdate();
|
||||
|
||||
echo '</div>';
|
||||
}
|
||||
|
||||
private function displayConfiguration() {
|
||||
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));
|
||||
|
||||
$requestInfoButton = $this->getMetadataButton();
|
||||
$this->row('Metadata URL', htmlentities($this->updateChecker->metadataUrl) . ' ' . $requestInfoButton . $this->responseBox);
|
||||
|
||||
$scheduler = $this->updateChecker->scheduler;
|
||||
if ( $scheduler->checkPeriod > 0 ) {
|
||||
$this->row('Automatic checks', 'Every ' . $scheduler->checkPeriod . ' hours');
|
||||
} else {
|
||||
$this->row('Automatic checks', 'Disabled');
|
||||
}
|
||||
|
||||
if ( isset($scheduler->throttleRedundantChecks) ) {
|
||||
if ( $scheduler->throttleRedundantChecks && ($scheduler->checkPeriod > 0) ) {
|
||||
$this->row(
|
||||
'Throttling',
|
||||
sprintf(
|
||||
'Enabled. If an update is already available, check for updates every %1$d hours instead of every %2$d hours.',
|
||||
$scheduler->throttledCheckPeriod,
|
||||
$scheduler->checkPeriod
|
||||
)
|
||||
);
|
||||
} else {
|
||||
$this->row('Throttling', 'Disabled');
|
||||
}
|
||||
}
|
||||
|
||||
$this->updateChecker->onDisplayConfiguration($this);
|
||||
|
||||
echo '</table>';
|
||||
}
|
||||
|
||||
protected function displayConfigHeader() {
|
||||
//Do nothing. This should be implemented in subclasses.
|
||||
}
|
||||
|
||||
protected function getMetadataButton() {
|
||||
return '';
|
||||
}
|
||||
|
||||
private function displayStatus() {
|
||||
echo '<h3>Status</h3>';
|
||||
echo '<table class="puc-debug-data">';
|
||||
$state = $this->updateChecker->getUpdateState();
|
||||
$checkNowButton = '';
|
||||
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'))
|
||||
);
|
||||
}
|
||||
|
||||
if ( $state->getLastCheck() > 0 ) {
|
||||
$this->row('Last check', $this->formatTimeWithDelta($state->getLastCheck()) . ' ' . $checkNowButton . $this->responseBox);
|
||||
} else {
|
||||
$this->row('Last check', 'Never');
|
||||
}
|
||||
|
||||
$nextCheck = wp_next_scheduled($this->updateChecker->scheduler->getCronHookName());
|
||||
$this->row('Next automatic check', $this->formatTimeWithDelta($nextCheck));
|
||||
|
||||
if ( $state->getCheckedVersion() !== '' ) {
|
||||
$this->row('Checked version', htmlentities($state->getCheckedVersion()));
|
||||
$this->row('Cached update', $state->getUpdate());
|
||||
}
|
||||
$this->row('Update checker class', htmlentities(get_class($this->updateChecker)));
|
||||
echo '</table>';
|
||||
}
|
||||
|
||||
private function displayCurrentUpdate() {
|
||||
$update = $this->updateChecker->getUpdate();
|
||||
if ( $update !== null ) {
|
||||
echo '<h3>An Update Is Available</h3>';
|
||||
echo '<table class="puc-debug-data">';
|
||||
$fields = $this->getUpdateFields();
|
||||
foreach($fields as $field) {
|
||||
if ( property_exists($update, $field) ) {
|
||||
$this->row(ucwords(str_replace('_', ' ', $field)), htmlentities($update->$field));
|
||||
}
|
||||
}
|
||||
echo '</table>';
|
||||
} else {
|
||||
echo '<h3>No updates currently available</h3>';
|
||||
}
|
||||
}
|
||||
|
||||
protected function getUpdateFields() {
|
||||
return array('version', 'download_url', 'slug',);
|
||||
}
|
||||
|
||||
private function formatTimeWithDelta($unixTime) {
|
||||
if ( empty($unixTime) ) {
|
||||
return 'Never';
|
||||
}
|
||||
|
||||
$delta = time() - $unixTime;
|
||||
$result = human_time_diff(time(), $unixTime);
|
||||
if ( $delta < 0 ) {
|
||||
$result = 'after ' . $result;
|
||||
} else {
|
||||
$result = $result . ' ago';
|
||||
}
|
||||
$result .= ' (' . $this->formatTimestamp($unixTime) . ')';
|
||||
return $result;
|
||||
}
|
||||
|
||||
private function formatTimestamp($unixTime) {
|
||||
return gmdate('Y-m-d H:i:s', $unixTime + (get_option('gmt_offset') * 3600));
|
||||
}
|
||||
|
||||
public function row($name, $value) {
|
||||
if ( is_object($value) || is_array($value) ) {
|
||||
$value = '<pre>' . htmlentities(print_r($value, true)) . '</pre>';
|
||||
} else if ($value === null) {
|
||||
$value = '<code>null</code>';
|
||||
}
|
||||
printf('<tr><th scope="row">%1$s</th> <td>%2$s</td></tr>', $name, $value);
|
||||
}
|
||||
}
|
||||
|
||||
endif;
|
33
dependencies/yahnis-elsts/plugin-update-checker/Puc/v4p11/DebugBar/PluginExtension.php
vendored
Normal file
33
dependencies/yahnis-elsts/plugin-update-checker/Puc/v4p11/DebugBar/PluginExtension.php
vendored
Normal file
@ -0,0 +1,33 @@
|
||||
<?php
|
||||
if ( !class_exists('Puc_v4p11_DebugBar_PluginExtension', false) ):
|
||||
|
||||
class Puc_v4p11_DebugBar_PluginExtension extends Puc_v4p11_DebugBar_Extension {
|
||||
/** @var Puc_v4p11_Plugin_UpdateChecker */
|
||||
protected $updateChecker;
|
||||
|
||||
public function __construct($updateChecker) {
|
||||
parent::__construct($updateChecker, 'Puc_v4p11_DebugBar_PluginPanel');
|
||||
|
||||
add_action('wp_ajax_puc_v4_debug_request_info', array($this, 'ajaxRequestInfo'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Request plugin info and output it.
|
||||
*/
|
||||
public function ajaxRequestInfo() {
|
||||
if ( $_POST['uid'] !== $this->updateChecker->getUniqueName('uid') ) {
|
||||
return;
|
||||
}
|
||||
$this->preAjaxRequest();
|
||||
$info = $this->updateChecker->requestInfo();
|
||||
if ( $info !== null ) {
|
||||
echo 'Successfully retrieved plugin info from the metadata URL:';
|
||||
echo '<pre>', htmlentities(print_r($info, true)), '</pre>';
|
||||
} else {
|
||||
echo 'Failed to retrieve plugin info from the metadata URL.';
|
||||
}
|
||||
exit;
|
||||
}
|
||||
}
|
||||
|
||||
endif;
|
38
dependencies/yahnis-elsts/plugin-update-checker/Puc/v4p11/DebugBar/PluginPanel.php
vendored
Normal file
38
dependencies/yahnis-elsts/plugin-update-checker/Puc/v4p11/DebugBar/PluginPanel.php
vendored
Normal file
@ -0,0 +1,38 @@
|
||||
<?php
|
||||
|
||||
if ( !class_exists('Puc_v4p11_DebugBar_PluginPanel', false) ):
|
||||
|
||||
class Puc_v4p11_DebugBar_PluginPanel extends Puc_v4p11_DebugBar_Panel {
|
||||
/**
|
||||
* @var Puc_v4p11_Plugin_UpdateChecker
|
||||
*/
|
||||
protected $updateChecker;
|
||||
|
||||
protected function displayConfigHeader() {
|
||||
$this->row('Plugin file', htmlentities($this->updateChecker->pluginFile));
|
||||
parent::displayConfigHeader();
|
||||
}
|
||||
|
||||
protected function getMetadataButton() {
|
||||
$requestInfoButton = '';
|
||||
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'))
|
||||
);
|
||||
}
|
||||
return $requestInfoButton;
|
||||
}
|
||||
|
||||
protected function getUpdateFields() {
|
||||
return array_merge(
|
||||
parent::getUpdateFields(),
|
||||
array('homepage', 'upgrade_notice', 'tested',)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
endif;
|
21
dependencies/yahnis-elsts/plugin-update-checker/Puc/v4p11/DebugBar/ThemePanel.php
vendored
Normal file
21
dependencies/yahnis-elsts/plugin-update-checker/Puc/v4p11/DebugBar/ThemePanel.php
vendored
Normal file
@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
if ( !class_exists('Puc_v4p11_DebugBar_ThemePanel', false) ):
|
||||
|
||||
class Puc_v4p11_DebugBar_ThemePanel extends Puc_v4p11_DebugBar_Panel {
|
||||
/**
|
||||
* @var Puc_v4p11_Theme_UpdateChecker
|
||||
*/
|
||||
protected $updateChecker;
|
||||
|
||||
protected function displayConfigHeader() {
|
||||
$this->row('Theme directory', htmlentities($this->updateChecker->directoryName));
|
||||
parent::displayConfigHeader();
|
||||
}
|
||||
|
||||
protected function getUpdateFields() {
|
||||
return array_merge(parent::getUpdateFields(), array('details_url'));
|
||||
}
|
||||
}
|
||||
|
||||
endif;
|
365
dependencies/yahnis-elsts/plugin-update-checker/Puc/v4p11/Factory.php
vendored
Normal file
365
dependencies/yahnis-elsts/plugin-update-checker/Puc/v4p11/Factory.php
vendored
Normal file
@ -0,0 +1,365 @@
|
||||
<?php
|
||||
if ( !class_exists('Puc_v4p11_Factory', false) ):
|
||||
|
||||
/**
|
||||
* A factory that builds update checker instances.
|
||||
*
|
||||
* When multiple versions of the same class have been loaded (e.g. PluginUpdateChecker 4.0
|
||||
* and 4.1), this factory will always use the latest available minor version. Register class
|
||||
* versions by calling {@link PucFactory::addVersion()}.
|
||||
*
|
||||
* At the moment it can only build instances of the UpdateChecker class. Other classes are
|
||||
* intended mainly for internal use and refer directly to specific implementations.
|
||||
*/
|
||||
class Puc_v4p11_Factory {
|
||||
protected static $classVersions = array();
|
||||
protected static $sorted = false;
|
||||
|
||||
protected static $myMajorVersion = '';
|
||||
protected static $latestCompatibleVersion = '';
|
||||
|
||||
/**
|
||||
* A wrapper method for buildUpdateChecker() that reads the metadata URL from the plugin or theme header.
|
||||
*
|
||||
* @param string $fullPath Full path to the main plugin file or the theme's style.css.
|
||||
* @param array $args Optional arguments. Keys should match the argument names of the buildUpdateChecker() method.
|
||||
* @return Puc_v4p11_Plugin_UpdateChecker|Puc_v4p11_Theme_UpdateChecker|Puc_v4p11_Vcs_BaseChecker
|
||||
*/
|
||||
public static function buildFromHeader($fullPath, $args = array()) {
|
||||
$fullPath = self::normalizePath($fullPath);
|
||||
|
||||
//Set up defaults.
|
||||
$defaults = array(
|
||||
'metadataUrl' => '',
|
||||
'slug' => '',
|
||||
'checkPeriod' => 12,
|
||||
'optionName' => '',
|
||||
'muPluginFile' => '',
|
||||
);
|
||||
$args = array_merge($defaults, array_intersect_key($args, $defaults));
|
||||
extract($args, EXTR_SKIP);
|
||||
|
||||
//Check for the service URI
|
||||
if ( empty($metadataUrl) ) {
|
||||
$metadataUrl = self::getServiceURI($fullPath);
|
||||
}
|
||||
|
||||
/** @noinspection PhpUndefinedVariableInspection These variables are created by extract(), above. */
|
||||
return self::buildUpdateChecker($metadataUrl, $fullPath, $slug, $checkPeriod, $optionName, $muPluginFile);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new instance of the update checker.
|
||||
*
|
||||
* This method automatically detects if you're using it for a plugin or a theme and chooses
|
||||
* the appropriate implementation for your update source (JSON file, GitHub, BitBucket, etc).
|
||||
*
|
||||
* @see Puc_v4p11_UpdateChecker::__construct
|
||||
*
|
||||
* @param string $metadataUrl The URL of the metadata file, a GitHub repository, or another supported update source.
|
||||
* @param string $fullPath Full path to the main plugin file or to the theme directory.
|
||||
* @param string $slug Custom slug. Defaults to the name of the main plugin file or the theme directory.
|
||||
* @param int $checkPeriod How often to check for updates (in hours).
|
||||
* @param string $optionName Where to store book-keeping info about update checks.
|
||||
* @param string $muPluginFile The plugin filename relative to the mu-plugins directory.
|
||||
* @return Puc_v4p11_Plugin_UpdateChecker|Puc_v4p11_Theme_UpdateChecker|Puc_v4p11_Vcs_BaseChecker
|
||||
*/
|
||||
public static function buildUpdateChecker($metadataUrl, $fullPath, $slug = '', $checkPeriod = 12, $optionName = '', $muPluginFile = '') {
|
||||
$fullPath = self::normalizePath($fullPath);
|
||||
$id = null;
|
||||
|
||||
//Plugin or theme?
|
||||
$themeDirectory = self::getThemeDirectoryName($fullPath);
|
||||
if ( self::isPluginFile($fullPath) ) {
|
||||
$type = 'Plugin';
|
||||
$id = $fullPath;
|
||||
} else if ( $themeDirectory !== null ) {
|
||||
$type = 'Theme';
|
||||
$id = $themeDirectory;
|
||||
} else {
|
||||
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)
|
||||
));
|
||||
}
|
||||
|
||||
//Which hosting service does the URL point to?
|
||||
$service = self::getVcsService($metadataUrl);
|
||||
|
||||
$apiClass = null;
|
||||
if ( empty($service) ) {
|
||||
//The default is to get update information from a remote JSON file.
|
||||
$checkerClass = $type . '_UpdateChecker';
|
||||
} else {
|
||||
//You can also use a VCS repository like GitHub.
|
||||
$checkerClass = 'Vcs_' . $type . 'UpdateChecker';
|
||||
$apiClass = $service . 'Api';
|
||||
}
|
||||
|
||||
$checkerClass = self::getCompatibleClassVersion($checkerClass);
|
||||
if ( $checkerClass === null ) {
|
||||
trigger_error(
|
||||
sprintf(
|
||||
'PUC %s does not support updates for %ss %s',
|
||||
htmlentities(self::$latestCompatibleVersion),
|
||||
strtolower($type),
|
||||
$service ? ('hosted on ' . htmlentities($service)) : 'using JSON metadata'
|
||||
),
|
||||
E_USER_ERROR
|
||||
);
|
||||
return null;
|
||||
}
|
||||
|
||||
//Add the current namespace to the class name(s).
|
||||
if ( version_compare(PHP_VERSION, '5.3', '>=') ) {
|
||||
$checkerClass = __NAMESPACE__ . '\\' . $checkerClass;
|
||||
}
|
||||
|
||||
if ( !isset($apiClass) ) {
|
||||
//Plain old update checker.
|
||||
return new $checkerClass($metadataUrl, $id, $slug, $checkPeriod, $optionName, $muPluginFile);
|
||||
} else {
|
||||
//VCS checker + an API client.
|
||||
$apiClass = self::getCompatibleClassVersion($apiClass);
|
||||
if ( $apiClass === null ) {
|
||||
trigger_error(sprintf(
|
||||
'PUC %s does not support %s',
|
||||
htmlentities(self::$latestCompatibleVersion),
|
||||
htmlentities($service)
|
||||
), E_USER_ERROR);
|
||||
return null;
|
||||
}
|
||||
|
||||
if ( version_compare(PHP_VERSION, '5.3', '>=') && (strpos($apiClass, '\\') === false) ) {
|
||||
$apiClass = __NAMESPACE__ . '\\' . $apiClass;
|
||||
}
|
||||
|
||||
return new $checkerClass(
|
||||
new $apiClass($metadataUrl),
|
||||
$id,
|
||||
$slug,
|
||||
$checkPeriod,
|
||||
$optionName,
|
||||
$muPluginFile
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* 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.0)
|
||||
*
|
||||
* @param string $path Path to normalize.
|
||||
* @return string Normalized path.
|
||||
*/
|
||||
public static function normalizePath($path) {
|
||||
if ( function_exists('wp_normalize_path') ) {
|
||||
return wp_normalize_path($path);
|
||||
}
|
||||
$path = str_replace('\\', '/', $path);
|
||||
$path = preg_replace('|(?<=.)/+|', '/', $path);
|
||||
if ( substr($path, 1, 1) === ':' ) {
|
||||
$path = ucfirst($path);
|
||||
}
|
||||
return $path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the path points to a plugin file.
|
||||
*
|
||||
* @param string $absolutePath Normalized path.
|
||||
* @return bool
|
||||
*/
|
||||
protected static function isPluginFile($absolutePath) {
|
||||
//Is the file inside the "plugins" or "mu-plugins" directory?
|
||||
$pluginDir = self::normalizePath(WP_PLUGIN_DIR);
|
||||
$muPluginDir = self::normalizePath(WPMU_PLUGIN_DIR);
|
||||
if ( (strpos($absolutePath, $pluginDir) === 0) || (strpos($absolutePath, $muPluginDir) === 0) ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
//Is it a file at all? Caution: is_file() can fail if the parent dir. doesn't have the +x permission set.
|
||||
if ( !is_file($absolutePath) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
//Does it have a valid plugin header?
|
||||
//This is a last-ditch check for plugins symlinked from outside the WP root.
|
||||
if ( function_exists('get_file_data') ) {
|
||||
$headers = get_file_data($absolutePath, array('Name' => 'Plugin Name'), 'plugin');
|
||||
return !empty($headers['Name']);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the name of the theme's directory from a full path to a file inside that directory.
|
||||
* E.g. "/abc/public_html/wp-content/themes/foo/whatever.php" => "foo".
|
||||
*
|
||||
* Note that subdirectories are currently not supported. For example,
|
||||
* "/xyz/wp-content/themes/my-theme/includes/whatever.php" => NULL.
|
||||
*
|
||||
* @param string $absolutePath Normalized path.
|
||||
* @return string|null Directory name, or NULL if the path doesn't point to a theme.
|
||||
*/
|
||||
protected static function getThemeDirectoryName($absolutePath) {
|
||||
if ( is_file($absolutePath) ) {
|
||||
$absolutePath = dirname($absolutePath);
|
||||
}
|
||||
|
||||
if ( file_exists($absolutePath . '/style.css') ) {
|
||||
return basename($absolutePath);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the service URI from the file header.
|
||||
*
|
||||
* @param string $fullPath
|
||||
* @return string
|
||||
*/
|
||||
private static function getServiceURI($fullPath) {
|
||||
//Look for the URI
|
||||
if ( is_readable($fullPath) ) {
|
||||
$seek = array(
|
||||
'github' => 'GitHub URI',
|
||||
'gitlab' => 'GitLab URI',
|
||||
'bucket' => 'BitBucket URI',
|
||||
);
|
||||
$seek = apply_filters('puc_get_source_uri', $seek);
|
||||
$data = get_file_data($fullPath, $seek);
|
||||
foreach ($data as $key => $uri) {
|
||||
if ( $uri ) {
|
||||
return $uri;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//URI was not found so throw an error.
|
||||
throw new RuntimeException(
|
||||
sprintf('Unable to locate URI in header of "%s"', htmlentities($fullPath))
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the name of the hosting service that the URL points to.
|
||||
*
|
||||
* @param string $metadataUrl
|
||||
* @return string|null
|
||||
*/
|
||||
private static function getVcsService($metadataUrl) {
|
||||
$service = null;
|
||||
|
||||
//Which hosting service does the URL point to?
|
||||
$host = parse_url($metadataUrl, PHP_URL_HOST);
|
||||
$path = parse_url($metadataUrl, PHP_URL_PATH);
|
||||
|
||||
//Check if the path looks like "/user-name/repository".
|
||||
//For GitLab.com it can also be "/user/group1/group2/.../repository".
|
||||
$repoRegex = '@^/?([^/]+?)/([^/#?&]+?)/?$@';
|
||||
if ( $host === 'gitlab.com' ) {
|
||||
$repoRegex = '@^/?(?:[^/#?&]++/){1,20}(?:[^/#?&]++)/?$@';
|
||||
}
|
||||
if ( preg_match($repoRegex, $path) ) {
|
||||
$knownServices = array(
|
||||
'github.com' => 'GitHub',
|
||||
'bitbucket.org' => 'BitBucket',
|
||||
'gitlab.com' => 'GitLab',
|
||||
);
|
||||
if ( isset($knownServices[$host]) ) {
|
||||
$service = $knownServices[$host];
|
||||
}
|
||||
}
|
||||
|
||||
return apply_filters('puc_get_vcs_service', $service, $host, $path, $metadataUrl);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the latest version of the specified class that has the same major version number
|
||||
* as this factory class.
|
||||
*
|
||||
* @param string $class Partial class name.
|
||||
* @return string|null Full class name.
|
||||
*/
|
||||
protected static function getCompatibleClassVersion($class) {
|
||||
if ( isset(self::$classVersions[$class][self::$latestCompatibleVersion]) ) {
|
||||
return self::$classVersions[$class][self::$latestCompatibleVersion];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the specific class name for the latest available version of a class.
|
||||
*
|
||||
* @param string $class
|
||||
* @return null|string
|
||||
*/
|
||||
public static function getLatestClassVersion($class) {
|
||||
if ( !self::$sorted ) {
|
||||
self::sortVersions();
|
||||
}
|
||||
|
||||
if ( isset(self::$classVersions[$class]) ) {
|
||||
return reset(self::$classVersions[$class]);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sort available class versions in descending order (i.e. newest first).
|
||||
*/
|
||||
protected static function sortVersions() {
|
||||
foreach ( self::$classVersions as $class => $versions ) {
|
||||
uksort($versions, array(__CLASS__, 'compareVersions'));
|
||||
self::$classVersions[$class] = $versions;
|
||||
}
|
||||
self::$sorted = true;
|
||||
}
|
||||
|
||||
protected static function compareVersions($a, $b) {
|
||||
return -version_compare($a, $b);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a version of a class.
|
||||
*
|
||||
* @access private This method is only for internal use by the library.
|
||||
*
|
||||
* @param string $generalClass Class name without version numbers, e.g. 'PluginUpdateChecker'.
|
||||
* @param string $versionedClass Actual class name, e.g. 'PluginUpdateChecker_1_2'.
|
||||
* @param string $version Version number, e.g. '1.2'.
|
||||
*/
|
||||
public static function addVersion($generalClass, $versionedClass, $version) {
|
||||
if ( empty(self::$myMajorVersion) ) {
|
||||
$nameParts = explode('_', __CLASS__, 3);
|
||||
self::$myMajorVersion = substr(ltrim($nameParts[1], 'v'), 0, 1);
|
||||
}
|
||||
|
||||
//Store the greatest version number that matches our major version.
|
||||
$components = explode('.', $version);
|
||||
if ( $components[0] === self::$myMajorVersion ) {
|
||||
|
||||
if (
|
||||
empty(self::$latestCompatibleVersion)
|
||||
|| version_compare($version, self::$latestCompatibleVersion, '>')
|
||||
) {
|
||||
self::$latestCompatibleVersion = $version;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if ( !isset(self::$classVersions[$generalClass]) ) {
|
||||
self::$classVersions[$generalClass] = array();
|
||||
}
|
||||
self::$classVersions[$generalClass][$version] = $versionedClass;
|
||||
self::$sorted = false;
|
||||
}
|
||||
}
|
||||
|
||||
endif;
|
103
dependencies/yahnis-elsts/plugin-update-checker/Puc/v4p11/InstalledPackage.php
vendored
Normal file
103
dependencies/yahnis-elsts/plugin-update-checker/Puc/v4p11/InstalledPackage.php
vendored
Normal file
@ -0,0 +1,103 @@
|
||||
<?php
|
||||
if ( !class_exists('Puc_v4p11_InstalledPackage', false) ):
|
||||
|
||||
/**
|
||||
* This class represents a currently installed plugin or theme.
|
||||
*
|
||||
* Not to be confused with the "package" field in WP update API responses that contains
|
||||
* the download URL of a the new version.
|
||||
*/
|
||||
abstract class Puc_v4p11_InstalledPackage {
|
||||
/**
|
||||
* @var Puc_v4p11_UpdateChecker
|
||||
*/
|
||||
protected $updateChecker;
|
||||
|
||||
public function __construct($updateChecker) {
|
||||
$this->updateChecker = $updateChecker;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the currently installed version of the plugin or theme.
|
||||
*
|
||||
* @return string|null Version number.
|
||||
*/
|
||||
abstract public function getInstalledVersion();
|
||||
|
||||
/**
|
||||
* Get the full path of the plugin or theme directory (without a trailing slash).
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
abstract public function getAbsoluteDirectoryPath();
|
||||
|
||||
/**
|
||||
* Check whether a regular file exists in the package's directory.
|
||||
*
|
||||
* @param string $relativeFileName File name relative to the package directory.
|
||||
* @return bool
|
||||
*/
|
||||
public function fileExists($relativeFileName) {
|
||||
return is_file(
|
||||
$this->getAbsoluteDirectoryPath()
|
||||
. DIRECTORY_SEPARATOR
|
||||
. ltrim($relativeFileName, '/\\')
|
||||
);
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------
|
||||
* File header parsing
|
||||
* -------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
/**
|
||||
* Parse plugin or theme metadata from the header comment.
|
||||
*
|
||||
* This is basically a simplified version of the get_file_data() function from /wp-includes/functions.php.
|
||||
* It's intended as a utility for subclasses that detect updates by parsing files in a VCS.
|
||||
*
|
||||
* @param string|null $content File contents.
|
||||
* @return string[]
|
||||
*/
|
||||
public function getFileHeader($content) {
|
||||
$content = (string)$content;
|
||||
|
||||
//WordPress only looks at the first 8 KiB of the file, so we do the same.
|
||||
$content = substr($content, 0, 8192);
|
||||
//Normalize line endings.
|
||||
$content = str_replace("\r", "\n", $content);
|
||||
|
||||
$headers = $this->getHeaderNames();
|
||||
$results = array();
|
||||
foreach ($headers as $field => $name) {
|
||||
$success = preg_match('/^[ \t\/*#@]*' . preg_quote($name, '/') . ':(.*)$/mi', $content, $matches);
|
||||
|
||||
if ( ($success === 1) && $matches[1] ) {
|
||||
$value = $matches[1];
|
||||
if ( function_exists('_cleanup_header_comment') ) {
|
||||
$value = _cleanup_header_comment($value);
|
||||
}
|
||||
$results[$field] = $value;
|
||||
} else {
|
||||
$results[$field] = '';
|
||||
}
|
||||
}
|
||||
|
||||
return $results;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array Format: ['HeaderKey' => 'Header Name']
|
||||
*/
|
||||
abstract protected function getHeaderNames();
|
||||
|
||||
/**
|
||||
* Get the value of a specific plugin or theme header.
|
||||
*
|
||||
* @param string $headerName
|
||||
* @return string Either the value of the header, or an empty string if the header doesn't exist.
|
||||
*/
|
||||
abstract public function getHeaderValue($headerName);
|
||||
|
||||
}
|
||||
endif;
|
132
dependencies/yahnis-elsts/plugin-update-checker/Puc/v4p11/Metadata.php
vendored
Normal file
132
dependencies/yahnis-elsts/plugin-update-checker/Puc/v4p11/Metadata.php
vendored
Normal file
@ -0,0 +1,132 @@
|
||||
<?php
|
||||
if ( !class_exists('Puc_v4p11_Metadata', false) ):
|
||||
|
||||
/**
|
||||
* A base container for holding information about updates and plugin metadata.
|
||||
*
|
||||
* @author Janis Elsts
|
||||
* @copyright 2016
|
||||
* @access public
|
||||
*/
|
||||
abstract class Puc_v4p11_Metadata {
|
||||
|
||||
/**
|
||||
* Create an instance of this class from a JSON document.
|
||||
*
|
||||
* @abstract
|
||||
* @param string $json
|
||||
* @return self
|
||||
*/
|
||||
public static function fromJson(/** @noinspection PhpUnusedParameterInspection */ $json) {
|
||||
throw new LogicException('The ' . __METHOD__ . ' method must be implemented by subclasses');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $json
|
||||
* @param self $target
|
||||
* @return bool
|
||||
*/
|
||||
protected static function createFromJson($json, $target) {
|
||||
/** @var StdClass $apiResponse */
|
||||
$apiResponse = json_decode($json);
|
||||
if ( empty($apiResponse) || !is_object($apiResponse) ){
|
||||
$errorMessage = "Failed to parse update metadata. Try validating your .json file with http://jsonlint.com/";
|
||||
do_action('puc_api_error', new WP_Error('puc-invalid-json', $errorMessage));
|
||||
trigger_error($errorMessage, E_USER_NOTICE);
|
||||
return false;
|
||||
}
|
||||
|
||||
$valid = $target->validateMetadata($apiResponse);
|
||||
if ( is_wp_error($valid) ){
|
||||
do_action('puc_api_error', $valid);
|
||||
trigger_error($valid->get_error_message(), E_USER_NOTICE);
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach(get_object_vars($apiResponse) as $key => $value){
|
||||
$target->$key = $value;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* No validation by default! Subclasses should check that the required fields are present.
|
||||
*
|
||||
* @param StdClass $apiResponse
|
||||
* @return bool|WP_Error
|
||||
*/
|
||||
protected function validateMetadata(/** @noinspection PhpUnusedParameterInspection */ $apiResponse) {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new instance by copying the necessary fields from another object.
|
||||
*
|
||||
* @abstract
|
||||
* @param StdClass|self $object The source object.
|
||||
* @return self The new copy.
|
||||
*/
|
||||
public static function fromObject(/** @noinspection PhpUnusedParameterInspection */ $object) {
|
||||
throw new LogicException('The ' . __METHOD__ . ' method must be implemented by subclasses');
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an instance of StdClass that can later be converted back to an
|
||||
* update or info container. Useful for serialization and caching, as it
|
||||
* avoids the "incomplete object" problem if the cached value is loaded
|
||||
* before this class.
|
||||
*
|
||||
* @return StdClass
|
||||
*/
|
||||
public function toStdClass() {
|
||||
$object = new stdClass();
|
||||
$this->copyFields($this, $object);
|
||||
return $object;
|
||||
}
|
||||
|
||||
/**
|
||||
* Transform the metadata into the format used by WordPress core.
|
||||
*
|
||||
* @return object
|
||||
*/
|
||||
abstract public function toWpFormat();
|
||||
|
||||
/**
|
||||
* Copy known fields from one object to another.
|
||||
*
|
||||
* @param StdClass|self $from
|
||||
* @param StdClass|self $to
|
||||
*/
|
||||
protected function copyFields($from, $to) {
|
||||
$fields = $this->getFieldNames();
|
||||
|
||||
if ( property_exists($from, 'slug') && !empty($from->slug) ) {
|
||||
//Let plugins add extra fields without having to create subclasses.
|
||||
$fields = apply_filters($this->getPrefixedFilter('retain_fields') . '-' . $from->slug, $fields);
|
||||
}
|
||||
|
||||
foreach ($fields as $field) {
|
||||
if ( property_exists($from, $field) ) {
|
||||
$to->$field = $from->$field;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string[]
|
||||
*/
|
||||
protected function getFieldNames() {
|
||||
return array();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $tag
|
||||
* @return string
|
||||
*/
|
||||
protected function getPrefixedFilter($tag) {
|
||||
return 'puc_' . $tag;
|
||||
}
|
||||
}
|
||||
|
||||
endif;
|
100
dependencies/yahnis-elsts/plugin-update-checker/Puc/v4p11/OAuthSignature.php
vendored
Normal file
100
dependencies/yahnis-elsts/plugin-update-checker/Puc/v4p11/OAuthSignature.php
vendored
Normal file
@ -0,0 +1,100 @@
|
||||
<?php
|
||||
|
||||
if ( !class_exists('Puc_v4p11_OAuthSignature', false) ):
|
||||
|
||||
/**
|
||||
* A basic signature generator for zero-legged OAuth 1.0.
|
||||
*/
|
||||
class Puc_v4p11_OAuthSignature {
|
||||
private $consumerKey = '';
|
||||
private $consumerSecret = '';
|
||||
|
||||
public function __construct($consumerKey, $consumerSecret) {
|
||||
$this->consumerKey = $consumerKey;
|
||||
$this->consumerSecret = $consumerSecret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sign a URL using OAuth 1.0.
|
||||
*
|
||||
* @param string $url The URL to be signed. It may contain query parameters.
|
||||
* @param string $method HTTP method such as "GET", "POST" and so on.
|
||||
* @return string The signed URL.
|
||||
*/
|
||||
public function sign($url, $method = 'GET') {
|
||||
$parameters = array();
|
||||
|
||||
//Parse query parameters.
|
||||
$query = parse_url($url, PHP_URL_QUERY);
|
||||
if ( !empty($query) ) {
|
||||
parse_str($query, $parsedParams);
|
||||
if ( is_array($parameters) ) {
|
||||
$parameters = $parsedParams;
|
||||
}
|
||||
//Remove the query string from the URL. We'll replace it later.
|
||||
$url = substr($url, 0, strpos($url, '?'));
|
||||
}
|
||||
|
||||
$parameters = array_merge(
|
||||
$parameters,
|
||||
array(
|
||||
'oauth_consumer_key' => $this->consumerKey,
|
||||
'oauth_nonce' => $this->nonce(),
|
||||
'oauth_signature_method' => 'HMAC-SHA1',
|
||||
'oauth_timestamp' => time(),
|
||||
'oauth_version' => '1.0',
|
||||
)
|
||||
);
|
||||
unset($parameters['oauth_signature']);
|
||||
|
||||
//Parameters must be sorted alphabetically before signing.
|
||||
ksort($parameters);
|
||||
|
||||
//The most complicated part of the request - generating the signature.
|
||||
//The string to sign contains the HTTP method, the URL path, and all of
|
||||
//our query parameters. Everything is URL encoded. Then we concatenate
|
||||
//them with ampersands into a single string to hash.
|
||||
$encodedVerb = urlencode($method);
|
||||
$encodedUrl = urlencode($url);
|
||||
$encodedParams = urlencode(http_build_query($parameters, '', '&'));
|
||||
|
||||
$stringToSign = $encodedVerb . '&' . $encodedUrl . '&' . $encodedParams;
|
||||
|
||||
//Since we only have one OAuth token (the consumer secret) we only have
|
||||
//to use it as our HMAC key. However, we still have to append an & to it
|
||||
//as if we were using it with additional tokens.
|
||||
$secret = urlencode($this->consumerSecret) . '&';
|
||||
|
||||
//The signature is a hash of the consumer key and the base string. Note
|
||||
//that we have to get the raw output from hash_hmac and base64 encode
|
||||
//the binary data result.
|
||||
$parameters['oauth_signature'] = base64_encode(hash_hmac('sha1', $stringToSign, $secret, true));
|
||||
|
||||
return ($url . '?' . http_build_query($parameters));
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a random nonce.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function nonce() {
|
||||
$mt = microtime();
|
||||
|
||||
$rand = null;
|
||||
if ( is_callable('random_bytes') ) {
|
||||
try {
|
||||
$rand = random_bytes(16);
|
||||
} catch (Exception $ex) {
|
||||
//Fall back to mt_rand (below).
|
||||
}
|
||||
}
|
||||
if ( $rand === null ) {
|
||||
$rand = mt_rand();
|
||||
}
|
||||
|
||||
return md5($mt . '_' . $rand);
|
||||
}
|
||||
}
|
||||
|
||||
endif;
|
132
dependencies/yahnis-elsts/plugin-update-checker/Puc/v4p11/Plugin/Info.php
vendored
Normal file
132
dependencies/yahnis-elsts/plugin-update-checker/Puc/v4p11/Plugin/Info.php
vendored
Normal file
@ -0,0 +1,132 @@
|
||||
<?php
|
||||
if ( !class_exists('Puc_v4p11_Plugin_Info', false) ):
|
||||
|
||||
/**
|
||||
* A container class for holding and transforming various plugin metadata.
|
||||
*
|
||||
* @author Janis Elsts
|
||||
* @copyright 2016
|
||||
* @access public
|
||||
*/
|
||||
class Puc_v4p11_Plugin_Info extends Puc_v4p11_Metadata {
|
||||
//Most fields map directly to the contents of the plugin's info.json file.
|
||||
//See the relevant docs for a description of their meaning.
|
||||
public $name;
|
||||
public $slug;
|
||||
public $version;
|
||||
public $homepage;
|
||||
public $sections = array();
|
||||
public $download_url;
|
||||
|
||||
public $banners;
|
||||
public $icons = array();
|
||||
public $translations = array();
|
||||
|
||||
public $author;
|
||||
public $author_homepage;
|
||||
|
||||
public $requires;
|
||||
public $tested;
|
||||
public $requires_php;
|
||||
public $upgrade_notice;
|
||||
|
||||
public $rating;
|
||||
public $num_ratings;
|
||||
public $downloaded;
|
||||
public $active_installs;
|
||||
public $last_updated;
|
||||
|
||||
public $id = 0; //The native WP.org API returns numeric plugin IDs, but they're not used for anything.
|
||||
|
||||
public $filename; //Plugin filename relative to the plugins directory.
|
||||
|
||||
/**
|
||||
* Create a new instance of Plugin Info from JSON-encoded plugin info
|
||||
* returned by an external update API.
|
||||
*
|
||||
* @param string $json Valid JSON string representing plugin info.
|
||||
* @return self|null New instance of Plugin Info, or NULL on error.
|
||||
*/
|
||||
public static function fromJson($json){
|
||||
$instance = new self();
|
||||
|
||||
if ( !parent::createFromJson($json, $instance) ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
//json_decode decodes assoc. arrays as objects. We want them as arrays.
|
||||
$instance->sections = (array)$instance->sections;
|
||||
$instance->icons = (array)$instance->icons;
|
||||
|
||||
return $instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Very, very basic validation.
|
||||
*
|
||||
* @param StdClass $apiResponse
|
||||
* @return bool|WP_Error
|
||||
*/
|
||||
protected function validateMetadata($apiResponse) {
|
||||
if (
|
||||
!isset($apiResponse->name, $apiResponse->version)
|
||||
|| empty($apiResponse->name)
|
||||
|| empty($apiResponse->version)
|
||||
) {
|
||||
return new WP_Error(
|
||||
'puc-invalid-metadata',
|
||||
"The plugin metadata file does not contain the required 'name' and/or 'version' keys."
|
||||
);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Transform plugin info into the format used by the native WordPress.org API
|
||||
*
|
||||
* @return object
|
||||
*/
|
||||
public function toWpFormat(){
|
||||
$info = new stdClass;
|
||||
|
||||
//The custom update API is built so that many fields have the same name and format
|
||||
//as those returned by the native WordPress.org API. These can be assigned directly.
|
||||
$sameFormat = array(
|
||||
'name', 'slug', 'version', 'requires', 'tested', 'rating', 'upgrade_notice',
|
||||
'num_ratings', 'downloaded', 'active_installs', 'homepage', 'last_updated',
|
||||
'requires_php',
|
||||
);
|
||||
foreach($sameFormat as $field){
|
||||
if ( isset($this->$field) ) {
|
||||
$info->$field = $this->$field;
|
||||
} else {
|
||||
$info->$field = null;
|
||||
}
|
||||
}
|
||||
|
||||
//Other fields need to be renamed and/or transformed.
|
||||
$info->download_link = $this->download_url;
|
||||
$info->author = $this->getFormattedAuthor();
|
||||
$info->sections = array_merge(array('description' => ''), $this->sections);
|
||||
|
||||
if ( !empty($this->banners) ) {
|
||||
//WP expects an array with two keys: "high" and "low". Both are optional.
|
||||
//Docs: https://wordpress.org/plugins/about/faq/#banners
|
||||
$info->banners = is_object($this->banners) ? get_object_vars($this->banners) : $this->banners;
|
||||
$info->banners = array_intersect_key($info->banners, array('high' => true, 'low' => true));
|
||||
}
|
||||
|
||||
return $info;
|
||||
}
|
||||
|
||||
protected function getFormattedAuthor() {
|
||||
if ( !empty($this->author_homepage) ){
|
||||
/** @noinspection HtmlUnknownTarget */
|
||||
return sprintf('<a href="%s">%s</a>', $this->author_homepage, $this->author);
|
||||
}
|
||||
return $this->author;
|
||||
}
|
||||
}
|
||||
|
||||
endif;
|
184
dependencies/yahnis-elsts/plugin-update-checker/Puc/v4p11/Plugin/Package.php
vendored
Normal file
184
dependencies/yahnis-elsts/plugin-update-checker/Puc/v4p11/Plugin/Package.php
vendored
Normal file
@ -0,0 +1,184 @@
|
||||
<?php
|
||||
if ( !class_exists('Puc_v4p11_Plugin_Package', false) ):
|
||||
|
||||
class Puc_v4p11_Plugin_Package extends Puc_v4p11_InstalledPackage {
|
||||
/**
|
||||
* @var Puc_v4p11_Plugin_UpdateChecker
|
||||
*/
|
||||
protected $updateChecker;
|
||||
|
||||
/**
|
||||
* @var string Full path of the main plugin file.
|
||||
*/
|
||||
protected $pluginAbsolutePath = '';
|
||||
|
||||
/**
|
||||
* @var string Plugin basename.
|
||||
*/
|
||||
private $pluginFile;
|
||||
|
||||
/**
|
||||
* @var string|null
|
||||
*/
|
||||
private $cachedInstalledVersion = null;
|
||||
|
||||
public function __construct($pluginAbsolutePath, $updateChecker) {
|
||||
$this->pluginAbsolutePath = $pluginAbsolutePath;
|
||||
$this->pluginFile = plugin_basename($this->pluginAbsolutePath);
|
||||
|
||||
parent::__construct($updateChecker);
|
||||
|
||||
//Clear the version number cache when something - anything - is upgraded or WP clears the update cache.
|
||||
add_filter('upgrader_post_install', array($this, 'clearCachedVersion'));
|
||||
add_action('delete_site_transient_update_plugins', array($this, 'clearCachedVersion'));
|
||||
}
|
||||
|
||||
public function getInstalledVersion() {
|
||||
if ( isset($this->cachedInstalledVersion) ) {
|
||||
return $this->cachedInstalledVersion;
|
||||
}
|
||||
|
||||
$pluginHeader = $this->getPluginHeader();
|
||||
if ( isset($pluginHeader['Version']) ) {
|
||||
$this->cachedInstalledVersion = $pluginHeader['Version'];
|
||||
return $pluginHeader['Version'];
|
||||
} else {
|
||||
//This can happen if the filename points to something that is not a plugin.
|
||||
$this->updateChecker->triggerError(
|
||||
sprintf(
|
||||
"Can't to read the Version header for '%s'. The filename is incorrect or is not a plugin.",
|
||||
$this->updateChecker->pluginFile
|
||||
),
|
||||
E_USER_WARNING
|
||||
);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear the cached plugin version. This method can be set up as a filter (hook) and will
|
||||
* return the filter argument unmodified.
|
||||
*
|
||||
* @param mixed $filterArgument
|
||||
* @return mixed
|
||||
*/
|
||||
public function clearCachedVersion($filterArgument = null) {
|
||||
$this->cachedInstalledVersion = null;
|
||||
return $filterArgument;
|
||||
}
|
||||
|
||||
public function getAbsoluteDirectoryPath() {
|
||||
return dirname($this->pluginAbsolutePath);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the value of a specific plugin or theme header.
|
||||
*
|
||||
* @param string $headerName
|
||||
* @param string $defaultValue
|
||||
* @return string Either the value of the header, or $defaultValue if the header doesn't exist or is empty.
|
||||
*/
|
||||
public function getHeaderValue($headerName, $defaultValue = '') {
|
||||
$headers = $this->getPluginHeader();
|
||||
if ( isset($headers[$headerName]) && ($headers[$headerName] !== '') ) {
|
||||
return $headers[$headerName];
|
||||
}
|
||||
return $defaultValue;
|
||||
}
|
||||
|
||||
protected function getHeaderNames() {
|
||||
return array(
|
||||
'Name' => 'Plugin Name',
|
||||
'PluginURI' => 'Plugin URI',
|
||||
'Version' => 'Version',
|
||||
'Description' => 'Description',
|
||||
'Author' => 'Author',
|
||||
'AuthorURI' => 'Author URI',
|
||||
'TextDomain' => 'Text Domain',
|
||||
'DomainPath' => 'Domain Path',
|
||||
'Network' => 'Network',
|
||||
|
||||
//The newest WordPress version that this plugin requires or has been tested with.
|
||||
//We support several different formats for compatibility with other libraries.
|
||||
'Tested WP' => 'Tested WP',
|
||||
'Requires WP' => 'Requires WP',
|
||||
'Tested up to' => 'Tested up to',
|
||||
'Requires at least' => 'Requires at least',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the translated plugin title.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getPluginTitle() {
|
||||
$title = '';
|
||||
$header = $this->getPluginHeader();
|
||||
if ( $header && !empty($header['Name']) && isset($header['TextDomain']) ) {
|
||||
$title = translate($header['Name'], $header['TextDomain']);
|
||||
}
|
||||
return $title;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get plugin's metadata from its file header.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getPluginHeader() {
|
||||
if ( !is_file($this->pluginAbsolutePath) ) {
|
||||
//This can happen if the plugin filename is wrong.
|
||||
$this->updateChecker->triggerError(
|
||||
sprintf(
|
||||
"Can't to read the plugin header for '%s'. The file does not exist.",
|
||||
$this->updateChecker->pluginFile
|
||||
),
|
||||
E_USER_WARNING
|
||||
);
|
||||
return array();
|
||||
}
|
||||
|
||||
if ( !function_exists('get_plugin_data') ) {
|
||||
/** @noinspection PhpIncludeInspection */
|
||||
require_once(ABSPATH . '/wp-admin/includes/plugin.php');
|
||||
}
|
||||
return get_plugin_data($this->pluginAbsolutePath, false, false);
|
||||
}
|
||||
|
||||
public function removeHooks() {
|
||||
remove_filter('upgrader_post_install', array($this, 'clearCachedVersion'));
|
||||
remove_action('delete_site_transient_update_plugins', array($this, 'clearCachedVersion'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the plugin file is inside the mu-plugins directory.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isMuPlugin() {
|
||||
static $cachedResult = null;
|
||||
|
||||
if ( $cachedResult === null ) {
|
||||
if ( !defined('WPMU_PLUGIN_DIR') || !is_string(WPMU_PLUGIN_DIR) ) {
|
||||
$cachedResult = false;
|
||||
return $cachedResult;
|
||||
}
|
||||
|
||||
//Convert both paths to the canonical form before comparison.
|
||||
$muPluginDir = realpath(WPMU_PLUGIN_DIR);
|
||||
$pluginPath = realpath($this->pluginAbsolutePath);
|
||||
//If realpath() fails, just normalize the syntax instead.
|
||||
if (($muPluginDir === false) || ($pluginPath === false)) {
|
||||
$muPluginDir = Puc_v4p11_Factory::normalizePath(WPMU_PLUGIN_DIR);
|
||||
$pluginPath = Puc_v4p11_Factory::normalizePath($this->pluginAbsolutePath);
|
||||
}
|
||||
|
||||
$cachedResult = (strpos($pluginPath, $muPluginDir) === 0);
|
||||
}
|
||||
|
||||
return $cachedResult;
|
||||
}
|
||||
}
|
||||
|
||||
endif;
|
278
dependencies/yahnis-elsts/plugin-update-checker/Puc/v4p11/Plugin/Ui.php
vendored
Normal file
278
dependencies/yahnis-elsts/plugin-update-checker/Puc/v4p11/Plugin/Ui.php
vendored
Normal file
@ -0,0 +1,278 @@
|
||||
<?php
|
||||
if ( !class_exists('Puc_v4p11_Plugin_Ui', false) ):
|
||||
/**
|
||||
* Additional UI elements for plugins.
|
||||
*/
|
||||
class Puc_v4p11_Plugin_Ui {
|
||||
private $updateChecker;
|
||||
private $manualCheckErrorTransient = '';
|
||||
|
||||
/**
|
||||
* @param Puc_v4p11_Plugin_UpdateChecker $updateChecker
|
||||
*/
|
||||
public function __construct($updateChecker) {
|
||||
$this->updateChecker = $updateChecker;
|
||||
$this->manualCheckErrorTransient = $this->updateChecker->getUniqueName('manual_check_errors');
|
||||
|
||||
add_action('admin_init', array($this, 'onAdminInit'));
|
||||
}
|
||||
|
||||
public function onAdminInit() {
|
||||
if ( $this->updateChecker->userCanInstallUpdates() ) {
|
||||
$this->handleManualCheck();
|
||||
|
||||
add_filter('plugin_row_meta', array($this, 'addViewDetailsLink'), 10, 3);
|
||||
add_filter('plugin_row_meta', array($this, 'addCheckForUpdatesLink'), 10, 2);
|
||||
add_action('all_admin_notices', array($this, 'displayManualCheckResult'));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a "View Details" link to the plugin row in the "Plugins" page. By default,
|
||||
* the new link will appear before the "Visit plugin site" link (if present).
|
||||
*
|
||||
* You can change the link text by using the "puc_view_details_link-$slug" filter.
|
||||
* Returning an empty string from the filter will disable the link.
|
||||
*
|
||||
* You can change the position of the link using the
|
||||
* "puc_view_details_link_position-$slug" filter.
|
||||
* Returning 'before' or 'after' will place the link immediately before/after
|
||||
* the "Visit plugin site" link.
|
||||
* Returning 'append' places the link after any existing links at the time of the hook.
|
||||
* Returning 'replace' replaces the "Visit plugin site" link.
|
||||
* Returning anything else disables the link when there is a "Visit plugin site" link.
|
||||
*
|
||||
* If there is no "Visit plugin site" link 'append' is always used!
|
||||
*
|
||||
* @param array $pluginMeta Array of meta links.
|
||||
* @param string $pluginFile
|
||||
* @param array $pluginData Array of plugin header data.
|
||||
* @return array
|
||||
*/
|
||||
public function addViewDetailsLink($pluginMeta, $pluginFile, $pluginData = array()) {
|
||||
if ( $this->isMyPluginFile($pluginFile) && !isset($pluginData['slug']) ) {
|
||||
$linkText = apply_filters($this->updateChecker->getUniqueName('view_details_link'), __('View details'));
|
||||
if ( !empty($linkText) ) {
|
||||
$viewDetailsLinkPosition = 'append';
|
||||
|
||||
//Find the "Visit plugin site" link (if present).
|
||||
$visitPluginSiteLinkIndex = count($pluginMeta) - 1;
|
||||
if ( $pluginData['PluginURI'] ) {
|
||||
$escapedPluginUri = esc_url($pluginData['PluginURI']);
|
||||
foreach ($pluginMeta as $linkIndex => $existingLink) {
|
||||
if ( strpos($existingLink, $escapedPluginUri) !== false ) {
|
||||
$visitPluginSiteLinkIndex = $linkIndex;
|
||||
$viewDetailsLinkPosition = apply_filters(
|
||||
$this->updateChecker->getUniqueName('view_details_link_position'),
|
||||
'before'
|
||||
);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$viewDetailsLink = sprintf('<a href="%s" class="thickbox open-plugin-details-modal" aria-label="%s" data-title="%s">%s</a>',
|
||||
esc_url(network_admin_url('plugin-install.php?tab=plugin-information&plugin=' . urlencode($this->updateChecker->slug) .
|
||||
'&TB_iframe=true&width=600&height=550')),
|
||||
esc_attr(sprintf(__('More information about %s'), $pluginData['Name'])),
|
||||
esc_attr($pluginData['Name']),
|
||||
$linkText
|
||||
);
|
||||
switch ($viewDetailsLinkPosition) {
|
||||
case 'before':
|
||||
array_splice($pluginMeta, $visitPluginSiteLinkIndex, 0, $viewDetailsLink);
|
||||
break;
|
||||
case 'after':
|
||||
array_splice($pluginMeta, $visitPluginSiteLinkIndex + 1, 0, $viewDetailsLink);
|
||||
break;
|
||||
case 'replace':
|
||||
$pluginMeta[$visitPluginSiteLinkIndex] = $viewDetailsLink;
|
||||
break;
|
||||
case 'append':
|
||||
default:
|
||||
$pluginMeta[] = $viewDetailsLink;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return $pluginMeta;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a "Check for updates" link to the plugin row in the "Plugins" page. By default,
|
||||
* the new link will appear after the "Visit plugin site" link if present, otherwise
|
||||
* after the "View plugin details" link.
|
||||
*
|
||||
* You can change the link text by using the "puc_manual_check_link-$slug" filter.
|
||||
* Returning an empty string from the filter will disable the link.
|
||||
*
|
||||
* @param array $pluginMeta Array of meta links.
|
||||
* @param string $pluginFile
|
||||
* @return array
|
||||
*/
|
||||
public function addCheckForUpdatesLink($pluginMeta, $pluginFile) {
|
||||
if ( $this->isMyPluginFile($pluginFile) ) {
|
||||
$linkUrl = wp_nonce_url(
|
||||
add_query_arg(
|
||||
array(
|
||||
'puc_check_for_updates' => 1,
|
||||
'puc_slug' => $this->updateChecker->slug,
|
||||
),
|
||||
self_admin_url('plugins.php')
|
||||
),
|
||||
'puc_check_for_updates'
|
||||
);
|
||||
|
||||
$linkText = apply_filters(
|
||||
$this->updateChecker->getUniqueName('manual_check_link'),
|
||||
__('Check for updates', 'plugin-update-checker')
|
||||
);
|
||||
if ( !empty($linkText) ) {
|
||||
/** @noinspection HtmlUnknownTarget */
|
||||
$pluginMeta[] = sprintf('<a href="%s">%s</a>', esc_attr($linkUrl), $linkText);
|
||||
}
|
||||
}
|
||||
return $pluginMeta;
|
||||
}
|
||||
|
||||
protected function isMyPluginFile($pluginFile) {
|
||||
return ($pluginFile == $this->updateChecker->pluginFile)
|
||||
|| (!empty($this->updateChecker->muPluginFile) && ($pluginFile == $this->updateChecker->muPluginFile));
|
||||
}
|
||||
|
||||
/**
|
||||
* Check for updates when the user clicks the "Check for updates" link.
|
||||
*
|
||||
* @see self::addCheckForUpdatesLink()
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function handleManualCheck() {
|
||||
$shouldCheck =
|
||||
isset($_GET['puc_check_for_updates'], $_GET['puc_slug'])
|
||||
&& $_GET['puc_slug'] == $this->updateChecker->slug
|
||||
&& check_admin_referer('puc_check_for_updates');
|
||||
|
||||
if ( $shouldCheck ) {
|
||||
$update = $this->updateChecker->checkForUpdates();
|
||||
$status = ($update === null) ? 'no_update' : 'update_available';
|
||||
$lastRequestApiErrors = $this->updateChecker->getLastRequestApiErrors();
|
||||
|
||||
if ( ($update === null) && !empty($lastRequestApiErrors) ) {
|
||||
//Some errors are not critical. For example, if PUC tries to retrieve the readme.txt
|
||||
//file from GitHub and gets a 404, that's an API error, but it doesn't prevent updates
|
||||
//from working. Maybe the plugin simply doesn't have a readme.
|
||||
//Let's only show important errors.
|
||||
$foundCriticalErrors = false;
|
||||
$questionableErrorCodes = array(
|
||||
'puc-github-http-error',
|
||||
'puc-gitlab-http-error',
|
||||
'puc-bitbucket-http-error',
|
||||
);
|
||||
|
||||
foreach ($lastRequestApiErrors as $item) {
|
||||
$wpError = $item['error'];
|
||||
/** @var WP_Error $wpError */
|
||||
if ( !in_array($wpError->get_error_code(), $questionableErrorCodes) ) {
|
||||
$foundCriticalErrors = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ( $foundCriticalErrors ) {
|
||||
$status = 'error';
|
||||
set_site_transient($this->manualCheckErrorTransient, $lastRequestApiErrors, 60);
|
||||
}
|
||||
}
|
||||
|
||||
wp_redirect(add_query_arg(
|
||||
array(
|
||||
'puc_update_check_result' => $status,
|
||||
'puc_slug' => $this->updateChecker->slug,
|
||||
),
|
||||
self_admin_url('plugins.php')
|
||||
));
|
||||
exit;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Display the results of a manual update check.
|
||||
*
|
||||
* @see self::handleManualCheck()
|
||||
*
|
||||
* You can change the result message by using the "puc_manual_check_message-$slug" filter.
|
||||
*/
|
||||
public function displayManualCheckResult() {
|
||||
if ( isset($_GET['puc_update_check_result'], $_GET['puc_slug']) && ($_GET['puc_slug'] == $this->updateChecker->slug) ) {
|
||||
$status = strval($_GET['puc_update_check_result']);
|
||||
$title = $this->updateChecker->getInstalledPackage()->getPluginTitle();
|
||||
$noticeClass = 'updated notice-success';
|
||||
$details = '';
|
||||
|
||||
if ( $status == 'no_update' ) {
|
||||
$message = sprintf(_x('The %s plugin is up to date.', 'the plugin title', 'plugin-update-checker'), $title);
|
||||
} else if ( $status == 'update_available' ) {
|
||||
$message = sprintf(_x('A new version of the %s plugin is available.', 'the plugin title', 'plugin-update-checker'), $title);
|
||||
} else if ( $status === 'error' ) {
|
||||
$message = sprintf(_x('Could not determine if updates are available for %s.', 'the plugin title', 'plugin-update-checker'), $title);
|
||||
$noticeClass = 'error notice-error';
|
||||
|
||||
$details = $this->formatManualCheckErrors(get_site_transient($this->manualCheckErrorTransient));
|
||||
delete_site_transient($this->manualCheckErrorTransient);
|
||||
} else {
|
||||
$message = sprintf(__('Unknown update checker status "%s"', 'plugin-update-checker'), htmlentities($status));
|
||||
$noticeClass = 'error notice-error';
|
||||
}
|
||||
printf(
|
||||
'<div class="notice %s is-dismissible"><p>%s</p>%s</div>',
|
||||
$noticeClass,
|
||||
apply_filters($this->updateChecker->getUniqueName('manual_check_message'), $message, $status),
|
||||
$details
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Format the list of errors that were thrown during an update check.
|
||||
*
|
||||
* @param array $errors
|
||||
* @return string
|
||||
*/
|
||||
protected function formatManualCheckErrors($errors) {
|
||||
if ( empty($errors) ) {
|
||||
return '';
|
||||
}
|
||||
$output = '';
|
||||
|
||||
$showAsList = count($errors) > 1;
|
||||
if ( $showAsList ) {
|
||||
$output .= '<ol>';
|
||||
$formatString = '<li>%1$s <code>%2$s</code></li>';
|
||||
} else {
|
||||
$formatString = '<p>%1$s <code>%2$s</code></p>';
|
||||
}
|
||||
foreach ($errors as $item) {
|
||||
$wpError = $item['error'];
|
||||
/** @var WP_Error $wpError */
|
||||
$output .= sprintf(
|
||||
$formatString,
|
||||
$wpError->get_error_message(),
|
||||
$wpError->get_error_code()
|
||||
);
|
||||
}
|
||||
if ( $showAsList ) {
|
||||
$output .= '</ol>';
|
||||
}
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
public function removeHooks() {
|
||||
remove_action('admin_init', array($this, 'onAdminInit'));
|
||||
remove_filter('plugin_row_meta', array($this, 'addViewDetailsLink'), 10);
|
||||
remove_filter('plugin_row_meta', array($this, 'addCheckForUpdatesLink'), 10);
|
||||
remove_action('all_admin_notices', array($this, 'displayManualCheckResult'));
|
||||
}
|
||||
}
|
||||
endif;
|
112
dependencies/yahnis-elsts/plugin-update-checker/Puc/v4p11/Plugin/Update.php
vendored
Normal file
112
dependencies/yahnis-elsts/plugin-update-checker/Puc/v4p11/Plugin/Update.php
vendored
Normal file
@ -0,0 +1,112 @@
|
||||
<?php
|
||||
if ( !class_exists('Puc_v4p11_Plugin_Update', false) ):
|
||||
|
||||
/**
|
||||
* A simple container class for holding information about an available update.
|
||||
*
|
||||
* @author Janis Elsts
|
||||
* @copyright 2016
|
||||
* @access public
|
||||
*/
|
||||
class Puc_v4p11_Plugin_Update extends Puc_v4p11_Update {
|
||||
public $id = 0;
|
||||
public $homepage;
|
||||
public $upgrade_notice;
|
||||
public $tested;
|
||||
public $requires_php = false;
|
||||
public $icons = array();
|
||||
public $filename; //Plugin filename relative to the plugins directory.
|
||||
|
||||
protected static $extraFields = array(
|
||||
'id', 'homepage', 'tested', 'requires_php', 'upgrade_notice', 'icons', 'filename',
|
||||
);
|
||||
|
||||
/**
|
||||
* Create a new instance of PluginUpdate from its JSON-encoded representation.
|
||||
*
|
||||
* @param string $json
|
||||
* @return Puc_v4p11_Plugin_Update|null
|
||||
*/
|
||||
public static function fromJson($json){
|
||||
//Since update-related information is simply a subset of the full plugin info,
|
||||
//we can parse the update JSON as if it was a plugin info string, then copy over
|
||||
//the parts that we care about.
|
||||
$pluginInfo = Puc_v4p11_Plugin_Info::fromJson($json);
|
||||
if ( $pluginInfo !== null ) {
|
||||
return self::fromPluginInfo($pluginInfo);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new instance of PluginUpdate based on an instance of PluginInfo.
|
||||
* Basically, this just copies a subset of fields from one object to another.
|
||||
*
|
||||
* @param Puc_v4p11_Plugin_Info $info
|
||||
* @return Puc_v4p11_Plugin_Update
|
||||
*/
|
||||
public static function fromPluginInfo($info){
|
||||
return self::fromObject($info);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new instance by copying the necessary fields from another object.
|
||||
*
|
||||
* @param StdClass|Puc_v4p11_Plugin_Info|Puc_v4p11_Plugin_Update $object The source object.
|
||||
* @return Puc_v4p11_Plugin_Update The new copy.
|
||||
*/
|
||||
public static function fromObject($object) {
|
||||
$update = new self();
|
||||
$update->copyFields($object, $update);
|
||||
return $update;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string[]
|
||||
*/
|
||||
protected function getFieldNames() {
|
||||
return array_merge(parent::getFieldNames(), self::$extraFields);
|
||||
}
|
||||
|
||||
/**
|
||||
* Transform the update into the format used by WordPress native plugin API.
|
||||
*
|
||||
* @return object
|
||||
*/
|
||||
public function toWpFormat() {
|
||||
$update = parent::toWpFormat();
|
||||
|
||||
$update->id = $this->id;
|
||||
$update->url = $this->homepage;
|
||||
$update->tested = $this->tested;
|
||||
$update->requires_php = $this->requires_php;
|
||||
$update->plugin = $this->filename;
|
||||
|
||||
if ( !empty($this->upgrade_notice) ) {
|
||||
$update->upgrade_notice = $this->upgrade_notice;
|
||||
}
|
||||
|
||||
if ( !empty($this->icons) && is_array($this->icons) ) {
|
||||
//This should be an array with up to 4 keys: 'svg', '1x', '2x' and 'default'.
|
||||
//Docs: https://developer.wordpress.org/plugins/wordpress-org/plugin-assets/#plugin-icons
|
||||
$icons = array_intersect_key(
|
||||
$this->icons,
|
||||
array('svg' => true, '1x' => true, '2x' => true, 'default' => true)
|
||||
);
|
||||
if ( !empty($icons) ) {
|
||||
$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.
|
||||
if ( !isset($update->icons['default']) ) {
|
||||
$update->icons['default'] = current($update->icons);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $update;
|
||||
}
|
||||
}
|
||||
|
||||
endif;
|
414
dependencies/yahnis-elsts/plugin-update-checker/Puc/v4p11/Plugin/UpdateChecker.php
vendored
Normal file
414
dependencies/yahnis-elsts/plugin-update-checker/Puc/v4p11/Plugin/UpdateChecker.php
vendored
Normal file
@ -0,0 +1,414 @@
|
||||
<?php
|
||||
if ( !class_exists('Puc_v4p11_Plugin_UpdateChecker', false) ):
|
||||
|
||||
/**
|
||||
* A custom plugin update checker.
|
||||
*
|
||||
* @author Janis Elsts
|
||||
* @copyright 2018
|
||||
* @access public
|
||||
*/
|
||||
class Puc_v4p11_Plugin_UpdateChecker extends Puc_v4p11_UpdateChecker {
|
||||
protected $updateTransient = 'update_plugins';
|
||||
protected $translationType = '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.
|
||||
public $muPluginFile = ''; //For MU plugins, the plugin filename relative to the mu-plugins directory.
|
||||
|
||||
/**
|
||||
* @var Puc_v4p11_Plugin_Package
|
||||
*/
|
||||
protected $package;
|
||||
|
||||
private $extraUi = null;
|
||||
|
||||
/**
|
||||
* Class constructor.
|
||||
*
|
||||
* @param string $metadataUrl The URL of the plugin's metadata file.
|
||||
* @param string $pluginFile Fully qualified path to the main plugin file.
|
||||
* @param string $slug The plugin's 'slug'. If not specified, the filename part of $pluginFile sans '.php' will be used as the slug.
|
||||
* @param integer $checkPeriod How often to check for updates (in hours). Defaults to checking every 12 hours. Set to 0 to disable automatic update checks.
|
||||
* @param string $optionName Where to store book-keeping info about update checks. Defaults to 'external_updates-$slug'.
|
||||
* @param string $muPluginFile Optional. The plugin filename relative to the mu-plugins directory.
|
||||
*/
|
||||
public function __construct($metadataUrl, $pluginFile, $slug = '', $checkPeriod = 12, $optionName = '', $muPluginFile = ''){
|
||||
$this->pluginAbsolutePath = $pluginFile;
|
||||
$this->pluginFile = plugin_basename($this->pluginAbsolutePath);
|
||||
$this->muPluginFile = $muPluginFile;
|
||||
|
||||
//If no slug is specified, use the name of the main plugin file as the slug.
|
||||
//For example, 'my-cool-plugin/cool-plugin.php' becomes 'cool-plugin'.
|
||||
if ( empty($slug) ){
|
||||
$slug = basename($this->pluginFile, '.php');
|
||||
}
|
||||
|
||||
//Plugin slugs must be unique.
|
||||
$slugCheckFilter = 'puc_is_slug_in_use-' . $slug;
|
||||
$slugUsedBy = apply_filters($slugCheckFilter, false);
|
||||
if ( $slugUsedBy ) {
|
||||
$this->triggerError(sprintf(
|
||||
'Plugin slug "%s" is already in use by %s. Slugs must be unique.',
|
||||
htmlentities($slug),
|
||||
htmlentities($slugUsedBy)
|
||||
), E_USER_ERROR);
|
||||
}
|
||||
add_filter($slugCheckFilter, array($this, 'getAbsolutePath'));
|
||||
|
||||
parent::__construct($metadataUrl, dirname($this->pluginFile), $slug, $checkPeriod, $optionName);
|
||||
|
||||
//Backwards compatibility: If the plugin is a mu-plugin but no $muPluginFile is specified, assume
|
||||
//it's the same as $pluginFile given that it's not in a subdirectory (WP only looks in the base dir).
|
||||
if ( (strpbrk($this->pluginFile, '/\\') === false) && $this->isUnknownMuPlugin() ) {
|
||||
$this->muPluginFile = $this->pluginFile;
|
||||
}
|
||||
|
||||
//To prevent a crash during plugin uninstallation, remove updater hooks when the user removes the plugin.
|
||||
//Details: https://github.com/YahnisElsts/plugin-update-checker/issues/138#issuecomment-335590964
|
||||
add_action('uninstall_' . $this->pluginFile, array($this, 'removeHooks'));
|
||||
|
||||
$this->extraUi = new Puc_v4p11_Plugin_Ui($this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an instance of the scheduler.
|
||||
*
|
||||
* @param int $checkPeriod
|
||||
* @return Puc_v4p11_Scheduler
|
||||
*/
|
||||
protected function createScheduler($checkPeriod) {
|
||||
$scheduler = new Puc_v4p11_Scheduler($this, $checkPeriod, array('load-plugins.php'));
|
||||
register_deactivation_hook($this->pluginFile, array($scheduler, 'removeUpdaterCron'));
|
||||
return $scheduler;
|
||||
}
|
||||
|
||||
/**
|
||||
* Install the hooks required to run periodic update checks and inject update info
|
||||
* into WP data structures.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function installHooks(){
|
||||
//Override requests for plugin information
|
||||
add_filter('plugins_api', array($this, 'injectInfo'), 20, 3);
|
||||
|
||||
parent::installHooks();
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove update checker hooks.
|
||||
*
|
||||
* The intent is to prevent a fatal error that can happen if the plugin has an uninstall
|
||||
* hook. During uninstallation, WP includes the main plugin file (which creates a PUC instance),
|
||||
* the uninstall hook runs, WP deletes the plugin files and then updates some transients.
|
||||
* If PUC hooks are still around at this time, they could throw an error while trying to
|
||||
* autoload classes from files that no longer exist.
|
||||
*
|
||||
* The "site_transient_{$transient}" filter is the main problem here, but let's also remove
|
||||
* most other PUC hooks to be safe.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
public function removeHooks() {
|
||||
parent::removeHooks();
|
||||
$this->extraUi->removeHooks();
|
||||
$this->package->removeHooks();
|
||||
|
||||
remove_filter('plugins_api', array($this, 'injectInfo'), 20);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve plugin info from the configured API endpoint.
|
||||
*
|
||||
* @uses wp_remote_get()
|
||||
*
|
||||
* @param array $queryArgs Additional query arguments to append to the request. Optional.
|
||||
* @return Puc_v4p11_Plugin_Info
|
||||
*/
|
||||
public function requestInfo($queryArgs = array()) {
|
||||
list($pluginInfo, $result) = $this->requestMetadata('Puc_v4p11_Plugin_Info', 'request_info', $queryArgs);
|
||||
|
||||
if ( $pluginInfo !== null ) {
|
||||
/** @var Puc_v4p11_Plugin_Info $pluginInfo */
|
||||
$pluginInfo->filename = $this->pluginFile;
|
||||
$pluginInfo->slug = $this->slug;
|
||||
}
|
||||
|
||||
$pluginInfo = apply_filters($this->getUniqueName('request_info_result'), $pluginInfo, $result);
|
||||
return $pluginInfo;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the latest update (if any) from the configured API endpoint.
|
||||
*
|
||||
* @uses PluginUpdateChecker::requestInfo()
|
||||
*
|
||||
* @return Puc_v4p11_Update|null An instance of Plugin_Update, or NULL when no updates are available.
|
||||
*/
|
||||
public function requestUpdate() {
|
||||
//For the sake of simplicity, this function just calls requestInfo()
|
||||
//and transforms the result accordingly.
|
||||
$pluginInfo = $this->requestInfo(array('checking_for_updates' => '1'));
|
||||
if ( $pluginInfo === null ){
|
||||
return null;
|
||||
}
|
||||
$update = Puc_v4p11_Plugin_Update::fromPluginInfo($pluginInfo);
|
||||
|
||||
$update = $this->filterUpdateResult($update);
|
||||
|
||||
return $update;
|
||||
}
|
||||
|
||||
/**
|
||||
* Intercept plugins_api() calls that request information about our plugin and
|
||||
* use the configured API endpoint to satisfy them.
|
||||
*
|
||||
* @see plugins_api()
|
||||
*
|
||||
* @param mixed $result
|
||||
* @param string $action
|
||||
* @param array|object $args
|
||||
* @return mixed
|
||||
*/
|
||||
public function injectInfo($result, $action = null, $args = null){
|
||||
$relevant = ($action == 'plugin_information') && isset($args->slug) && (
|
||||
($args->slug == $this->slug) || ($args->slug == dirname($this->pluginFile))
|
||||
);
|
||||
if ( !$relevant ) {
|
||||
return $result;
|
||||
}
|
||||
|
||||
$pluginInfo = $this->requestInfo();
|
||||
$this->fixSupportedWordpressVersion($pluginInfo);
|
||||
|
||||
$pluginInfo = apply_filters($this->getUniqueName('pre_inject_info'), $pluginInfo);
|
||||
if ( $pluginInfo ) {
|
||||
return $pluginInfo->toWpFormat();
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
protected function shouldShowUpdates() {
|
||||
//No update notifications for mu-plugins unless explicitly enabled. The MU plugin file
|
||||
//is usually different from the main plugin file so the update wouldn't show up properly anyway.
|
||||
return !$this->isUnknownMuPlugin();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param stdClass|null $updates
|
||||
* @param stdClass $updateToAdd
|
||||
* @return stdClass
|
||||
*/
|
||||
protected function addUpdateToList($updates, $updateToAdd) {
|
||||
if ( $this->package->isMuPlugin() ) {
|
||||
//WP does not support automatic update installation for mu-plugins, but we can
|
||||
//still display a notice.
|
||||
$updateToAdd->package = null;
|
||||
}
|
||||
return parent::addUpdateToList($updates, $updateToAdd);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param stdClass|null $updates
|
||||
* @return stdClass|null
|
||||
*/
|
||||
protected function removeUpdateFromList($updates) {
|
||||
$updates = parent::removeUpdateFromList($updates);
|
||||
if ( !empty($this->muPluginFile) && isset($updates, $updates->response) ) {
|
||||
unset($updates->response[$this->muPluginFile]);
|
||||
}
|
||||
return $updates;
|
||||
}
|
||||
|
||||
/**
|
||||
* For plugins, the update array is indexed by the plugin filename relative to the "plugins"
|
||||
* directory. Example: "plugin-name/plugin.php".
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getUpdateListKey() {
|
||||
if ( $this->package->isMuPlugin() ) {
|
||||
return $this->muPluginFile;
|
||||
}
|
||||
return $this->pluginFile;
|
||||
}
|
||||
|
||||
protected function getNoUpdateItemFields() {
|
||||
return array_merge(
|
||||
parent::getNoUpdateItemFields(),
|
||||
array(
|
||||
'id' => $this->pluginFile,
|
||||
'slug' => $this->slug,
|
||||
'plugin' => $this->pluginFile,
|
||||
'icons' => array(),
|
||||
'banners' => array(),
|
||||
'banners_rtl' => array(),
|
||||
'tested' => '',
|
||||
'compatibility' => new stdClass(),
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Alias for isBeingUpgraded().
|
||||
*
|
||||
* @deprecated
|
||||
* @param WP_Upgrader|null $upgrader The upgrader that's performing the current update.
|
||||
* @return bool
|
||||
*/
|
||||
public function isPluginBeingUpgraded($upgrader = null) {
|
||||
return $this->isBeingUpgraded($upgrader);
|
||||
}
|
||||
|
||||
/**
|
||||
* Is there an update being installed for this plugin, right now?
|
||||
*
|
||||
* @param WP_Upgrader|null $upgrader
|
||||
* @return bool
|
||||
*/
|
||||
public function isBeingUpgraded($upgrader = null) {
|
||||
return $this->upgraderStatus->isPluginBeingUpgraded($this->pluginFile, $upgrader);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the details of the currently available update, if any.
|
||||
*
|
||||
* If no updates are available, or if the last known update version is below or equal
|
||||
* to the currently installed version, this method will return NULL.
|
||||
*
|
||||
* Uses cached update data. To retrieve update information straight from
|
||||
* the metadata URL, call requestUpdate() instead.
|
||||
*
|
||||
* @return Puc_v4p11_Plugin_Update|null
|
||||
*/
|
||||
public function getUpdate() {
|
||||
$update = parent::getUpdate();
|
||||
if ( isset($update) ) {
|
||||
/** @var Puc_v4p11_Plugin_Update $update */
|
||||
$update->filename = $this->pluginFile;
|
||||
}
|
||||
return $update;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the translated plugin title.
|
||||
*
|
||||
* @deprecated
|
||||
* @return string
|
||||
*/
|
||||
public function getPluginTitle() {
|
||||
return $this->package->getPluginTitle();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the current user has the required permissions to install updates.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function userCanInstallUpdates() {
|
||||
return current_user_can('update_plugins');
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the plugin file is inside the mu-plugins directory.
|
||||
*
|
||||
* @deprecated
|
||||
* @return bool
|
||||
*/
|
||||
protected function isMuPlugin() {
|
||||
return $this->package->isMuPlugin();
|
||||
}
|
||||
|
||||
/**
|
||||
* MU plugins are partially supported, but only when we know which file in mu-plugins
|
||||
* corresponds to this plugin.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function isUnknownMuPlugin() {
|
||||
return empty($this->muPluginFile) && $this->package->isMuPlugin();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get absolute path to the main plugin file.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getAbsolutePath() {
|
||||
return $this->pluginAbsolutePath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a callback for filtering query arguments.
|
||||
*
|
||||
* The callback function should take one argument - an associative array of query arguments.
|
||||
* It should return a modified array of query arguments.
|
||||
*
|
||||
* @uses add_filter() This method is a convenience wrapper for add_filter().
|
||||
*
|
||||
* @param callable $callback
|
||||
* @return void
|
||||
*/
|
||||
public function addQueryArgFilter($callback){
|
||||
$this->addFilter('request_info_query_args', $callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a callback for filtering arguments passed to wp_remote_get().
|
||||
*
|
||||
* The callback function should take one argument - an associative array of arguments -
|
||||
* and return a modified array or arguments. See the WP documentation on wp_remote_get()
|
||||
* for details on what arguments are available and how they work.
|
||||
*
|
||||
* @uses add_filter() This method is a convenience wrapper for add_filter().
|
||||
*
|
||||
* @param callable $callback
|
||||
* @return void
|
||||
*/
|
||||
public function addHttpRequestArgFilter($callback) {
|
||||
$this->addFilter('request_info_options', $callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a callback for filtering the plugin info retrieved from the external API.
|
||||
*
|
||||
* The callback function should take two arguments. If the plugin info was retrieved
|
||||
* successfully, the first argument passed will be an instance of PluginInfo. Otherwise,
|
||||
* it will be NULL. The second argument will be the corresponding return value of
|
||||
* wp_remote_get (see WP docs for details).
|
||||
*
|
||||
* The callback function should return a new or modified instance of PluginInfo or NULL.
|
||||
*
|
||||
* @uses add_filter() This method is a convenience wrapper for add_filter().
|
||||
*
|
||||
* @param callable $callback
|
||||
* @return void
|
||||
*/
|
||||
public function addResultFilter($callback) {
|
||||
$this->addFilter('request_info_result', $callback, 10, 2);
|
||||
}
|
||||
|
||||
protected function createDebugBarExtension() {
|
||||
return new Puc_v4p11_DebugBar_PluginExtension($this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a package instance that represents this plugin or theme.
|
||||
*
|
||||
* @return Puc_v4p11_InstalledPackage
|
||||
*/
|
||||
protected function createInstalledPackage() {
|
||||
return new Puc_v4p11_Plugin_Package($this->pluginAbsolutePath, $this);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Puc_v4p11_Plugin_Package
|
||||
*/
|
||||
public function getInstalledPackage() {
|
||||
return $this->package;
|
||||
}
|
||||
}
|
||||
|
||||
endif;
|
266
dependencies/yahnis-elsts/plugin-update-checker/Puc/v4p11/Scheduler.php
vendored
Normal file
266
dependencies/yahnis-elsts/plugin-update-checker/Puc/v4p11/Scheduler.php
vendored
Normal file
@ -0,0 +1,266 @@
|
||||
<?php
|
||||
if ( !class_exists('Puc_v4p11_Scheduler', false) ):
|
||||
|
||||
/**
|
||||
* The scheduler decides when and how often to check for updates.
|
||||
* It calls @see Puc_v4p11_UpdateChecker::checkForUpdates() to perform the actual checks.
|
||||
*/
|
||||
class Puc_v4p11_Scheduler {
|
||||
public $checkPeriod = 12; //How often to check for updates (in hours).
|
||||
public $throttleRedundantChecks = false; //Check less often if we already know that an update is available.
|
||||
public $throttledCheckPeriod = 72;
|
||||
|
||||
protected $hourlyCheckHooks = array('load-update.php');
|
||||
|
||||
/**
|
||||
* @var Puc_v4p11_UpdateChecker
|
||||
*/
|
||||
protected $updateChecker;
|
||||
|
||||
private $cronHook = null;
|
||||
|
||||
/**
|
||||
* Scheduler constructor.
|
||||
*
|
||||
* @param Puc_v4p11_UpdateChecker $updateChecker
|
||||
* @param int $checkPeriod How often to check for updates (in hours).
|
||||
* @param array $hourlyHooks
|
||||
*/
|
||||
public function __construct($updateChecker, $checkPeriod, $hourlyHooks = array('load-plugins.php')) {
|
||||
$this->updateChecker = $updateChecker;
|
||||
$this->checkPeriod = $checkPeriod;
|
||||
|
||||
//Set up the periodic update checks
|
||||
$this->cronHook = $this->updateChecker->getUniqueName('cron_check_updates');
|
||||
if ( $this->checkPeriod > 0 ){
|
||||
|
||||
//Trigger the check via Cron.
|
||||
//Try to use one of the default schedules if possible as it's less likely to conflict
|
||||
//with other plugins and their custom schedules.
|
||||
$defaultSchedules = array(
|
||||
1 => 'hourly',
|
||||
12 => 'twicedaily',
|
||||
24 => 'daily',
|
||||
);
|
||||
if ( array_key_exists($this->checkPeriod, $defaultSchedules) ) {
|
||||
$scheduleName = $defaultSchedules[$this->checkPeriod];
|
||||
} else {
|
||||
//Use a custom cron schedule.
|
||||
$scheduleName = 'every' . $this->checkPeriod . 'hours';
|
||||
add_filter('cron_schedules', array($this, '_addCustomSchedule'));
|
||||
}
|
||||
|
||||
if ( !wp_next_scheduled($this->cronHook) && !defined('WP_INSTALLING') ) {
|
||||
//Randomly offset the schedule to help prevent update server traffic spikes. Without this
|
||||
//most checks may happen during times of day when people are most likely to install new plugins.
|
||||
$firstCheckTime = time() - rand(0, max($this->checkPeriod * 3600 - 15 * 60, 1));
|
||||
$firstCheckTime = apply_filters(
|
||||
$this->updateChecker->getUniqueName('first_check_time'),
|
||||
$firstCheckTime
|
||||
);
|
||||
wp_schedule_event($firstCheckTime, $scheduleName, $this->cronHook);
|
||||
}
|
||||
add_action($this->cronHook, array($this, 'maybeCheckForUpdates'));
|
||||
|
||||
//In case Cron is disabled or unreliable, we also manually trigger
|
||||
//the periodic checks while the user is browsing the Dashboard.
|
||||
add_action( 'admin_init', array($this, 'maybeCheckForUpdates') );
|
||||
|
||||
//Like WordPress itself, we check more often on certain pages.
|
||||
/** @see wp_update_plugins */
|
||||
add_action('load-update-core.php', array($this, 'maybeCheckForUpdates'));
|
||||
//"load-update.php" and "load-plugins.php" or "load-themes.php".
|
||||
$this->hourlyCheckHooks = array_merge($this->hourlyCheckHooks, $hourlyHooks);
|
||||
foreach($this->hourlyCheckHooks as $hook) {
|
||||
add_action($hook, array($this, 'maybeCheckForUpdates'));
|
||||
}
|
||||
//This hook fires after a bulk update is complete.
|
||||
add_action('upgrader_process_complete', array($this, 'upgraderProcessComplete'), 11, 2);
|
||||
|
||||
} else {
|
||||
//Periodic checks are disabled.
|
||||
wp_clear_scheduled_hook($this->cronHook);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs upon the WP action upgrader_process_complete.
|
||||
*
|
||||
* We look at the parameters to decide whether to call maybeCheckForUpdates() or not.
|
||||
* We also check if the update checker has been removed by the update.
|
||||
*
|
||||
* @param WP_Upgrader $upgrader WP_Upgrader instance
|
||||
* @param array $upgradeInfo extra information about the upgrade
|
||||
*/
|
||||
public function upgraderProcessComplete(
|
||||
/** @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'])
|
||||
|| 'update' !== $upgradeInfo['action'] || !in_array($upgradeInfo['type'], array('plugin', 'theme'))
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
//Filter out notifications of upgrades that should have no bearing upon whether or not our
|
||||
//current info is up-to-date.
|
||||
if ( is_a($this->updateChecker, 'Puc_v4p11_Theme_UpdateChecker') ) {
|
||||
if ( 'theme' !== $upgradeInfo['type'] || !isset($upgradeInfo['themes']) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
//Letting too many things going through for checks is not a real problem, so we compare widely.
|
||||
if ( !in_array(
|
||||
strtolower($this->updateChecker->directoryName),
|
||||
array_map('strtolower', $upgradeInfo['themes'])
|
||||
) ) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if ( is_a($this->updateChecker, 'Puc_v4p11_Plugin_UpdateChecker') ) {
|
||||
if ( 'plugin' !== $upgradeInfo['type'] || !isset($upgradeInfo['plugins']) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
//Themes pass in directory names in the information array, but plugins use the relative plugin path.
|
||||
if ( !in_array(
|
||||
strtolower($this->updateChecker->directoryName),
|
||||
array_map('dirname', array_map('strtolower', $upgradeInfo['plugins']))
|
||||
) ) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
$this->maybeCheckForUpdates();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check for updates if the configured check interval has already elapsed.
|
||||
* Will use a shorter check interval on certain admin pages like "Dashboard -> Updates" or when doing cron.
|
||||
*
|
||||
* You can override the default behaviour by using the "puc_check_now-$slug" filter.
|
||||
* The filter callback will be passed three parameters:
|
||||
* - Current decision. TRUE = check updates now, FALSE = don't check now.
|
||||
* - Last check time as a Unix timestamp.
|
||||
* - Configured check period in hours.
|
||||
* Return TRUE to check for updates immediately, or FALSE to cancel.
|
||||
*
|
||||
* This method is declared public because it's a hook callback. Calling it directly is not recommended.
|
||||
*/
|
||||
public function maybeCheckForUpdates() {
|
||||
if ( empty($this->checkPeriod) ){
|
||||
return;
|
||||
}
|
||||
|
||||
$state = $this->updateChecker->getUpdateState();
|
||||
$shouldCheck = ($state->timeSinceLastCheck() >= $this->getEffectiveCheckPeriod());
|
||||
|
||||
//Let plugin authors substitute their own algorithm.
|
||||
$shouldCheck = apply_filters(
|
||||
$this->updateChecker->getUniqueName('check_now'),
|
||||
$shouldCheck,
|
||||
$state->getLastCheck(),
|
||||
$this->checkPeriod
|
||||
);
|
||||
|
||||
if ( $shouldCheck ) {
|
||||
$this->updateChecker->checkForUpdates();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate the actual check period based on the current status and environment.
|
||||
*
|
||||
* @return int Check period in seconds.
|
||||
*/
|
||||
protected function getEffectiveCheckPeriod() {
|
||||
$currentFilter = current_filter();
|
||||
if ( in_array($currentFilter, array('load-update-core.php', 'upgrader_process_complete')) ) {
|
||||
//Check more often when the user visits "Dashboard -> Updates" or does a bulk update.
|
||||
$period = 60;
|
||||
} else if ( in_array($currentFilter, $this->hourlyCheckHooks) ) {
|
||||
//Also check more often on /wp-admin/update.php and the "Plugins" or "Themes" page.
|
||||
$period = 3600;
|
||||
} else if ( $this->throttleRedundantChecks && ($this->updateChecker->getUpdate() !== null) ) {
|
||||
//Check less frequently if it's already known that an update is available.
|
||||
$period = $this->throttledCheckPeriod * 3600;
|
||||
} else if ( defined('DOING_CRON') && constant('DOING_CRON') ) {
|
||||
//WordPress cron schedules are not exact, so lets do an update check even
|
||||
//if slightly less than $checkPeriod hours have elapsed since the last check.
|
||||
$cronFuzziness = 20 * 60;
|
||||
$period = $this->checkPeriod * 3600 - $cronFuzziness;
|
||||
} else {
|
||||
$period = $this->checkPeriod * 3600;
|
||||
}
|
||||
|
||||
return $period;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add our custom schedule to the array of Cron schedules used by WP.
|
||||
*
|
||||
* @param array $schedules
|
||||
* @return array
|
||||
*/
|
||||
public function _addCustomSchedule($schedules) {
|
||||
if ( $this->checkPeriod && ($this->checkPeriod > 0) ){
|
||||
$scheduleName = 'every' . $this->checkPeriod . 'hours';
|
||||
$schedules[$scheduleName] = array(
|
||||
'interval' => $this->checkPeriod * 3600,
|
||||
'display' => sprintf('Every %d hours', $this->checkPeriod),
|
||||
);
|
||||
}
|
||||
return $schedules;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the scheduled cron event that the library uses to check for updates.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function removeUpdaterCron() {
|
||||
wp_clear_scheduled_hook($this->cronHook);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the name of the update checker's WP-cron hook. Mostly useful for debugging.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getCronHookName() {
|
||||
return $this->cronHook;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove most hooks added by the scheduler.
|
||||
*/
|
||||
public function removeHooks() {
|
||||
remove_filter('cron_schedules', array($this, '_addCustomSchedule'));
|
||||
remove_action('admin_init', array($this, 'maybeCheckForUpdates'));
|
||||
remove_action('load-update-core.php', array($this, 'maybeCheckForUpdates'));
|
||||
|
||||
if ( $this->cronHook !== null ) {
|
||||
remove_action($this->cronHook, array($this, 'maybeCheckForUpdates'));
|
||||
}
|
||||
if ( !empty($this->hourlyCheckHooks) ) {
|
||||
foreach ($this->hourlyCheckHooks as $hook) {
|
||||
remove_action($hook, array($this, 'maybeCheckForUpdates'));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
endif;
|
207
dependencies/yahnis-elsts/plugin-update-checker/Puc/v4p11/StateStore.php
vendored
Normal file
207
dependencies/yahnis-elsts/plugin-update-checker/Puc/v4p11/StateStore.php
vendored
Normal file
@ -0,0 +1,207 @@
|
||||
<?php
|
||||
|
||||
if ( !class_exists('Puc_v4p11_StateStore', false) ):
|
||||
|
||||
class Puc_v4p11_StateStore {
|
||||
/**
|
||||
* @var int Last update check timestamp.
|
||||
*/
|
||||
protected $lastCheck = 0;
|
||||
|
||||
/**
|
||||
* @var string Version number.
|
||||
*/
|
||||
protected $checkedVersion = '';
|
||||
|
||||
/**
|
||||
* @var Puc_v4p11_Update|null Cached update.
|
||||
*/
|
||||
protected $update = null;
|
||||
|
||||
/**
|
||||
* @var string Site option name.
|
||||
*/
|
||||
private $optionName = '';
|
||||
|
||||
/**
|
||||
* @var bool Whether we've already tried to load the state from the database.
|
||||
*/
|
||||
private $isLoaded = false;
|
||||
|
||||
public function __construct($optionName) {
|
||||
$this->optionName = $optionName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get time elapsed since the last update check.
|
||||
*
|
||||
* If there are no recorded update checks, this method returns a large arbitrary number
|
||||
* (i.e. time since the Unix epoch).
|
||||
*
|
||||
* @return int Elapsed time in seconds.
|
||||
*/
|
||||
public function timeSinceLastCheck() {
|
||||
$this->lazyLoad();
|
||||
return time() - $this->lastCheck;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getLastCheck() {
|
||||
$this->lazyLoad();
|
||||
return $this->lastCheck;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the time of the last update check to the current timestamp.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setLastCheckToNow() {
|
||||
$this->lazyLoad();
|
||||
$this->lastCheck = time();
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return null|Puc_v4p11_Update
|
||||
*/
|
||||
public function getUpdate() {
|
||||
$this->lazyLoad();
|
||||
return $this->update;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Puc_v4p11_Update|null $update
|
||||
* @return $this
|
||||
*/
|
||||
public function setUpdate(Puc_v4p11_Update $update = null) {
|
||||
$this->lazyLoad();
|
||||
$this->update = $update;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getCheckedVersion() {
|
||||
$this->lazyLoad();
|
||||
return $this->checkedVersion;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $version
|
||||
* @return $this
|
||||
*/
|
||||
public function setCheckedVersion($version) {
|
||||
$this->lazyLoad();
|
||||
$this->checkedVersion = strval($version);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get translation updates.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getTranslations() {
|
||||
$this->lazyLoad();
|
||||
if ( isset($this->update, $this->update->translations) ) {
|
||||
return $this->update->translations;
|
||||
}
|
||||
return array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set translation updates.
|
||||
*
|
||||
* @param array $translationUpdates
|
||||
*/
|
||||
public function setTranslations($translationUpdates) {
|
||||
$this->lazyLoad();
|
||||
if ( isset($this->update) ) {
|
||||
$this->update->translations = $translationUpdates;
|
||||
$this->save();
|
||||
}
|
||||
}
|
||||
|
||||
public function save() {
|
||||
$state = new stdClass();
|
||||
|
||||
$state->lastCheck = $this->lastCheck;
|
||||
$state->checkedVersion = $this->checkedVersion;
|
||||
|
||||
if ( isset($this->update)) {
|
||||
$state->update = $this->update->toStdClass();
|
||||
|
||||
$updateClass = get_class($this->update);
|
||||
$state->updateClass = $updateClass;
|
||||
$prefix = $this->getLibPrefix();
|
||||
if ( Puc_v4p11_Utils::startsWith($updateClass, $prefix) ) {
|
||||
$state->updateBaseClass = substr($updateClass, strlen($prefix));
|
||||
}
|
||||
}
|
||||
|
||||
update_site_option($this->optionName, $state);
|
||||
$this->isLoaded = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return $this
|
||||
*/
|
||||
public function lazyLoad() {
|
||||
if ( !$this->isLoaded ) {
|
||||
$this->load();
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
protected function load() {
|
||||
$this->isLoaded = true;
|
||||
|
||||
$state = get_site_option($this->optionName, null);
|
||||
|
||||
if ( !is_object($state) ) {
|
||||
$this->lastCheck = 0;
|
||||
$this->checkedVersion = '';
|
||||
$this->update = null;
|
||||
return;
|
||||
}
|
||||
|
||||
$this->lastCheck = intval(Puc_v4p11_Utils::get($state, 'lastCheck', 0));
|
||||
$this->checkedVersion = Puc_v4p11_Utils::get($state, 'checkedVersion', '');
|
||||
$this->update = null;
|
||||
|
||||
if ( isset($state->update) ) {
|
||||
//This mess is due to the fact that the want the update class from this version
|
||||
//of the library, not the version that saved the update.
|
||||
|
||||
$updateClass = null;
|
||||
if ( isset($state->updateBaseClass) ) {
|
||||
$updateClass = $this->getLibPrefix() . $state->updateBaseClass;
|
||||
} else if ( isset($state->updateClass) && class_exists($state->updateClass) ) {
|
||||
$updateClass = $state->updateClass;
|
||||
}
|
||||
|
||||
if ( $updateClass !== null ) {
|
||||
$this->update = call_user_func(array($updateClass, 'fromObject'), $state->update);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function delete() {
|
||||
delete_site_option($this->optionName);
|
||||
|
||||
$this->lastCheck = 0;
|
||||
$this->checkedVersion = '';
|
||||
$this->update = null;
|
||||
}
|
||||
|
||||
private function getLibPrefix() {
|
||||
$parts = explode('_', __CLASS__, 3);
|
||||
return $parts[0] . '_' . $parts[1] . '_';
|
||||
}
|
||||
}
|
||||
|
||||
endif;
|
65
dependencies/yahnis-elsts/plugin-update-checker/Puc/v4p11/Theme/Package.php
vendored
Normal file
65
dependencies/yahnis-elsts/plugin-update-checker/Puc/v4p11/Theme/Package.php
vendored
Normal file
@ -0,0 +1,65 @@
|
||||
<?php
|
||||
if ( !class_exists('Puc_v4p11_Theme_Package', false) ):
|
||||
|
||||
class Puc_v4p11_Theme_Package extends Puc_v4p11_InstalledPackage {
|
||||
/**
|
||||
* @var string Theme directory name.
|
||||
*/
|
||||
protected $stylesheet;
|
||||
|
||||
/**
|
||||
* @var WP_Theme Theme object.
|
||||
*/
|
||||
protected $theme;
|
||||
|
||||
public function __construct($stylesheet, $updateChecker) {
|
||||
$this->stylesheet = $stylesheet;
|
||||
$this->theme = wp_get_theme($this->stylesheet);
|
||||
|
||||
parent::__construct($updateChecker);
|
||||
}
|
||||
|
||||
public function getInstalledVersion() {
|
||||
return $this->theme->get('Version');
|
||||
}
|
||||
|
||||
public function getAbsoluteDirectoryPath() {
|
||||
if ( method_exists($this->theme, 'get_stylesheet_directory') ) {
|
||||
return $this->theme->get_stylesheet_directory(); //Available since WP 3.4.
|
||||
}
|
||||
return get_theme_root($this->stylesheet) . '/' . $this->stylesheet;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the value of a specific plugin or theme header.
|
||||
*
|
||||
* @param string $headerName
|
||||
* @param string $defaultValue
|
||||
* @return string Either the value of the header, or $defaultValue if the header doesn't exist or is empty.
|
||||
*/
|
||||
public function getHeaderValue($headerName, $defaultValue = '') {
|
||||
$value = $this->theme->get($headerName);
|
||||
if ( ($headerName === false) || ($headerName === '') ) {
|
||||
return $defaultValue;
|
||||
}
|
||||
return $value;
|
||||
}
|
||||
|
||||
protected function getHeaderNames() {
|
||||
return array(
|
||||
'Name' => 'Theme Name',
|
||||
'ThemeURI' => 'Theme URI',
|
||||
'Description' => 'Description',
|
||||
'Author' => 'Author',
|
||||
'AuthorURI' => 'Author URI',
|
||||
'Version' => 'Version',
|
||||
'Template' => 'Template',
|
||||
'Status' => 'Status',
|
||||
'Tags' => 'Tags',
|
||||
'TextDomain' => 'Text Domain',
|
||||
'DomainPath' => 'Domain Path',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
endif;
|
84
dependencies/yahnis-elsts/plugin-update-checker/Puc/v4p11/Theme/Update.php
vendored
Normal file
84
dependencies/yahnis-elsts/plugin-update-checker/Puc/v4p11/Theme/Update.php
vendored
Normal file
@ -0,0 +1,84 @@
|
||||
<?php
|
||||
|
||||
if ( !class_exists('Puc_v4p11_Theme_Update', false) ):
|
||||
|
||||
class Puc_v4p11_Theme_Update extends Puc_v4p11_Update {
|
||||
public $details_url = '';
|
||||
|
||||
protected static $extraFields = array('details_url');
|
||||
|
||||
/**
|
||||
* Transform the metadata into the format used by WordPress core.
|
||||
* Note the inconsistency: WP stores plugin updates as objects and theme updates as arrays.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function toWpFormat() {
|
||||
$update = array(
|
||||
'theme' => $this->slug,
|
||||
'new_version' => $this->version,
|
||||
'url' => $this->details_url,
|
||||
);
|
||||
|
||||
if ( !empty($this->download_url) ) {
|
||||
$update['package'] = $this->download_url;
|
||||
}
|
||||
|
||||
return $update;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new instance of Theme_Update from its JSON-encoded representation.
|
||||
*
|
||||
* @param string $json Valid JSON string representing a theme information object.
|
||||
* @return self New instance of ThemeUpdate, or NULL on error.
|
||||
*/
|
||||
public static function fromJson($json) {
|
||||
$instance = new self();
|
||||
if ( !parent::createFromJson($json, $instance) ) {
|
||||
return null;
|
||||
}
|
||||
return $instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new instance by copying the necessary fields from another object.
|
||||
*
|
||||
* @param StdClass|Puc_v4p11_Theme_Update $object The source object.
|
||||
* @return Puc_v4p11_Theme_Update The new copy.
|
||||
*/
|
||||
public static function fromObject($object) {
|
||||
$update = new self();
|
||||
$update->copyFields($object, $update);
|
||||
return $update;
|
||||
}
|
||||
|
||||
/**
|
||||
* Basic validation.
|
||||
*
|
||||
* @param StdClass $apiResponse
|
||||
* @return bool|WP_Error
|
||||
*/
|
||||
protected function validateMetadata($apiResponse) {
|
||||
$required = array('version', 'details_url');
|
||||
foreach($required as $key) {
|
||||
if ( !isset($apiResponse->$key) || empty($apiResponse->$key) ) {
|
||||
return new WP_Error(
|
||||
'tuc-invalid-metadata',
|
||||
sprintf('The theme metadata is missing the required "%s" key.', $key)
|
||||
);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
protected function getFieldNames() {
|
||||
return array_merge(parent::getFieldNames(), self::$extraFields);
|
||||
}
|
||||
|
||||
protected function getPrefixedFilter($tag) {
|
||||
return parent::getPrefixedFilter($tag) . '_theme';
|
||||
}
|
||||
}
|
||||
|
||||
endif;
|
152
dependencies/yahnis-elsts/plugin-update-checker/Puc/v4p11/Theme/UpdateChecker.php
vendored
Normal file
152
dependencies/yahnis-elsts/plugin-update-checker/Puc/v4p11/Theme/UpdateChecker.php
vendored
Normal file
@ -0,0 +1,152 @@
|
||||
<?php
|
||||
|
||||
if ( !class_exists('Puc_v4p11_Theme_UpdateChecker', false) ):
|
||||
|
||||
class Puc_v4p11_Theme_UpdateChecker extends Puc_v4p11_UpdateChecker {
|
||||
protected $filterSuffix = 'theme';
|
||||
protected $updateTransient = 'update_themes';
|
||||
protected $translationType = 'theme';
|
||||
|
||||
/**
|
||||
* @var string Theme directory name.
|
||||
*/
|
||||
protected $stylesheet;
|
||||
|
||||
public function __construct($metadataUrl, $stylesheet = null, $customSlug = null, $checkPeriod = 12, $optionName = '') {
|
||||
if ( $stylesheet === null ) {
|
||||
$stylesheet = get_stylesheet();
|
||||
}
|
||||
$this->stylesheet = $stylesheet;
|
||||
|
||||
parent::__construct(
|
||||
$metadataUrl,
|
||||
$stylesheet,
|
||||
$customSlug ? $customSlug : $stylesheet,
|
||||
$checkPeriod,
|
||||
$optionName
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* For themes, the update array is indexed by theme directory name.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getUpdateListKey() {
|
||||
return $this->directoryName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the latest update (if any) from the configured API endpoint.
|
||||
*
|
||||
* @return Puc_v4p11_Update|null An instance of Update, or NULL when no updates are available.
|
||||
*/
|
||||
public function requestUpdate() {
|
||||
list($themeUpdate, $result) = $this->requestMetadata('Puc_v4p11_Theme_Update', 'request_update');
|
||||
|
||||
if ( $themeUpdate !== null ) {
|
||||
/** @var Puc_v4p11_Theme_Update $themeUpdate */
|
||||
$themeUpdate->slug = $this->slug;
|
||||
}
|
||||
|
||||
$themeUpdate = $this->filterUpdateResult($themeUpdate, $result);
|
||||
return $themeUpdate;
|
||||
}
|
||||
|
||||
protected function getNoUpdateItemFields() {
|
||||
return array_merge(
|
||||
parent::getNoUpdateItemFields(),
|
||||
array(
|
||||
'theme' => $this->directoryName,
|
||||
'requires' => '',
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
public function userCanInstallUpdates() {
|
||||
return current_user_can('update_themes');
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an instance of the scheduler.
|
||||
*
|
||||
* @param int $checkPeriod
|
||||
* @return Puc_v4p11_Scheduler
|
||||
*/
|
||||
protected function createScheduler($checkPeriod) {
|
||||
return new Puc_v4p11_Scheduler($this, $checkPeriod, array('load-themes.php'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Is there an update being installed right now for this theme?
|
||||
*
|
||||
* @param WP_Upgrader|null $upgrader The upgrader that's performing the current update.
|
||||
* @return bool
|
||||
*/
|
||||
public function isBeingUpgraded($upgrader = null) {
|
||||
return $this->upgraderStatus->isThemeBeingUpgraded($this->stylesheet, $upgrader);
|
||||
}
|
||||
|
||||
protected function createDebugBarExtension() {
|
||||
return new Puc_v4p11_DebugBar_Extension($this, 'Puc_v4p11_DebugBar_ThemePanel');
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a callback for filtering query arguments.
|
||||
*
|
||||
* The callback function should take one argument - an associative array of query arguments.
|
||||
* It should return a modified array of query arguments.
|
||||
*
|
||||
* @param callable $callback
|
||||
* @return void
|
||||
*/
|
||||
public function addQueryArgFilter($callback){
|
||||
$this->addFilter('request_update_query_args', $callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a callback for filtering arguments passed to wp_remote_get().
|
||||
*
|
||||
* The callback function should take one argument - an associative array of arguments -
|
||||
* and return a modified array or arguments. See the WP documentation on wp_remote_get()
|
||||
* for details on what arguments are available and how they work.
|
||||
*
|
||||
* @uses add_filter() This method is a convenience wrapper for add_filter().
|
||||
*
|
||||
* @param callable $callback
|
||||
* @return void
|
||||
*/
|
||||
public function addHttpRequestArgFilter($callback) {
|
||||
$this->addFilter('request_update_options', $callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a callback for filtering theme updates retrieved from the external API.
|
||||
*
|
||||
* The callback function should take two arguments. If the theme update was retrieved
|
||||
* successfully, the first argument passed will be an instance of Theme_Update. Otherwise,
|
||||
* it will be NULL. The second argument will be the corresponding return value of
|
||||
* wp_remote_get (see WP docs for details).
|
||||
*
|
||||
* The callback function should return a new or modified instance of Theme_Update or NULL.
|
||||
*
|
||||
* @uses add_filter() This method is a convenience wrapper for add_filter().
|
||||
*
|
||||
* @param callable $callback
|
||||
* @return void
|
||||
*/
|
||||
public function addResultFilter($callback) {
|
||||
$this->addFilter('request_update_result', $callback, 10, 2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a package instance that represents this plugin or theme.
|
||||
*
|
||||
* @return Puc_v4p11_InstalledPackage
|
||||
*/
|
||||
protected function createInstalledPackage() {
|
||||
return new Puc_v4p11_Theme_Package($this->stylesheet, $this);
|
||||
}
|
||||
}
|
||||
|
||||
endif;
|
34
dependencies/yahnis-elsts/plugin-update-checker/Puc/v4p11/Update.php
vendored
Normal file
34
dependencies/yahnis-elsts/plugin-update-checker/Puc/v4p11/Update.php
vendored
Normal file
@ -0,0 +1,34 @@
|
||||
<?php
|
||||
if ( !class_exists('Puc_v4p11_Update', false) ):
|
||||
|
||||
/**
|
||||
* A simple container class for holding information about an available update.
|
||||
*
|
||||
* @author Janis Elsts
|
||||
* @access public
|
||||
*/
|
||||
abstract class Puc_v4p11_Update extends Puc_v4p11_Metadata {
|
||||
public $slug;
|
||||
public $version;
|
||||
public $download_url;
|
||||
public $translations = array();
|
||||
|
||||
/**
|
||||
* @return string[]
|
||||
*/
|
||||
protected function getFieldNames() {
|
||||
return array('slug', 'version', 'download_url', 'translations');
|
||||
}
|
||||
|
||||
public function toWpFormat() {
|
||||
$update = new stdClass();
|
||||
|
||||
$update->slug = $this->slug;
|
||||
$update->new_version = $this->version;
|
||||
$update->package = $this->download_url;
|
||||
|
||||
return $update;
|
||||
}
|
||||
}
|
||||
|
||||
endif;
|
997
dependencies/yahnis-elsts/plugin-update-checker/Puc/v4p11/UpdateChecker.php
vendored
Normal file
997
dependencies/yahnis-elsts/plugin-update-checker/Puc/v4p11/UpdateChecker.php
vendored
Normal file
@ -0,0 +1,997 @@
|
||||
<?php
|
||||
|
||||
if ( !class_exists('Puc_v4p11_UpdateChecker', false) ):
|
||||
|
||||
abstract class Puc_v4p11_UpdateChecker {
|
||||
protected $filterSuffix = '';
|
||||
protected $updateTransient = '';
|
||||
protected $translationType = ''; //"plugin" or "theme".
|
||||
|
||||
/**
|
||||
* Set to TRUE to enable error reporting. Errors are raised using trigger_error()
|
||||
* and should be logged to the standard PHP error log.
|
||||
* @var bool
|
||||
*/
|
||||
public $debugMode = null;
|
||||
|
||||
/**
|
||||
* @var string Where to store the update info.
|
||||
*/
|
||||
public $optionName = '';
|
||||
|
||||
/**
|
||||
* @var string The URL of the metadata file.
|
||||
*/
|
||||
public $metadataUrl = '';
|
||||
|
||||
/**
|
||||
* @var string Plugin or theme directory name.
|
||||
*/
|
||||
public $directoryName = '';
|
||||
|
||||
/**
|
||||
* @var string The slug that will be used in update checker hooks and remote API requests.
|
||||
* Usually matches the directory name unless the plugin/theme directory has been renamed.
|
||||
*/
|
||||
public $slug = '';
|
||||
|
||||
/**
|
||||
* @var Puc_v4p11_InstalledPackage
|
||||
*/
|
||||
protected $package;
|
||||
|
||||
/**
|
||||
* @var Puc_v4p11_Scheduler
|
||||
*/
|
||||
public $scheduler;
|
||||
|
||||
/**
|
||||
* @var Puc_v4p11_UpgraderStatus
|
||||
*/
|
||||
protected $upgraderStatus;
|
||||
|
||||
/**
|
||||
* @var Puc_v4p11_StateStore
|
||||
*/
|
||||
protected $updateState;
|
||||
|
||||
/**
|
||||
* @var array List of API errors triggered during the last checkForUpdates() call.
|
||||
*/
|
||||
protected $lastRequestApiErrors = array();
|
||||
|
||||
/**
|
||||
* @var string|mixed The default is 0 because parse_url() can return NULL or FALSE.
|
||||
*/
|
||||
protected $cachedMetadataHost = 0;
|
||||
|
||||
/**
|
||||
* @var Puc_v4p11_DebugBar_Extension|null
|
||||
*/
|
||||
protected $debugBarExtension = null;
|
||||
|
||||
public function __construct($metadataUrl, $directoryName, $slug = null, $checkPeriod = 12, $optionName = '') {
|
||||
$this->debugMode = (bool)(constant('WP_DEBUG'));
|
||||
$this->metadataUrl = $metadataUrl;
|
||||
$this->directoryName = $directoryName;
|
||||
$this->slug = !empty($slug) ? $slug : $this->directoryName;
|
||||
|
||||
$this->optionName = $optionName;
|
||||
if ( empty($this->optionName) ) {
|
||||
//BC: Initially the library only supported plugin updates and didn't use type prefixes
|
||||
//in the option name. Lets use the same prefix-less name when possible.
|
||||
if ( $this->filterSuffix === '' ) {
|
||||
$this->optionName = 'external_updates-' . $this->slug;
|
||||
} else {
|
||||
$this->optionName = $this->getUniqueName('external_updates');
|
||||
}
|
||||
}
|
||||
|
||||
$this->package = $this->createInstalledPackage();
|
||||
$this->scheduler = $this->createScheduler($checkPeriod);
|
||||
$this->upgraderStatus = new Puc_v4p11_UpgraderStatus();
|
||||
$this->updateState = new Puc_v4p11_StateStore($this->optionName);
|
||||
|
||||
if ( did_action('init') ) {
|
||||
$this->loadTextDomain();
|
||||
} else {
|
||||
add_action('init', array($this, 'loadTextDomain'));
|
||||
}
|
||||
|
||||
$this->installHooks();
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
public function loadTextDomain() {
|
||||
//We're not using load_plugin_textdomain() or its siblings because figuring out where
|
||||
//the library is located (plugin, mu-plugin, theme, custom wp-content paths) is messy.
|
||||
$domain = 'plugin-update-checker';
|
||||
$locale = apply_filters(
|
||||
'plugin_locale',
|
||||
(is_admin() && function_exists('get_user_locale')) ? get_user_locale() : get_locale(),
|
||||
$domain
|
||||
);
|
||||
|
||||
$moFile = $domain . '-' . $locale . '.mo';
|
||||
$path = realpath(dirname(__FILE__) . '/../../languages');
|
||||
|
||||
if ($path && file_exists($path)) {
|
||||
load_textdomain($domain, $path . '/' . $moFile);
|
||||
}
|
||||
}
|
||||
|
||||
protected function installHooks() {
|
||||
//Insert our update info into the update array maintained by WP.
|
||||
add_filter('site_transient_' . $this->updateTransient, array($this,'injectUpdate'));
|
||||
|
||||
//Insert translation updates into the update list.
|
||||
add_filter('site_transient_' . $this->updateTransient, array($this, 'injectTranslationUpdates'));
|
||||
|
||||
//Clear translation updates when WP clears the update cache.
|
||||
//This needs to be done directly because the library doesn't actually remove obsolete plugin updates,
|
||||
//it just hides them (see getUpdate()). We can't do that with translations - too much disk I/O.
|
||||
add_action(
|
||||
'delete_site_transient_' . $this->updateTransient,
|
||||
array($this, 'clearCachedTranslationUpdates')
|
||||
);
|
||||
|
||||
//Rename the update directory to be the same as the existing directory.
|
||||
if ( $this->directoryName !== '.' ) {
|
||||
add_filter('upgrader_source_selection', array($this, 'fixDirectoryName'), 10, 3);
|
||||
}
|
||||
|
||||
//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);
|
||||
|
||||
//DebugBar integration.
|
||||
if ( did_action('plugins_loaded') ) {
|
||||
$this->maybeInitDebugBar();
|
||||
} else {
|
||||
add_action('plugins_loaded', array($this, 'maybeInitDebugBar'));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove hooks that were added by this update checker instance.
|
||||
*/
|
||||
public function removeHooks() {
|
||||
remove_filter('site_transient_' . $this->updateTransient, array($this,'injectUpdate'));
|
||||
remove_filter('site_transient_' . $this->updateTransient, array($this, 'injectTranslationUpdates'));
|
||||
remove_action(
|
||||
'delete_site_transient_' . $this->updateTransient,
|
||||
array($this, 'clearCachedTranslationUpdates')
|
||||
);
|
||||
|
||||
remove_filter('upgrader_source_selection', array($this, 'fixDirectoryName'), 10);
|
||||
remove_filter('http_request_host_is_external', array($this, 'allowMetadataHost'), 10);
|
||||
remove_action('plugins_loaded', array($this, 'maybeInitDebugBar'));
|
||||
|
||||
remove_action('init', array($this, 'loadTextDomain'));
|
||||
|
||||
if ( $this->scheduler ) {
|
||||
$this->scheduler->removeHooks();
|
||||
}
|
||||
|
||||
if ( $this->debugBarExtension ) {
|
||||
$this->debugBarExtension->removeHooks();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the current user has the required permissions to install updates.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
abstract public function userCanInstallUpdates();
|
||||
|
||||
/**
|
||||
* Explicitly allow HTTP requests to the metadata URL.
|
||||
*
|
||||
* WordPress has a security feature where the HTTP API will reject all requests that are sent to
|
||||
* another site hosted on the same server as the current site (IP match), a local host, or a local
|
||||
* IP, unless the host exactly matches the current site.
|
||||
*
|
||||
* This feature is opt-in (at least in WP 4.4). Apparently some people enable it.
|
||||
*
|
||||
* That can be a problem when you're developing your plugin and you decide to host the update information
|
||||
* on the same server as your test site. Update requests will mysteriously fail.
|
||||
*
|
||||
* We fix that by adding an exception for the metadata host.
|
||||
*
|
||||
* @param bool $allow
|
||||
* @param string $host
|
||||
* @return bool
|
||||
*/
|
||||
public function allowMetadataHost($allow, $host) {
|
||||
if ( $this->cachedMetadataHost === 0 ) {
|
||||
$this->cachedMetadataHost = parse_url($this->metadataUrl, PHP_URL_HOST);
|
||||
}
|
||||
|
||||
if ( is_string($this->cachedMetadataHost) && (strtolower($host) === strtolower($this->cachedMetadataHost)) ) {
|
||||
return true;
|
||||
}
|
||||
return $allow;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a package instance that represents this plugin or theme.
|
||||
*
|
||||
* @return Puc_v4p11_InstalledPackage
|
||||
*/
|
||||
abstract protected function createInstalledPackage();
|
||||
|
||||
/**
|
||||
* @return Puc_v4p11_InstalledPackage
|
||||
*/
|
||||
public function getInstalledPackage() {
|
||||
return $this->package;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an instance of the scheduler.
|
||||
*
|
||||
* This is implemented as a method to make it possible for plugins to subclass the update checker
|
||||
* and substitute their own scheduler.
|
||||
*
|
||||
* @param int $checkPeriod
|
||||
* @return Puc_v4p11_Scheduler
|
||||
*/
|
||||
abstract protected function createScheduler($checkPeriod);
|
||||
|
||||
/**
|
||||
* Check for updates. The results are stored in the DB option specified in $optionName.
|
||||
*
|
||||
* @return Puc_v4p11_Update|null
|
||||
*/
|
||||
public function checkForUpdates() {
|
||||
$installedVersion = $this->getInstalledVersion();
|
||||
//Fail silently if we can't find the plugin/theme or read its header.
|
||||
if ( $installedVersion === null ) {
|
||||
$this->triggerError(
|
||||
sprintf('Skipping update check for %s - installed version unknown.', $this->slug),
|
||||
E_USER_WARNING
|
||||
);
|
||||
return null;
|
||||
}
|
||||
|
||||
//Start collecting API errors.
|
||||
$this->lastRequestApiErrors = array();
|
||||
add_action('puc_api_error', array($this, 'collectApiErrors'), 10, 4);
|
||||
|
||||
$state = $this->updateState;
|
||||
$state->setLastCheckToNow()
|
||||
->setCheckedVersion($installedVersion)
|
||||
->save(); //Save before checking in case something goes wrong
|
||||
|
||||
$state->setUpdate($this->requestUpdate());
|
||||
$state->save();
|
||||
|
||||
//Stop collecting API errors.
|
||||
remove_action('puc_api_error', array($this, 'collectApiErrors'), 10);
|
||||
|
||||
return $this->getUpdate();
|
||||
}
|
||||
|
||||
/**
|
||||
* Load the update checker state from the DB.
|
||||
*
|
||||
* @return Puc_v4p11_StateStore
|
||||
*/
|
||||
public function getUpdateState() {
|
||||
return $this->updateState->lazyLoad();
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset update checker state - i.e. last check time, cached update data and so on.
|
||||
*
|
||||
* Call this when your plugin is being uninstalled, or if you want to
|
||||
* clear the update cache.
|
||||
*/
|
||||
public function resetUpdateState() {
|
||||
$this->updateState->delete();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the details of the currently available update, if any.
|
||||
*
|
||||
* If no updates are available, or if the last known update version is below or equal
|
||||
* to the currently installed version, this method will return NULL.
|
||||
*
|
||||
* Uses cached update data. To retrieve update information straight from
|
||||
* the metadata URL, call requestUpdate() instead.
|
||||
*
|
||||
* @return Puc_v4p11_Update|null
|
||||
*/
|
||||
public function getUpdate() {
|
||||
$update = $this->updateState->getUpdate();
|
||||
|
||||
//Is there an update available?
|
||||
if ( isset($update) ) {
|
||||
//Check if the update is actually newer than the currently installed version.
|
||||
$installedVersion = $this->getInstalledVersion();
|
||||
if ( ($installedVersion !== null) && version_compare($update->version, $installedVersion, '>') ){
|
||||
return $update;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the latest update (if any) from the configured API endpoint.
|
||||
*
|
||||
* Subclasses should run the update through filterUpdateResult before returning it.
|
||||
*
|
||||
* @return Puc_v4p11_Update An instance of Update, or NULL when no updates are available.
|
||||
*/
|
||||
abstract public function requestUpdate();
|
||||
|
||||
/**
|
||||
* Filter the result of a requestUpdate() call.
|
||||
*
|
||||
* @param Puc_v4p11_Update|null $update
|
||||
* @param array|WP_Error|null $httpResult The value returned by wp_remote_get(), if any.
|
||||
* @return Puc_v4p11_Update
|
||||
*/
|
||||
protected function filterUpdateResult($update, $httpResult = null) {
|
||||
//Let plugins/themes modify the update.
|
||||
$update = apply_filters($this->getUniqueName('request_update_result'), $update, $httpResult);
|
||||
|
||||
$this->fixSupportedWordpressVersion($update);
|
||||
|
||||
if ( isset($update, $update->translations) ) {
|
||||
//Keep only those translation updates that apply to this site.
|
||||
$update->translations = $this->filterApplicableTranslations($update->translations);
|
||||
}
|
||||
|
||||
return $update;
|
||||
}
|
||||
|
||||
/**
|
||||
* The "Tested up to" field in the plugin metadata is supposed to be in the form of "major.minor",
|
||||
* while WordPress core's list_plugin_updates() expects the $update->tested field to be an exact
|
||||
* version, e.g. "major.minor.patch", to say it's compatible. In other case it shows
|
||||
* "Compatibility: Unknown".
|
||||
* The function mimics how wordpress.org API crafts the "tested" field out of "Tested up to".
|
||||
*
|
||||
* @param Puc_v4p11_Metadata|null $update
|
||||
*/
|
||||
protected function fixSupportedWordpressVersion(Puc_v4p11_Metadata $update = null) {
|
||||
if ( !isset($update->tested) || !preg_match('/^\d++\.\d++$/', $update->tested) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$actualWpVersions = array();
|
||||
|
||||
$wpVersion = $GLOBALS['wp_version'];
|
||||
|
||||
if ( function_exists('get_core_updates') ) {
|
||||
$coreUpdates = get_core_updates();
|
||||
if ( is_array($coreUpdates) ) {
|
||||
foreach ($coreUpdates as $coreUpdate) {
|
||||
if ( isset($coreUpdate->current) ) {
|
||||
$actualWpVersions[] = $coreUpdate->current;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$actualWpVersions[] = $wpVersion;
|
||||
|
||||
$actualWpPatchNumber = null;
|
||||
foreach ($actualWpVersions as $version) {
|
||||
if ( preg_match('/^(?P<majorMinor>\d++\.\d++)(?:\.(?P<patch>\d++))?/', $version, $versionParts) ) {
|
||||
if ( $versionParts['majorMinor'] === $update->tested ) {
|
||||
$patch = isset($versionParts['patch']) ? intval($versionParts['patch']) : 0;
|
||||
if ( $actualWpPatchNumber === null ) {
|
||||
$actualWpPatchNumber = $patch;
|
||||
} else {
|
||||
$actualWpPatchNumber = max($actualWpPatchNumber, $patch);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if ( $actualWpPatchNumber === null ) {
|
||||
$actualWpPatchNumber = 999;
|
||||
}
|
||||
|
||||
if ( $actualWpPatchNumber > 0 ) {
|
||||
$update->tested .= '.' . $actualWpPatchNumber;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the currently installed version of the plugin or theme.
|
||||
*
|
||||
* @return string|null Version number.
|
||||
*/
|
||||
public function getInstalledVersion() {
|
||||
return $this->package->getInstalledVersion();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the full path of the plugin or theme directory.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getAbsoluteDirectoryPath() {
|
||||
return $this->package->getAbsoluteDirectoryPath();
|
||||
}
|
||||
|
||||
/**
|
||||
* Trigger a PHP error, but only when $debugMode is enabled.
|
||||
*
|
||||
* @param string $message
|
||||
* @param int $errorType
|
||||
*/
|
||||
public function triggerError($message, $errorType) {
|
||||
if ( $this->isDebugModeEnabled() ) {
|
||||
trigger_error($message, $errorType);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
protected function isDebugModeEnabled() {
|
||||
if ( $this->debugMode === null ) {
|
||||
$this->debugMode = (bool)(constant('WP_DEBUG'));
|
||||
}
|
||||
return $this->debugMode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the full name of an update checker filter, action or DB entry.
|
||||
*
|
||||
* This method adds the "puc_" prefix and the "-$slug" suffix to the filter name.
|
||||
* For example, "pre_inject_update" becomes "puc_pre_inject_update-plugin-slug".
|
||||
*
|
||||
* @param string $baseTag
|
||||
* @return string
|
||||
*/
|
||||
public function getUniqueName($baseTag) {
|
||||
$name = 'puc_' . $baseTag;
|
||||
if ( $this->filterSuffix !== '' ) {
|
||||
$name .= '_' . $this->filterSuffix;
|
||||
}
|
||||
return $name . '-' . $this->slug;
|
||||
}
|
||||
|
||||
/**
|
||||
* Store API errors that are generated when checking for updates.
|
||||
*
|
||||
* @internal
|
||||
* @param WP_Error $error
|
||||
* @param array|null $httpResponse
|
||||
* @param string|null $url
|
||||
* @param string|null $slug
|
||||
*/
|
||||
public function collectApiErrors($error, $httpResponse = null, $url = null, $slug = null) {
|
||||
if ( isset($slug) && ($slug !== $this->slug) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->lastRequestApiErrors[] = array(
|
||||
'error' => $error,
|
||||
'httpResponse' => $httpResponse,
|
||||
'url' => $url,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getLastRequestApiErrors() {
|
||||
return $this->lastRequestApiErrors;
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------
|
||||
* PUC filters and filter utilities
|
||||
* -------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
/**
|
||||
* Register a callback for one of the update checker filters.
|
||||
*
|
||||
* Identical to add_filter(), except it automatically adds the "puc_" prefix
|
||||
* and the "-$slug" suffix to the filter name. For example, "request_info_result"
|
||||
* becomes "puc_request_info_result-your_plugin_slug".
|
||||
*
|
||||
* @param string $tag
|
||||
* @param callable $callback
|
||||
* @param int $priority
|
||||
* @param int $acceptedArgs
|
||||
*/
|
||||
public function addFilter($tag, $callback, $priority = 10, $acceptedArgs = 1) {
|
||||
add_filter($this->getUniqueName($tag), $callback, $priority, $acceptedArgs);
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------
|
||||
* Inject updates
|
||||
* -------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
/**
|
||||
* Insert the latest update (if any) into the update list maintained by WP.
|
||||
*
|
||||
* @param stdClass $updates Update list.
|
||||
* @return stdClass Modified update list.
|
||||
*/
|
||||
public function injectUpdate($updates) {
|
||||
//Is there an update to insert?
|
||||
$update = $this->getUpdate();
|
||||
|
||||
if ( !$this->shouldShowUpdates() ) {
|
||||
$update = null;
|
||||
}
|
||||
|
||||
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());
|
||||
} else {
|
||||
//Clean up any stale update info.
|
||||
$updates = $this->removeUpdateFromList($updates);
|
||||
//Add a placeholder item to the "no_update" list to enable auto-update support.
|
||||
//If we don't do this, the option to enable automatic updates will only show up
|
||||
//when an update is available.
|
||||
$updates = $this->addNoUpdateItem($updates);
|
||||
}
|
||||
|
||||
return $updates;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param stdClass|null $updates
|
||||
* @param stdClass|array $updateToAdd
|
||||
* @return stdClass
|
||||
*/
|
||||
protected function addUpdateToList($updates, $updateToAdd) {
|
||||
if ( !is_object($updates) ) {
|
||||
$updates = new stdClass();
|
||||
$updates->response = array();
|
||||
}
|
||||
|
||||
$updates->response[$this->getUpdateListKey()] = $updateToAdd;
|
||||
return $updates;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param stdClass|null $updates
|
||||
* @return stdClass|null
|
||||
*/
|
||||
protected function removeUpdateFromList($updates) {
|
||||
if ( isset($updates, $updates->response) ) {
|
||||
unset($updates->response[$this->getUpdateListKey()]);
|
||||
}
|
||||
return $updates;
|
||||
}
|
||||
|
||||
/**
|
||||
* See this post for more information:
|
||||
* @link https://make.wordpress.org/core/2020/07/30/recommended-usage-of-the-updates-api-to-support-the-auto-updates-ui-for-plugins-and-themes-in-wordpress-5-5/
|
||||
*
|
||||
* @param stdClass|null $updates
|
||||
* @return stdClass
|
||||
*/
|
||||
protected function addNoUpdateItem($updates) {
|
||||
if ( !is_object($updates) ) {
|
||||
$updates = new stdClass();
|
||||
$updates->response = array();
|
||||
$updates->no_update = array();
|
||||
} else if ( !isset($updates->no_update) ) {
|
||||
$updates->no_update = array();
|
||||
}
|
||||
|
||||
$updates->no_update[$this->getUpdateListKey()] = (object) $this->getNoUpdateItemFields();
|
||||
|
||||
return $updates;
|
||||
}
|
||||
|
||||
/**
|
||||
* Subclasses should override this method to add fields that are specific to plugins or themes.
|
||||
* @return array
|
||||
*/
|
||||
protected function getNoUpdateItemFields() {
|
||||
return array(
|
||||
'new_version' => $this->getInstalledVersion(),
|
||||
'url' => '',
|
||||
'package' => '',
|
||||
'requires_php' => '',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the key that will be used when adding updates to the update list that's maintained
|
||||
* by the WordPress core. The list is always an associative array, but the key is different
|
||||
* for plugins and themes.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
abstract protected function getUpdateListKey();
|
||||
|
||||
/**
|
||||
* Should we show available updates?
|
||||
*
|
||||
* Usually the answer is "yes", but there are exceptions. For example, WordPress doesn't
|
||||
* support automatic updates installation for mu-plugins, so PUC usually won't show update
|
||||
* notifications in that case. See the plugin-specific subclass for details.
|
||||
*
|
||||
* Note: This method only applies to updates that are displayed (or not) in the WordPress
|
||||
* admin. It doesn't affect APIs like requestUpdate and getUpdate.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function shouldShowUpdates() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------
|
||||
* JSON-based update API
|
||||
* -------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
/**
|
||||
* Retrieve plugin or theme metadata from the JSON document at $this->metadataUrl.
|
||||
*
|
||||
* @param string $metaClass Parse the JSON as an instance of this class. It must have a static fromJson method.
|
||||
* @param string $filterRoot
|
||||
* @param array $queryArgs Additional query arguments.
|
||||
* @return array [Puc_v4p11_Metadata|null, array|WP_Error] A metadata instance and the value returned by wp_remote_get().
|
||||
*/
|
||||
protected function requestMetadata($metaClass, $filterRoot, $queryArgs = array()) {
|
||||
//Query args to append to the URL. Plugins can add their own by using a filter callback (see addQueryArgFilter()).
|
||||
$queryArgs = array_merge(
|
||||
array(
|
||||
'installed_version' => strval($this->getInstalledVersion()),
|
||||
'php' => phpversion(),
|
||||
'locale' => get_locale(),
|
||||
),
|
||||
$queryArgs
|
||||
);
|
||||
$queryArgs = apply_filters($this->getUniqueName($filterRoot . '_query_args'), $queryArgs);
|
||||
|
||||
//Various options for the wp_remote_get() call. Plugins can filter these, too.
|
||||
$options = array(
|
||||
'timeout' => 10, //seconds
|
||||
'headers' => array(
|
||||
'Accept' => 'application/json',
|
||||
),
|
||||
);
|
||||
$options = apply_filters($this->getUniqueName($filterRoot . '_options'), $options);
|
||||
|
||||
//The metadata file should be at 'http://your-api.com/url/here/$slug/info.json'
|
||||
$url = $this->metadataUrl;
|
||||
if ( !empty($queryArgs) ){
|
||||
$url = add_query_arg($queryArgs, $url);
|
||||
}
|
||||
|
||||
$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;
|
||||
if ( !is_wp_error($status) ){
|
||||
if ( version_compare(PHP_VERSION, '5.3', '>=') && (strpos($metaClass, '\\') === false) ) {
|
||||
$metaClass = __NAMESPACE__ . '\\' . $metaClass;
|
||||
}
|
||||
$metadata = call_user_func(array($metaClass, 'fromJson'), $result['body']);
|
||||
} else {
|
||||
do_action('puc_api_error', $status, $result, $url, $this->slug);
|
||||
$this->triggerError(
|
||||
sprintf('The URL %s does not point to a valid metadata file. ', $url)
|
||||
. $status->get_error_message(),
|
||||
E_USER_WARNING
|
||||
);
|
||||
}
|
||||
|
||||
return array($metadata, $result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if $result is a successful update API response.
|
||||
*
|
||||
* @param array|WP_Error $result
|
||||
* @return true|WP_Error
|
||||
*/
|
||||
protected function validateApiResponse($result) {
|
||||
if ( is_wp_error($result) ) { /** @var WP_Error $result */
|
||||
return new WP_Error($result->get_error_code(), 'WP HTTP Error: ' . $result->get_error_message());
|
||||
}
|
||||
|
||||
if ( !isset($result['response']['code']) ) {
|
||||
return new WP_Error(
|
||||
'puc_no_response_code',
|
||||
'wp_remote_get() returned an unexpected result.'
|
||||
);
|
||||
}
|
||||
|
||||
if ( $result['response']['code'] !== 200 ) {
|
||||
return new WP_Error(
|
||||
'puc_unexpected_response_code',
|
||||
'HTTP response code is ' . $result['response']['code'] . ' (expected: 200)'
|
||||
);
|
||||
}
|
||||
|
||||
if ( empty($result['body']) ) {
|
||||
return new WP_Error('puc_empty_response', 'The metadata file appears to be empty.');
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------
|
||||
* Language packs / Translation updates
|
||||
* -------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
/**
|
||||
* Filter a list of translation updates and return a new list that contains only updates
|
||||
* that apply to the current site.
|
||||
*
|
||||
* @param array $translations
|
||||
* @return array
|
||||
*/
|
||||
protected function filterApplicableTranslations($translations) {
|
||||
$languages = array_flip(array_values(get_available_languages()));
|
||||
$installedTranslations = $this->getInstalledTranslations();
|
||||
|
||||
$applicableTranslations = array();
|
||||
foreach ($translations as $translation) {
|
||||
//Does it match one of the available core languages?
|
||||
$isApplicable = array_key_exists($translation->language, $languages);
|
||||
//Is it more recent than an already-installed translation?
|
||||
if ( isset($installedTranslations[$translation->language]) ) {
|
||||
$updateTimestamp = strtotime($translation->updated);
|
||||
$installedTimestamp = strtotime($installedTranslations[$translation->language]['PO-Revision-Date']);
|
||||
$isApplicable = $updateTimestamp > $installedTimestamp;
|
||||
}
|
||||
|
||||
if ( $isApplicable ) {
|
||||
$applicableTranslations[] = $translation;
|
||||
}
|
||||
}
|
||||
|
||||
return $applicableTranslations;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a list of installed translations for this plugin or theme.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function getInstalledTranslations() {
|
||||
if ( !function_exists('wp_get_installed_translations') ) {
|
||||
return array();
|
||||
}
|
||||
$installedTranslations = wp_get_installed_translations($this->translationType . 's');
|
||||
if ( isset($installedTranslations[$this->directoryName]) ) {
|
||||
$installedTranslations = $installedTranslations[$this->directoryName];
|
||||
} else {
|
||||
$installedTranslations = array();
|
||||
}
|
||||
return $installedTranslations;
|
||||
}
|
||||
|
||||
/**
|
||||
* Insert translation updates into the list maintained by WordPress.
|
||||
*
|
||||
* @param stdClass $updates
|
||||
* @return stdClass
|
||||
*/
|
||||
public function injectTranslationUpdates($updates) {
|
||||
$translationUpdates = $this->getTranslationUpdates();
|
||||
if ( empty($translationUpdates) ) {
|
||||
return $updates;
|
||||
}
|
||||
|
||||
//Being defensive.
|
||||
if ( !is_object($updates) ) {
|
||||
$updates = new stdClass();
|
||||
}
|
||||
if ( !isset($updates->translations) ) {
|
||||
$updates->translations = array();
|
||||
}
|
||||
|
||||
//In case there's a name collision with a plugin or theme hosted on wordpress.org,
|
||||
//remove any preexisting updates that match our thing.
|
||||
$updates->translations = array_values(array_filter(
|
||||
$updates->translations,
|
||||
array($this, 'isNotMyTranslation')
|
||||
));
|
||||
|
||||
//Add our updates to the list.
|
||||
foreach($translationUpdates as $update) {
|
||||
$convertedUpdate = array_merge(
|
||||
array(
|
||||
'type' => $this->translationType,
|
||||
'slug' => $this->directoryName,
|
||||
'autoupdate' => 0,
|
||||
//AFAICT, WordPress doesn't actually use the "version" field for anything.
|
||||
//But lets make sure it's there, just in case.
|
||||
'version' => isset($update->version) ? $update->version : ('1.' . strtotime($update->updated)),
|
||||
),
|
||||
(array)$update
|
||||
);
|
||||
|
||||
$updates->translations[] = $convertedUpdate;
|
||||
}
|
||||
|
||||
return $updates;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a list of available translation updates.
|
||||
*
|
||||
* This method will return an empty array if there are no updates.
|
||||
* Uses cached update data.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getTranslationUpdates() {
|
||||
return $this->updateState->getTranslations();
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove all cached translation updates.
|
||||
*
|
||||
* @see wp_clean_update_cache
|
||||
*/
|
||||
public function clearCachedTranslationUpdates() {
|
||||
$this->updateState->setTranslations(array());
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter callback. Keeps only translations that *don't* match this plugin or theme.
|
||||
*
|
||||
* @param array $translation
|
||||
* @return bool
|
||||
*/
|
||||
protected function isNotMyTranslation($translation) {
|
||||
$isMatch = isset($translation['type'], $translation['slug'])
|
||||
&& ($translation['type'] === $this->translationType)
|
||||
&& ($translation['slug'] === $this->directoryName);
|
||||
|
||||
return !$isMatch;
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------
|
||||
* Fix directory name when installing updates
|
||||
* -------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
/**
|
||||
* Rename the update directory to match the existing plugin/theme directory.
|
||||
*
|
||||
* When WordPress installs a plugin or theme update, it assumes that the ZIP file will contain
|
||||
* exactly one directory, and that the directory name will be the same as the directory where
|
||||
* the plugin or theme is currently installed.
|
||||
*
|
||||
* GitHub and other repositories provide ZIP downloads, but they often use directory names like
|
||||
* "project-branch" or "project-tag-hash". We need to change the name to the actual plugin folder.
|
||||
*
|
||||
* This is a hook callback. Don't call it from a plugin.
|
||||
*
|
||||
* @access protected
|
||||
*
|
||||
* @param string $source The directory to copy to /wp-content/plugins or /wp-content/themes. Usually a subdirectory of $remoteSource.
|
||||
* @param string $remoteSource WordPress has extracted the update to this directory.
|
||||
* @param WP_Upgrader $upgrader
|
||||
* @return string|WP_Error
|
||||
*/
|
||||
public function fixDirectoryName($source, $remoteSource, $upgrader) {
|
||||
global $wp_filesystem;
|
||||
/** @var WP_Filesystem_Base $wp_filesystem */
|
||||
|
||||
//Basic sanity checks.
|
||||
if ( !isset($source, $remoteSource, $upgrader, $upgrader->skin, $wp_filesystem) ) {
|
||||
return $source;
|
||||
}
|
||||
|
||||
//If WordPress is upgrading anything other than our plugin/theme, leave the directory name unchanged.
|
||||
if ( !$this->isBeingUpgraded($upgrader) ) {
|
||||
return $source;
|
||||
}
|
||||
|
||||
//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…',
|
||||
'<span class="code">' . basename($source) . '</span>',
|
||||
'<span class="code">' . $this->directoryName . '</span>'
|
||||
));
|
||||
|
||||
if ( $wp_filesystem->move($source, $correctedSource, true) ) {
|
||||
$upgrader->skin->feedback('Directory successfully renamed.');
|
||||
return $correctedSource;
|
||||
} else {
|
||||
return new WP_Error(
|
||||
'puc-rename-failed',
|
||||
'Unable to rename the update to match the existing directory.'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return $source;
|
||||
}
|
||||
|
||||
/**
|
||||
* Is there an update being installed right now, for this plugin or theme?
|
||||
*
|
||||
* @param WP_Upgrader|null $upgrader The upgrader that's performing the current update.
|
||||
* @return bool
|
||||
*/
|
||||
abstract public function isBeingUpgraded($upgrader = null);
|
||||
|
||||
/**
|
||||
* Check for incorrect update directory structure. An update must contain a single directory,
|
||||
* all other files should be inside that directory.
|
||||
*
|
||||
* @param string $remoteSource Directory path.
|
||||
* @return bool
|
||||
*/
|
||||
protected function isBadDirectoryStructure($remoteSource) {
|
||||
global $wp_filesystem;
|
||||
/** @var WP_Filesystem_Base $wp_filesystem */
|
||||
|
||||
$sourceFiles = $wp_filesystem->dirlist($remoteSource);
|
||||
if ( is_array($sourceFiles) ) {
|
||||
$sourceFiles = array_keys($sourceFiles);
|
||||
$firstFilePath = trailingslashit($remoteSource) . $sourceFiles[0];
|
||||
return (count($sourceFiles) > 1) || (!$wp_filesystem->is_dir($firstFilePath));
|
||||
}
|
||||
|
||||
//Assume it's fine.
|
||||
return false;
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------
|
||||
* DebugBar integration
|
||||
* -------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
/**
|
||||
* Initialize the update checker Debug Bar plugin/add-on thingy.
|
||||
*/
|
||||
public function maybeInitDebugBar() {
|
||||
if ( class_exists('Debug_Bar', false) && file_exists(dirname(__FILE__) . '/DebugBar') ) {
|
||||
$this->debugBarExtension = $this->createDebugBarExtension();
|
||||
}
|
||||
}
|
||||
|
||||
protected function createDebugBarExtension() {
|
||||
return new Puc_v4p11_DebugBar_Extension($this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Display additional configuration details in the Debug Bar panel.
|
||||
*
|
||||
* @param Puc_v4p11_DebugBar_Panel $panel
|
||||
*/
|
||||
public function onDisplayConfiguration($panel) {
|
||||
//Do nothing. Subclasses can use this to add additional info to the panel.
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
endif;
|
199
dependencies/yahnis-elsts/plugin-update-checker/Puc/v4p11/UpgraderStatus.php
vendored
Normal file
199
dependencies/yahnis-elsts/plugin-update-checker/Puc/v4p11/UpgraderStatus.php
vendored
Normal file
@ -0,0 +1,199 @@
|
||||
<?php
|
||||
if ( !class_exists('Puc_v4p11_UpgraderStatus', false) ):
|
||||
|
||||
/**
|
||||
* A utility class that helps figure out which plugin or theme WordPress is upgrading.
|
||||
*
|
||||
* It may seem strange to have a separate class just for that, but the task is surprisingly complicated.
|
||||
* Core classes like Plugin_Upgrader don't expose the plugin file name during an in-progress update (AFAICT).
|
||||
* This class uses a few workarounds and heuristics to get the file name.
|
||||
*/
|
||||
class Puc_v4p11_UpgraderStatus {
|
||||
private $currentType = null; //"plugin" or "theme".
|
||||
private $currentId = null; //Plugin basename or theme directory name.
|
||||
|
||||
public function __construct() {
|
||||
//Keep track of which plugin/theme WordPress is currently upgrading.
|
||||
add_filter('upgrader_pre_install', array($this, 'setUpgradedThing'), 10, 2);
|
||||
add_filter('upgrader_package_options', array($this, 'setUpgradedPluginFromOptions'), 10, 1);
|
||||
add_filter('upgrader_post_install', array($this, 'clearUpgradedThing'), 10, 1);
|
||||
add_action('upgrader_process_complete', array($this, 'clearUpgradedThing'), 10, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Is there and update being installed RIGHT NOW, for a specific plugin?
|
||||
*
|
||||
* Caution: This method is unreliable. WordPress doesn't make it easy to figure out what it is upgrading,
|
||||
* and upgrader implementations are liable to change without notice.
|
||||
*
|
||||
* @param string $pluginFile The plugin to check.
|
||||
* @param WP_Upgrader|null $upgrader The upgrader that's performing the current update.
|
||||
* @return bool True if the plugin identified by $pluginFile is being upgraded.
|
||||
*/
|
||||
public function isPluginBeingUpgraded($pluginFile, $upgrader = null) {
|
||||
return $this->isBeingUpgraded('plugin', $pluginFile, $upgrader);
|
||||
}
|
||||
|
||||
/**
|
||||
* Is there an update being installed for a specific theme?
|
||||
*
|
||||
* @param string $stylesheet Theme directory name.
|
||||
* @param WP_Upgrader|null $upgrader The upgrader that's performing the current update.
|
||||
* @return bool
|
||||
*/
|
||||
public function isThemeBeingUpgraded($stylesheet, $upgrader = null) {
|
||||
return $this->isBeingUpgraded('theme', $stylesheet, $upgrader);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a specific theme or plugin is being upgraded.
|
||||
*
|
||||
* @param string $type
|
||||
* @param string $id
|
||||
* @param Plugin_Upgrader|WP_Upgrader|null $upgrader
|
||||
* @return bool
|
||||
*/
|
||||
protected function isBeingUpgraded($type, $id, $upgrader = null) {
|
||||
if ( isset($upgrader) ) {
|
||||
list($currentType, $currentId) = $this->getThingBeingUpgradedBy($upgrader);
|
||||
if ( $currentType !== null ) {
|
||||
$this->currentType = $currentType;
|
||||
$this->currentId = $currentId;
|
||||
}
|
||||
}
|
||||
return ($this->currentType === $type) && ($this->currentId === $id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Figure out which theme or plugin is being upgraded by a WP_Upgrader instance.
|
||||
*
|
||||
* Returns an array with two items. The first item is the type of the thing that's being
|
||||
* upgraded: "plugin" or "theme". The second item is either the plugin basename or
|
||||
* the theme directory name. If we can't determine what the upgrader is doing, both items
|
||||
* will be NULL.
|
||||
*
|
||||
* Examples:
|
||||
* ['plugin', 'plugin-dir-name/plugin.php']
|
||||
* ['theme', 'theme-dir-name']
|
||||
*
|
||||
* @param Plugin_Upgrader|WP_Upgrader $upgrader
|
||||
* @return array
|
||||
*/
|
||||
private function getThingBeingUpgradedBy($upgrader) {
|
||||
if ( !isset($upgrader, $upgrader->skin) ) {
|
||||
return array(null, null);
|
||||
}
|
||||
|
||||
//Figure out which plugin or theme is being upgraded.
|
||||
$pluginFile = null;
|
||||
$themeDirectoryName = null;
|
||||
|
||||
$skin = $upgrader->skin;
|
||||
if ( isset($skin->theme_info) && ($skin->theme_info instanceof WP_Theme) ) {
|
||||
$themeDirectoryName = $skin->theme_info->get_stylesheet();
|
||||
} elseif ( $skin instanceof Plugin_Upgrader_Skin ) {
|
||||
if ( isset($skin->plugin) && is_string($skin->plugin) && ($skin->plugin !== '') ) {
|
||||
$pluginFile = $skin->plugin;
|
||||
}
|
||||
} elseif ( $skin instanceof Theme_Upgrader_Skin ) {
|
||||
if ( isset($skin->theme) && is_string($skin->theme) && ($skin->theme !== '') ) {
|
||||
$themeDirectoryName = $skin->theme;
|
||||
}
|
||||
} elseif ( isset($skin->plugin_info) && is_array($skin->plugin_info) ) {
|
||||
//This case is tricky because Bulk_Plugin_Upgrader_Skin (etc) doesn't actually store the plugin
|
||||
//filename anywhere. Instead, it has the plugin headers in $plugin_info. So the best we can
|
||||
//do is compare those headers to the headers of installed plugins.
|
||||
$pluginFile = $this->identifyPluginByHeaders($skin->plugin_info);
|
||||
}
|
||||
|
||||
if ( $pluginFile !== null ) {
|
||||
return array('plugin', $pluginFile);
|
||||
} elseif ( $themeDirectoryName !== null ) {
|
||||
return array('theme', $themeDirectoryName);
|
||||
}
|
||||
return array(null, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Identify an installed plugin based on its headers.
|
||||
*
|
||||
* @param array $searchHeaders The plugin file header to look for.
|
||||
* @return string|null Plugin basename ("foo/bar.php"), or NULL if we can't identify the plugin.
|
||||
*/
|
||||
private function identifyPluginByHeaders($searchHeaders) {
|
||||
if ( !function_exists('get_plugins') ){
|
||||
/** @noinspection PhpIncludeInspection */
|
||||
require_once( ABSPATH . '/wp-admin/includes/plugin.php' );
|
||||
}
|
||||
|
||||
$installedPlugins = get_plugins();
|
||||
$matches = array();
|
||||
foreach($installedPlugins as $pluginBasename => $headers) {
|
||||
$diff1 = array_diff_assoc($headers, $searchHeaders);
|
||||
$diff2 = array_diff_assoc($searchHeaders, $headers);
|
||||
if ( empty($diff1) && empty($diff2) ) {
|
||||
$matches[] = $pluginBasename;
|
||||
}
|
||||
}
|
||||
|
||||
//It's possible (though very unlikely) that there could be two plugins with identical
|
||||
//headers. In that case, we can't unambiguously identify the plugin that's being upgraded.
|
||||
if ( count($matches) !== 1 ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return reset($matches);
|
||||
}
|
||||
|
||||
/**
|
||||
* @access private
|
||||
*
|
||||
* @param mixed $input
|
||||
* @param array $hookExtra
|
||||
* @return mixed Returns $input unaltered.
|
||||
*/
|
||||
public function setUpgradedThing($input, $hookExtra) {
|
||||
if ( !empty($hookExtra['plugin']) && is_string($hookExtra['plugin']) ) {
|
||||
$this->currentId = $hookExtra['plugin'];
|
||||
$this->currentType = 'plugin';
|
||||
} elseif ( !empty($hookExtra['theme']) && is_string($hookExtra['theme']) ) {
|
||||
$this->currentId = $hookExtra['theme'];
|
||||
$this->currentType = 'theme';
|
||||
} else {
|
||||
$this->currentType = null;
|
||||
$this->currentId = null;
|
||||
}
|
||||
return $input;
|
||||
}
|
||||
|
||||
/**
|
||||
* @access private
|
||||
*
|
||||
* @param array $options
|
||||
* @return array
|
||||
*/
|
||||
public function setUpgradedPluginFromOptions($options) {
|
||||
if ( isset($options['hook_extra']['plugin']) && is_string($options['hook_extra']['plugin']) ) {
|
||||
$this->currentType = 'plugin';
|
||||
$this->currentId = $options['hook_extra']['plugin'];
|
||||
} else {
|
||||
$this->currentType = null;
|
||||
$this->currentId = null;
|
||||
}
|
||||
return $options;
|
||||
}
|
||||
|
||||
/**
|
||||
* @access private
|
||||
*
|
||||
* @param mixed $input
|
||||
* @return mixed Returns $input unaltered.
|
||||
*/
|
||||
public function clearUpgradedThing($input = null) {
|
||||
$this->currentId = null;
|
||||
$this->currentType = null;
|
||||
return $input;
|
||||
}
|
||||
}
|
||||
|
||||
endif;
|
69
dependencies/yahnis-elsts/plugin-update-checker/Puc/v4p11/Utils.php
vendored
Normal file
69
dependencies/yahnis-elsts/plugin-update-checker/Puc/v4p11/Utils.php
vendored
Normal file
@ -0,0 +1,69 @@
|
||||
<?php
|
||||
|
||||
if ( !class_exists('Puc_v4p11_Utils', false) ):
|
||||
|
||||
class Puc_v4p11_Utils {
|
||||
/**
|
||||
* Get a value from a nested array or object based on a path.
|
||||
*
|
||||
* @param array|object|null $collection Get an entry from this array.
|
||||
* @param array|string $path A list of array keys in hierarchy order, or a string path like "foo.bar.baz".
|
||||
* @param mixed $default The value to return if the specified path is not found.
|
||||
* @param string $separator Path element separator. Only applies to string paths.
|
||||
* @return mixed
|
||||
*/
|
||||
public static function get($collection, $path, $default = null, $separator = '.') {
|
||||
if ( is_string($path) ) {
|
||||
$path = explode($separator, $path);
|
||||
}
|
||||
|
||||
//Follow the $path into $input as far as possible.
|
||||
$currentValue = $collection;
|
||||
foreach ($path as $node) {
|
||||
if ( is_array($currentValue) && isset($currentValue[$node]) ) {
|
||||
$currentValue = $currentValue[$node];
|
||||
} else if ( is_object($currentValue) && isset($currentValue->$node) ) {
|
||||
$currentValue = $currentValue->$node;
|
||||
} else {
|
||||
return $default;
|
||||
}
|
||||
}
|
||||
|
||||
return $currentValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the first array element that is not empty.
|
||||
*
|
||||
* @param array $values
|
||||
* @param mixed|null $default Returns this value if there are no non-empty elements.
|
||||
* @return mixed|null
|
||||
*/
|
||||
public static function findNotEmpty($values, $default = null) {
|
||||
if ( empty($values) ) {
|
||||
return $default;
|
||||
}
|
||||
|
||||
foreach ($values as $value) {
|
||||
if ( !empty($value) ) {
|
||||
return $value;
|
||||
}
|
||||
}
|
||||
|
||||
return $default;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the input string starts with the specified prefix.
|
||||
*
|
||||
* @param string $input
|
||||
* @param string $prefix
|
||||
* @return bool
|
||||
*/
|
||||
public static function startsWith($input, $prefix) {
|
||||
$length = strlen($prefix);
|
||||
return (substr($input, 0, $length) === $prefix);
|
||||
}
|
||||
}
|
||||
|
||||
endif;
|
302
dependencies/yahnis-elsts/plugin-update-checker/Puc/v4p11/Vcs/Api.php
vendored
Normal file
302
dependencies/yahnis-elsts/plugin-update-checker/Puc/v4p11/Vcs/Api.php
vendored
Normal file
@ -0,0 +1,302 @@
|
||||
<?php
|
||||
if ( !class_exists('Puc_v4p11_Vcs_Api') ):
|
||||
|
||||
abstract class Puc_v4p11_Vcs_Api {
|
||||
protected $tagNameProperty = 'name';
|
||||
protected $slug = '';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $repositoryUrl = '';
|
||||
|
||||
/**
|
||||
* @var mixed Authentication details for private repositories. Format depends on service.
|
||||
*/
|
||||
protected $credentials = null;
|
||||
|
||||
/**
|
||||
* @var string The filter tag that's used to filter options passed to wp_remote_get.
|
||||
* For example, "puc_request_info_options-slug" or "puc_request_update_options_theme-slug".
|
||||
*/
|
||||
protected $httpFilterName = '';
|
||||
|
||||
/**
|
||||
* @var string|null
|
||||
*/
|
||||
protected $localDirectory = null;
|
||||
|
||||
/**
|
||||
* Puc_v4p11_Vcs_Api constructor.
|
||||
*
|
||||
* @param string $repositoryUrl
|
||||
* @param array|string|null $credentials
|
||||
*/
|
||||
public function __construct($repositoryUrl, $credentials = null) {
|
||||
$this->repositoryUrl = $repositoryUrl;
|
||||
$this->setAuthentication($credentials);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getRepositoryUrl() {
|
||||
return $this->repositoryUrl;
|
||||
}
|
||||
|
||||
/**
|
||||
* Figure out which reference (i.e tag or branch) contains the latest version.
|
||||
*
|
||||
* @param string $configBranch Start looking in this branch.
|
||||
* @return null|Puc_v4p11_Vcs_Reference
|
||||
*/
|
||||
abstract public function chooseReference($configBranch);
|
||||
|
||||
/**
|
||||
* Get the readme.txt file from the remote repository and parse it
|
||||
* according to the plugin readme standard.
|
||||
*
|
||||
* @param string $ref Tag or branch name.
|
||||
* @return array Parsed readme.
|
||||
*/
|
||||
public function getRemoteReadme($ref = 'master') {
|
||||
$fileContents = $this->getRemoteFile($this->getLocalReadmeName(), $ref);
|
||||
if ( empty($fileContents) ) {
|
||||
return array();
|
||||
}
|
||||
|
||||
$parser = new PucReadmeParser();
|
||||
return $parser->parse_readme_contents($fileContents);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the case-sensitive name of the local readme.txt file.
|
||||
*
|
||||
* In most cases it should just be called "readme.txt", but some plugins call it "README.txt",
|
||||
* "README.TXT", or even "Readme.txt". Most VCS are case-sensitive so we need to know the correct
|
||||
* capitalization.
|
||||
*
|
||||
* Defaults to "readme.txt" (all lowercase).
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getLocalReadmeName() {
|
||||
static $fileName = null;
|
||||
if ( $fileName !== null ) {
|
||||
return $fileName;
|
||||
}
|
||||
|
||||
$fileName = 'readme.txt';
|
||||
if ( isset($this->localDirectory) ) {
|
||||
$files = scandir($this->localDirectory);
|
||||
if ( !empty($files) ) {
|
||||
foreach ($files as $possibleFileName) {
|
||||
if ( strcasecmp($possibleFileName, 'readme.txt') === 0 ) {
|
||||
$fileName = $possibleFileName;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return $fileName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a branch.
|
||||
*
|
||||
* @param string $branchName
|
||||
* @return Puc_v4p11_Vcs_Reference|null
|
||||
*/
|
||||
abstract public function getBranch($branchName);
|
||||
|
||||
/**
|
||||
* Get a specific tag.
|
||||
*
|
||||
* @param string $tagName
|
||||
* @return Puc_v4p11_Vcs_Reference|null
|
||||
*/
|
||||
abstract public function getTag($tagName);
|
||||
|
||||
/**
|
||||
* Get the tag that looks like the highest version number.
|
||||
* (Implementations should skip pre-release versions if possible.)
|
||||
*
|
||||
* @return Puc_v4p11_Vcs_Reference|null
|
||||
*/
|
||||
abstract public function getLatestTag();
|
||||
|
||||
/**
|
||||
* Check if a tag name string looks like a version number.
|
||||
*
|
||||
* @param string $name
|
||||
* @return bool
|
||||
*/
|
||||
protected function looksLikeVersion($name) {
|
||||
//Tag names may be prefixed with "v", e.g. "v1.2.3".
|
||||
$name = ltrim($name, 'v');
|
||||
|
||||
//The version string must start with a number.
|
||||
if ( !is_numeric(substr($name, 0, 1)) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
//The goal is to accept any SemVer-compatible or "PHP-standardized" version number.
|
||||
return (preg_match('@^(\d{1,5}?)(\.\d{1,10}?){0,4}?($|[abrdp+_\-]|\s)@i', $name) === 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a tag appears to be named like a version number.
|
||||
*
|
||||
* @param stdClass $tag
|
||||
* @return bool
|
||||
*/
|
||||
protected function isVersionTag($tag) {
|
||||
$property = $this->tagNameProperty;
|
||||
return isset($tag->$property) && $this->looksLikeVersion($tag->$property);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sort a list of tags as if they were version numbers.
|
||||
* Tags that don't look like version number will be removed.
|
||||
*
|
||||
* @param stdClass[] $tags Array of tag objects.
|
||||
* @return stdClass[] Filtered array of tags sorted in descending order.
|
||||
*/
|
||||
protected function sortTagsByVersion($tags) {
|
||||
//Keep only those tags that look like version numbers.
|
||||
$versionTags = array_filter($tags, array($this, 'isVersionTag'));
|
||||
//Sort them in descending order.
|
||||
usort($versionTags, array($this, 'compareTagNames'));
|
||||
|
||||
return $versionTags;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compare two tags as if they were version number.
|
||||
*
|
||||
* @param stdClass $tag1 Tag object.
|
||||
* @param stdClass $tag2 Another tag object.
|
||||
* @return int
|
||||
*/
|
||||
protected function compareTagNames($tag1, $tag2) {
|
||||
$property = $this->tagNameProperty;
|
||||
if ( !isset($tag1->$property) ) {
|
||||
return 1;
|
||||
}
|
||||
if ( !isset($tag2->$property) ) {
|
||||
return -1;
|
||||
}
|
||||
return -version_compare(ltrim($tag1->$property, 'v'), ltrim($tag2->$property, 'v'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the contents of a file from a specific branch or tag.
|
||||
*
|
||||
* @param string $path File name.
|
||||
* @param string $ref
|
||||
* @return null|string Either the contents of the file, or null if the file doesn't exist or there's an error.
|
||||
*/
|
||||
abstract public function getRemoteFile($path, $ref = 'master');
|
||||
|
||||
/**
|
||||
* Get the timestamp of the latest commit that changed the specified branch or tag.
|
||||
*
|
||||
* @param string $ref Reference name (e.g. branch or tag).
|
||||
* @return string|null
|
||||
*/
|
||||
abstract public function getLatestCommitTime($ref);
|
||||
|
||||
/**
|
||||
* Get the contents of the changelog file from the repository.
|
||||
*
|
||||
* @param string $ref
|
||||
* @param string $localDirectory Full path to the local plugin or theme directory.
|
||||
* @return null|string The HTML contents of the changelog.
|
||||
*/
|
||||
public function getRemoteChangelog($ref, $localDirectory) {
|
||||
$filename = $this->findChangelogName($localDirectory);
|
||||
if ( empty($filename) ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$changelog = $this->getRemoteFile($filename, $ref);
|
||||
if ( $changelog === null ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/** @noinspection PhpUndefinedClassInspection */
|
||||
return Parsedown::instance()->text($changelog);
|
||||
}
|
||||
|
||||
/**
|
||||
* Guess the name of the changelog file.
|
||||
*
|
||||
* @param string $directory
|
||||
* @return string|null
|
||||
*/
|
||||
protected function findChangelogName($directory = null) {
|
||||
if ( !isset($directory) ) {
|
||||
$directory = $this->localDirectory;
|
||||
}
|
||||
if ( empty($directory) || !is_dir($directory) || ($directory === '.') ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$possibleNames = array('CHANGES.md', 'CHANGELOG.md', 'changes.md', 'changelog.md');
|
||||
$files = scandir($directory);
|
||||
$foundNames = array_intersect($possibleNames, $files);
|
||||
|
||||
if ( !empty($foundNames) ) {
|
||||
return reset($foundNames);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set authentication credentials.
|
||||
*
|
||||
* @param $credentials
|
||||
*/
|
||||
public function setAuthentication($credentials) {
|
||||
$this->credentials = $credentials;
|
||||
}
|
||||
|
||||
public function isAuthenticationEnabled() {
|
||||
return !empty($this->credentials);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $url
|
||||
* @return string
|
||||
*/
|
||||
public function signDownloadUrl($url) {
|
||||
return $url;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $filterName
|
||||
*/
|
||||
public function setHttpFilterName($filterName) {
|
||||
$this->httpFilterName = $filterName;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $directory
|
||||
*/
|
||||
public function setLocalDirectory($directory) {
|
||||
if ( empty($directory) || !is_dir($directory) || ($directory === '.') ) {
|
||||
$this->localDirectory = null;
|
||||
} else {
|
||||
$this->localDirectory = $directory;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $slug
|
||||
*/
|
||||
public function setSlug($slug) {
|
||||
$this->slug = $slug;
|
||||
}
|
||||
}
|
||||
|
||||
endif;
|
27
dependencies/yahnis-elsts/plugin-update-checker/Puc/v4p11/Vcs/BaseChecker.php
vendored
Normal file
27
dependencies/yahnis-elsts/plugin-update-checker/Puc/v4p11/Vcs/BaseChecker.php
vendored
Normal file
@ -0,0 +1,27 @@
|
||||
<?php
|
||||
if ( !interface_exists('Puc_v4p11_Vcs_BaseChecker', false) ):
|
||||
|
||||
interface Puc_v4p11_Vcs_BaseChecker {
|
||||
/**
|
||||
* Set the repository branch to use for updates. Defaults to 'master'.
|
||||
*
|
||||
* @param string $branch
|
||||
* @return $this
|
||||
*/
|
||||
public function setBranch($branch);
|
||||
|
||||
/**
|
||||
* Set authentication credentials.
|
||||
*
|
||||
* @param array|string $credentials
|
||||
* @return $this
|
||||
*/
|
||||
public function setAuthentication($credentials);
|
||||
|
||||
/**
|
||||
* @return Puc_v4p11_Vcs_Api
|
||||
*/
|
||||
public function getVcsApi();
|
||||
}
|
||||
|
||||
endif;
|
265
dependencies/yahnis-elsts/plugin-update-checker/Puc/v4p11/Vcs/BitBucketApi.php
vendored
Normal file
265
dependencies/yahnis-elsts/plugin-update-checker/Puc/v4p11/Vcs/BitBucketApi.php
vendored
Normal file
@ -0,0 +1,265 @@
|
||||
<?php
|
||||
if ( !class_exists('Puc_v4p11_Vcs_BitBucketApi', false) ):
|
||||
|
||||
class Puc_v4p11_Vcs_BitBucketApi extends Puc_v4p11_Vcs_Api {
|
||||
/**
|
||||
* @var Puc_v4p11_OAuthSignature
|
||||
*/
|
||||
private $oauth = null;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $username;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $repository;
|
||||
|
||||
public function __construct($repositoryUrl, $credentials = array()) {
|
||||
$path = parse_url($repositoryUrl, PHP_URL_PATH);
|
||||
if ( preg_match('@^/?(?P<username>[^/]+?)/(?P<repository>[^/#?&]+?)/?$@', $path, $matches) ) {
|
||||
$this->username = $matches['username'];
|
||||
$this->repository = $matches['repository'];
|
||||
} else {
|
||||
throw new InvalidArgumentException('Invalid BitBucket repository URL: "' . $repositoryUrl . '"');
|
||||
}
|
||||
|
||||
parent::__construct($repositoryUrl, $credentials);
|
||||
}
|
||||
|
||||
/**
|
||||
* Figure out which reference (i.e tag or branch) contains the latest version.
|
||||
*
|
||||
* @param string $configBranch Start looking in this branch.
|
||||
* @return null|Puc_v4p11_Vcs_Reference
|
||||
*/
|
||||
public function chooseReference($configBranch) {
|
||||
$updateSource = null;
|
||||
|
||||
//Check if there's a "Stable tag: 1.2.3" header that points to a valid tag.
|
||||
$updateSource = $this->getStableTag($configBranch);
|
||||
|
||||
//Look for version-like tags.
|
||||
if ( !$updateSource && ($configBranch === 'master') ) {
|
||||
$updateSource = $this->getLatestTag();
|
||||
}
|
||||
//If all else fails, use the specified branch itself.
|
||||
if ( !$updateSource ) {
|
||||
$updateSource = $this->getBranch($configBranch);
|
||||
}
|
||||
|
||||
return $updateSource;
|
||||
}
|
||||
|
||||
public function getBranch($branchName) {
|
||||
$branch = $this->api('/refs/branches/' . $branchName);
|
||||
if ( is_wp_error($branch) || empty($branch) ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return new Puc_v4p11_Vcs_Reference(array(
|
||||
'name' => $branch->name,
|
||||
'updated' => $branch->target->date,
|
||||
'downloadUrl' => $this->getDownloadUrl($branch->name),
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a specific tag.
|
||||
*
|
||||
* @param string $tagName
|
||||
* @return Puc_v4p11_Vcs_Reference|null
|
||||
*/
|
||||
public function getTag($tagName) {
|
||||
$tag = $this->api('/refs/tags/' . $tagName);
|
||||
if ( is_wp_error($tag) || empty($tag) ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return new Puc_v4p11_Vcs_Reference(array(
|
||||
'name' => $tag->name,
|
||||
'version' => ltrim($tag->name, 'v'),
|
||||
'updated' => $tag->target->date,
|
||||
'downloadUrl' => $this->getDownloadUrl($tag->name),
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the tag that looks like the highest version number.
|
||||
*
|
||||
* @return Puc_v4p11_Vcs_Reference|null
|
||||
*/
|
||||
public function getLatestTag() {
|
||||
$tags = $this->api('/refs/tags?sort=-target.date');
|
||||
if ( !isset($tags, $tags->values) || !is_array($tags->values) ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
//Filter and sort the list of tags.
|
||||
$versionTags = $this->sortTagsByVersion($tags->values);
|
||||
|
||||
//Return the first result.
|
||||
if ( !empty($versionTags) ) {
|
||||
$tag = $versionTags[0];
|
||||
return new Puc_v4p11_Vcs_Reference(array(
|
||||
'name' => $tag->name,
|
||||
'version' => ltrim($tag->name, 'v'),
|
||||
'updated' => $tag->target->date,
|
||||
'downloadUrl' => $this->getDownloadUrl($tag->name),
|
||||
));
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the tag/ref specified by the "Stable tag" header in the readme.txt of a given branch.
|
||||
*
|
||||
* @param string $branch
|
||||
* @return null|Puc_v4p11_Vcs_Reference
|
||||
*/
|
||||
protected function getStableTag($branch) {
|
||||
$remoteReadme = $this->getRemoteReadme($branch);
|
||||
if ( !empty($remoteReadme['stable_tag']) ) {
|
||||
$tag = $remoteReadme['stable_tag'];
|
||||
|
||||
//You can explicitly opt out of using tags by setting "Stable tag" to
|
||||
//"trunk" or the name of the current branch.
|
||||
if ( ($tag === $branch) || ($tag === 'trunk') ) {
|
||||
return $this->getBranch($branch);
|
||||
}
|
||||
|
||||
return $this->getTag($tag);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $ref
|
||||
* @return string
|
||||
*/
|
||||
protected function getDownloadUrl($ref) {
|
||||
return sprintf(
|
||||
'https://bitbucket.org/%s/%s/get/%s.zip',
|
||||
$this->username,
|
||||
$this->repository,
|
||||
$ref
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the contents of a file from a specific branch or tag.
|
||||
*
|
||||
* @param string $path File name.
|
||||
* @param string $ref
|
||||
* @return null|string Either the contents of the file, or null if the file doesn't exist or there's an error.
|
||||
*/
|
||||
public function getRemoteFile($path, $ref = 'master') {
|
||||
$response = $this->api('src/' . $ref . '/' . ltrim($path));
|
||||
if ( is_wp_error($response) || !is_string($response) ) {
|
||||
return null;
|
||||
}
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the timestamp of the latest commit that changed the specified branch or tag.
|
||||
*
|
||||
* @param string $ref Reference name (e.g. branch or tag).
|
||||
* @return string|null
|
||||
*/
|
||||
public function getLatestCommitTime($ref) {
|
||||
$response = $this->api('commits/' . $ref);
|
||||
if ( isset($response->values, $response->values[0], $response->values[0]->date) ) {
|
||||
return $response->values[0]->date;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform a BitBucket API 2.0 request.
|
||||
*
|
||||
* @param string $url
|
||||
* @param string $version
|
||||
* @return mixed|WP_Error
|
||||
*/
|
||||
public function api($url, $version = '2.0') {
|
||||
$url = ltrim($url, '/');
|
||||
$isSrcResource = Puc_v4p11_Utils::startsWith($url, 'src/');
|
||||
|
||||
$url = implode('/', array(
|
||||
'https://api.bitbucket.org',
|
||||
$version,
|
||||
'repositories',
|
||||
$this->username,
|
||||
$this->repository,
|
||||
$url
|
||||
));
|
||||
$baseUrl = $url;
|
||||
|
||||
if ( $this->oauth ) {
|
||||
$url = $this->oauth->sign($url,'GET');
|
||||
}
|
||||
|
||||
$options = array('timeout' => 10);
|
||||
if ( !empty($this->httpFilterName) ) {
|
||||
$options = apply_filters($this->httpFilterName, $options);
|
||||
}
|
||||
$response = wp_remote_get($url, $options);
|
||||
if ( is_wp_error($response) ) {
|
||||
do_action('puc_api_error', $response, null, $url, $this->slug);
|
||||
return $response;
|
||||
}
|
||||
|
||||
$code = wp_remote_retrieve_response_code($response);
|
||||
$body = wp_remote_retrieve_body($response);
|
||||
if ( $code === 200 ) {
|
||||
if ( $isSrcResource ) {
|
||||
//Most responses are JSON-encoded, but src resources just
|
||||
//return raw file contents.
|
||||
$document = $body;
|
||||
} else {
|
||||
$document = json_decode($body);
|
||||
}
|
||||
return $document;
|
||||
}
|
||||
|
||||
$error = new WP_Error(
|
||||
'puc-bitbucket-http-error',
|
||||
sprintf('BitBucket API error. Base URL: "%s", HTTP status code: %d.', $baseUrl, $code)
|
||||
);
|
||||
do_action('puc_api_error', $error, $response, $url, $this->slug);
|
||||
|
||||
return $error;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $credentials
|
||||
*/
|
||||
public function setAuthentication($credentials) {
|
||||
parent::setAuthentication($credentials);
|
||||
|
||||
if ( !empty($credentials) && !empty($credentials['consumer_key']) ) {
|
||||
$this->oauth = new Puc_v4p11_OAuthSignature(
|
||||
$credentials['consumer_key'],
|
||||
$credentials['consumer_secret']
|
||||
);
|
||||
} 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;
|
441
dependencies/yahnis-elsts/plugin-update-checker/Puc/v4p11/Vcs/GitHubApi.php
vendored
Normal file
441
dependencies/yahnis-elsts/plugin-update-checker/Puc/v4p11/Vcs/GitHubApi.php
vendored
Normal file
@ -0,0 +1,441 @@
|
||||
<?php
|
||||
|
||||
if ( !class_exists('Puc_v4p11_Vcs_GitHubApi', false) ):
|
||||
|
||||
class Puc_v4p11_Vcs_GitHubApi extends Puc_v4p11_Vcs_Api {
|
||||
/**
|
||||
* @var string GitHub username.
|
||||
*/
|
||||
protected $userName;
|
||||
/**
|
||||
* @var string GitHub repository name.
|
||||
*/
|
||||
protected $repositoryName;
|
||||
|
||||
/**
|
||||
* @var string Either a fully qualified repository URL, or just "user/repo-name".
|
||||
*/
|
||||
protected $repositoryUrl;
|
||||
|
||||
/**
|
||||
* @var string GitHub authentication token. Optional.
|
||||
*/
|
||||
protected $accessToken;
|
||||
|
||||
/**
|
||||
* @var bool Whether to download release assets instead of the auto-generated source code archives.
|
||||
*/
|
||||
protected $releaseAssetsEnabled = false;
|
||||
|
||||
/**
|
||||
* @var string|null Regular expression that's used to filter release assets by name. Optional.
|
||||
*/
|
||||
protected $assetFilterRegex = null;
|
||||
|
||||
/**
|
||||
* @var string|null The unchanging part of a release asset URL. Used to identify download attempts.
|
||||
*/
|
||||
protected $assetApiBaseUrl = null;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
private $downloadFilterAdded = false;
|
||||
|
||||
public function __construct($repositoryUrl, $accessToken = null) {
|
||||
$path = parse_url($repositoryUrl, PHP_URL_PATH);
|
||||
if ( preg_match('@^/?(?P<username>[^/]+?)/(?P<repository>[^/#?&]+?)/?$@', $path, $matches) ) {
|
||||
$this->userName = $matches['username'];
|
||||
$this->repositoryName = $matches['repository'];
|
||||
} else {
|
||||
throw new InvalidArgumentException('Invalid GitHub repository URL: "' . $repositoryUrl . '"');
|
||||
}
|
||||
|
||||
parent::__construct($repositoryUrl, $accessToken);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the latest release from GitHub.
|
||||
*
|
||||
* @return Puc_v4p11_Vcs_Reference|null
|
||||
*/
|
||||
public function getLatestRelease() {
|
||||
$release = $this->api('/repos/:user/:repo/releases/latest');
|
||||
if ( is_wp_error($release) || !is_object($release) || !isset($release->tag_name) ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$reference = new Puc_v4p11_Vcs_Reference(array(
|
||||
'name' => $release->tag_name,
|
||||
'version' => ltrim($release->tag_name, 'v'), //Remove the "v" prefix from "v1.2.3".
|
||||
'downloadUrl' => $release->zipball_url,
|
||||
'updated' => $release->created_at,
|
||||
'apiResponse' => $release,
|
||||
));
|
||||
|
||||
if ( isset($release->assets[0]) ) {
|
||||
$reference->downloadCount = $release->assets[0]->download_count;
|
||||
}
|
||||
|
||||
if ( $this->releaseAssetsEnabled && isset($release->assets, $release->assets[0]) ) {
|
||||
//Use the first release asset that matches the specified regular expression.
|
||||
$matchingAssets = array_filter($release->assets, array($this, 'matchesAssetFilter'));
|
||||
if ( !empty($matchingAssets) ) {
|
||||
if ( $this->isAuthenticationEnabled() ) {
|
||||
/**
|
||||
* Keep in mind that we'll need to add an "Accept" header to download this asset.
|
||||
*
|
||||
* @see setUpdateDownloadHeaders()
|
||||
*/
|
||||
$reference->downloadUrl = $matchingAssets[0]->url;
|
||||
} else {
|
||||
//It seems that browser_download_url only works for public repositories.
|
||||
//Using an access_token doesn't help. Maybe OAuth would work?
|
||||
$reference->downloadUrl = $matchingAssets[0]->browser_download_url;
|
||||
}
|
||||
|
||||
$reference->downloadCount = $matchingAssets[0]->download_count;
|
||||
}
|
||||
}
|
||||
|
||||
if ( !empty($release->body) ) {
|
||||
/** @noinspection PhpUndefinedClassInspection */
|
||||
$reference->changelog = Parsedown::instance()->text($release->body);
|
||||
}
|
||||
|
||||
return $reference;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the tag that looks like the highest version number.
|
||||
*
|
||||
* @return Puc_v4p11_Vcs_Reference|null
|
||||
*/
|
||||
public function getLatestTag() {
|
||||
$tags = $this->api('/repos/:user/:repo/tags');
|
||||
|
||||
if ( is_wp_error($tags) || !is_array($tags) ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$versionTags = $this->sortTagsByVersion($tags);
|
||||
if ( empty($versionTags) ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$tag = $versionTags[0];
|
||||
return new Puc_v4p11_Vcs_Reference(array(
|
||||
'name' => $tag->name,
|
||||
'version' => ltrim($tag->name, 'v'),
|
||||
'downloadUrl' => $tag->zipball_url,
|
||||
'apiResponse' => $tag,
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a branch by name.
|
||||
*
|
||||
* @param string $branchName
|
||||
* @return null|Puc_v4p11_Vcs_Reference
|
||||
*/
|
||||
public function getBranch($branchName) {
|
||||
$branch = $this->api('/repos/:user/:repo/branches/' . $branchName);
|
||||
if ( is_wp_error($branch) || empty($branch) ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$reference = new Puc_v4p11_Vcs_Reference(array(
|
||||
'name' => $branch->name,
|
||||
'downloadUrl' => $this->buildArchiveDownloadUrl($branch->name),
|
||||
'apiResponse' => $branch,
|
||||
));
|
||||
|
||||
if ( isset($branch->commit, $branch->commit->commit, $branch->commit->commit->author->date) ) {
|
||||
$reference->updated = $branch->commit->commit->author->date;
|
||||
}
|
||||
|
||||
return $reference;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the latest commit that changed the specified file.
|
||||
*
|
||||
* @param string $filename
|
||||
* @param string $ref Reference name (e.g. branch or tag).
|
||||
* @return StdClass|null
|
||||
*/
|
||||
public function getLatestCommit($filename, $ref = 'master') {
|
||||
$commits = $this->api(
|
||||
'/repos/:user/:repo/commits',
|
||||
array(
|
||||
'path' => $filename,
|
||||
'sha' => $ref,
|
||||
)
|
||||
);
|
||||
if ( !is_wp_error($commits) && isset($commits[0]) ) {
|
||||
return $commits[0];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the timestamp of the latest commit that changed the specified branch or tag.
|
||||
*
|
||||
* @param string $ref Reference name (e.g. branch or tag).
|
||||
* @return string|null
|
||||
*/
|
||||
public function getLatestCommitTime($ref) {
|
||||
$commits = $this->api('/repos/:user/:repo/commits', array('sha' => $ref));
|
||||
if ( !is_wp_error($commits) && isset($commits[0]) ) {
|
||||
return $commits[0]->commit->author->date;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform a GitHub API request.
|
||||
*
|
||||
* @param string $url
|
||||
* @param array $queryParams
|
||||
* @return mixed|WP_Error
|
||||
*/
|
||||
protected function api($url, $queryParams = array()) {
|
||||
$baseUrl = $url;
|
||||
$url = $this->buildApiUrl($url, $queryParams);
|
||||
|
||||
$options = array('timeout' => 10);
|
||||
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);
|
||||
if ( is_wp_error($response) ) {
|
||||
do_action('puc_api_error', $response, null, $url, $this->slug);
|
||||
return $response;
|
||||
}
|
||||
|
||||
$code = wp_remote_retrieve_response_code($response);
|
||||
$body = wp_remote_retrieve_body($response);
|
||||
if ( $code === 200 ) {
|
||||
$document = json_decode($body);
|
||||
return $document;
|
||||
}
|
||||
|
||||
$error = new WP_Error(
|
||||
'puc-github-http-error',
|
||||
sprintf('GitHub API error. Base URL: "%s", HTTP status code: %d.', $baseUrl, $code)
|
||||
);
|
||||
do_action('puc_api_error', $error, $response, $url, $this->slug);
|
||||
|
||||
return $error;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a fully qualified URL for an API request.
|
||||
*
|
||||
* @param string $url
|
||||
* @param array $queryParams
|
||||
* @return string
|
||||
*/
|
||||
protected function buildApiUrl($url, $queryParams) {
|
||||
$variables = array(
|
||||
'user' => $this->userName,
|
||||
'repo' => $this->repositoryName,
|
||||
);
|
||||
foreach ($variables as $name => $value) {
|
||||
$url = str_replace('/:' . $name, '/' . urlencode($value), $url);
|
||||
}
|
||||
$url = 'https://api.github.com' . $url;
|
||||
|
||||
if ( !empty($queryParams) ) {
|
||||
$url = add_query_arg($queryParams, $url);
|
||||
}
|
||||
|
||||
return $url;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the contents of a file from a specific branch or tag.
|
||||
*
|
||||
* @param string $path File name.
|
||||
* @param string $ref
|
||||
* @return null|string Either the contents of the file, or null if the file doesn't exist or there's an error.
|
||||
*/
|
||||
public function getRemoteFile($path, $ref = 'master') {
|
||||
$apiUrl = '/repos/:user/:repo/contents/' . $path;
|
||||
$response = $this->api($apiUrl, array('ref' => $ref));
|
||||
|
||||
if ( is_wp_error($response) || !isset($response->content) || ($response->encoding !== 'base64') ) {
|
||||
return null;
|
||||
}
|
||||
return base64_decode($response->content);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a URL to download a ZIP archive of the specified branch/tag/etc.
|
||||
*
|
||||
* @param string $ref
|
||||
* @return string
|
||||
*/
|
||||
public function buildArchiveDownloadUrl($ref = 'master') {
|
||||
$url = sprintf(
|
||||
'https://api.github.com/repos/%1$s/%2$s/zipball/%3$s',
|
||||
urlencode($this->userName),
|
||||
urlencode($this->repositoryName),
|
||||
urlencode($ref)
|
||||
);
|
||||
return $url;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a specific tag.
|
||||
*
|
||||
* @param string $tagName
|
||||
* @return void
|
||||
*/
|
||||
public function getTag($tagName) {
|
||||
//The current GitHub update checker doesn't use getTag, so I didn't bother to implement it.
|
||||
throw new LogicException('The ' . __METHOD__ . ' method is not implemented and should not be used.');
|
||||
}
|
||||
|
||||
public function setAuthentication($credentials) {
|
||||
parent::setAuthentication($credentials);
|
||||
$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+
|
||||
}
|
||||
|
||||
/**
|
||||
* Figure out which reference (i.e tag or branch) contains the latest version.
|
||||
*
|
||||
* @param string $configBranch Start looking in this branch.
|
||||
* @return null|Puc_v4p11_Vcs_Reference
|
||||
*/
|
||||
public function chooseReference($configBranch) {
|
||||
$updateSource = null;
|
||||
|
||||
if ( $configBranch === 'master' ) {
|
||||
//Use the latest release.
|
||||
$updateSource = $this->getLatestRelease();
|
||||
if ( $updateSource === null ) {
|
||||
//Failing that, use the tag with the highest version number.
|
||||
$updateSource = $this->getLatestTag();
|
||||
}
|
||||
}
|
||||
//Alternatively, just use the branch itself.
|
||||
if ( empty($updateSource) ) {
|
||||
$updateSource = $this->getBranch($configBranch);
|
||||
}
|
||||
|
||||
return $updateSource;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable updating via release assets.
|
||||
*
|
||||
* If the latest release contains no usable assets, the update checker
|
||||
* will fall back to using the automatically generated ZIP archive.
|
||||
*
|
||||
* Private repositories will only work with WordPress 3.7 or later.
|
||||
*
|
||||
* @param string|null $fileNameRegex Optional. Use only those assets where the file name matches this regex.
|
||||
*/
|
||||
public function enableReleaseAssets($fileNameRegex = null) {
|
||||
$this->releaseAssetsEnabled = true;
|
||||
$this->assetFilterRegex = $fileNameRegex;
|
||||
$this->assetApiBaseUrl = sprintf(
|
||||
'//api.github.com/repos/%1$s/%2$s/releases/assets/',
|
||||
$this->userName,
|
||||
$this->repositoryName
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Does this asset match the file name regex?
|
||||
*
|
||||
* @param stdClass $releaseAsset
|
||||
* @return bool
|
||||
*/
|
||||
protected function matchesAssetFilter($releaseAsset) {
|
||||
if ( $this->assetFilterRegex === null ) {
|
||||
//The default is to accept all assets.
|
||||
return true;
|
||||
}
|
||||
return isset($releaseAsset->name) && preg_match($this->assetFilterRegex, $releaseAsset->name);
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* @param bool $result
|
||||
* @return bool
|
||||
*/
|
||||
public function addHttpRequestFilter($result) {
|
||||
if ( !$this->downloadFilterAdded && $this->isAuthenticationEnabled() ) {
|
||||
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 = '') {
|
||||
//Is WordPress trying to download one of our release assets?
|
||||
if ( $this->releaseAssetsEnabled && (strpos($url, $this->assetApiBaseUrl) !== 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.
|
||||
*
|
||||
* @internal
|
||||
* @param string $location
|
||||
* @param array $headers
|
||||
*/
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
endif;
|
309
dependencies/yahnis-elsts/plugin-update-checker/Puc/v4p11/Vcs/GitLabApi.php
vendored
Normal file
309
dependencies/yahnis-elsts/plugin-update-checker/Puc/v4p11/Vcs/GitLabApi.php
vendored
Normal file
@ -0,0 +1,309 @@
|
||||
<?php
|
||||
|
||||
if ( !class_exists('Puc_v4p11_Vcs_GitLabApi', false) ):
|
||||
|
||||
class Puc_v4p11_Vcs_GitLabApi extends Puc_v4p11_Vcs_Api {
|
||||
/**
|
||||
* @var string GitLab username.
|
||||
*/
|
||||
protected $userName;
|
||||
|
||||
/**
|
||||
* @var string GitLab server host.
|
||||
*/
|
||||
protected $repositoryHost;
|
||||
|
||||
/**
|
||||
* @var string Protocol used by this GitLab server: "http" or "https".
|
||||
*/
|
||||
protected $repositoryProtocol = 'https';
|
||||
|
||||
/**
|
||||
* @var string GitLab repository name.
|
||||
*/
|
||||
protected $repositoryName;
|
||||
|
||||
/**
|
||||
* @var string GitLab authentication token. Optional.
|
||||
*/
|
||||
protected $accessToken;
|
||||
|
||||
public function __construct($repositoryUrl, $accessToken = null, $subgroup = null) {
|
||||
//Parse the repository host to support custom hosts.
|
||||
$port = parse_url($repositoryUrl, PHP_URL_PORT);
|
||||
if ( !empty($port) ) {
|
||||
$port = ':' . $port;
|
||||
}
|
||||
$this->repositoryHost = parse_url($repositoryUrl, PHP_URL_HOST) . $port;
|
||||
|
||||
if ( $this->repositoryHost !== 'gitlab.com' ) {
|
||||
$this->repositoryProtocol = parse_url($repositoryUrl, PHP_URL_SCHEME);
|
||||
}
|
||||
|
||||
//Find the repository information
|
||||
$path = parse_url($repositoryUrl, PHP_URL_PATH);
|
||||
if ( preg_match('@^/?(?P<username>[^/]+?)/(?P<repository>[^/#?&]+?)/?$@', $path, $matches) ) {
|
||||
$this->userName = $matches['username'];
|
||||
$this->repositoryName = $matches['repository'];
|
||||
} elseif ( ($this->repositoryHost === 'gitlab.com') ) {
|
||||
//This is probably a repository in a subgroup, e.g. "/organization/category/repo".
|
||||
$parts = explode('/', trim($path, '/'));
|
||||
if ( count($parts) < 3 ) {
|
||||
throw new InvalidArgumentException('Invalid GitLab.com repository URL: "' . $repositoryUrl . '"');
|
||||
}
|
||||
$lastPart = array_pop($parts);
|
||||
$this->userName = implode('/', $parts);
|
||||
$this->repositoryName = $lastPart;
|
||||
} else {
|
||||
//There could be subgroups in the URL: gitlab.domain.com/group/subgroup/subgroup2/repository
|
||||
if ( $subgroup !== null ) {
|
||||
$path = str_replace(trailingslashit($subgroup), '', $path);
|
||||
}
|
||||
|
||||
//This is not a traditional url, it could be gitlab is in a deeper subdirectory.
|
||||
//Get the path segments.
|
||||
$segments = explode('/', untrailingslashit(ltrim($path, '/')));
|
||||
|
||||
//We need at least /user-name/repository-name/
|
||||
if ( count($segments) < 2 ) {
|
||||
throw new InvalidArgumentException('Invalid GitLab repository URL: "' . $repositoryUrl . '"');
|
||||
}
|
||||
|
||||
//Get the username and repository name.
|
||||
$usernameRepo = array_splice($segments, -2, 2);
|
||||
$this->userName = $usernameRepo[0];
|
||||
$this->repositoryName = $usernameRepo[1];
|
||||
|
||||
//Append the remaining segments to the host if there are segments left.
|
||||
if ( count($segments) > 0 ) {
|
||||
$this->repositoryHost = trailingslashit($this->repositoryHost) . implode('/', $segments);
|
||||
}
|
||||
|
||||
//Add subgroups to username.
|
||||
if ( $subgroup !== null ) {
|
||||
$this->userName = $usernameRepo[0] . '/' . untrailingslashit($subgroup);
|
||||
}
|
||||
}
|
||||
|
||||
parent::__construct($repositoryUrl, $accessToken);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the latest release from GitLab.
|
||||
*
|
||||
* @return Puc_v4p11_Vcs_Reference|null
|
||||
*/
|
||||
public function getLatestRelease() {
|
||||
return $this->getLatestTag();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the tag that looks like the highest version number.
|
||||
*
|
||||
* @return Puc_v4p11_Vcs_Reference|null
|
||||
*/
|
||||
public function getLatestTag() {
|
||||
$tags = $this->api('/:id/repository/tags');
|
||||
if ( is_wp_error($tags) || empty($tags) || !is_array($tags) ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$versionTags = $this->sortTagsByVersion($tags);
|
||||
if ( empty($versionTags) ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$tag = $versionTags[0];
|
||||
return new Puc_v4p11_Vcs_Reference(array(
|
||||
'name' => $tag->name,
|
||||
'version' => ltrim($tag->name, 'v'),
|
||||
'downloadUrl' => $this->buildArchiveDownloadUrl($tag->name),
|
||||
'apiResponse' => $tag,
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a branch by name.
|
||||
*
|
||||
* @param string $branchName
|
||||
* @return null|Puc_v4p11_Vcs_Reference
|
||||
*/
|
||||
public function getBranch($branchName) {
|
||||
$branch = $this->api('/:id/repository/branches/' . $branchName);
|
||||
if ( is_wp_error($branch) || empty($branch) ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$reference = new Puc_v4p11_Vcs_Reference(array(
|
||||
'name' => $branch->name,
|
||||
'downloadUrl' => $this->buildArchiveDownloadUrl($branch->name),
|
||||
'apiResponse' => $branch,
|
||||
));
|
||||
|
||||
if ( isset($branch->commit, $branch->commit->committed_date) ) {
|
||||
$reference->updated = $branch->commit->committed_date;
|
||||
}
|
||||
|
||||
return $reference;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the timestamp of the latest commit that changed the specified branch or tag.
|
||||
*
|
||||
* @param string $ref Reference name (e.g. branch or tag).
|
||||
* @return string|null
|
||||
*/
|
||||
public function getLatestCommitTime($ref) {
|
||||
$commits = $this->api('/:id/repository/commits/', array('ref_name' => $ref));
|
||||
if ( is_wp_error($commits) || !is_array($commits) || !isset($commits[0]) ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $commits[0]->committed_date;
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform a GitLab API request.
|
||||
*
|
||||
* @param string $url
|
||||
* @param array $queryParams
|
||||
* @return mixed|WP_Error
|
||||
*/
|
||||
protected function api($url, $queryParams = array()) {
|
||||
$baseUrl = $url;
|
||||
$url = $this->buildApiUrl($url, $queryParams);
|
||||
|
||||
$options = array('timeout' => 10);
|
||||
if ( !empty($this->httpFilterName) ) {
|
||||
$options = apply_filters($this->httpFilterName, $options);
|
||||
}
|
||||
|
||||
$response = wp_remote_get($url, $options);
|
||||
if ( is_wp_error($response) ) {
|
||||
do_action('puc_api_error', $response, null, $url, $this->slug);
|
||||
return $response;
|
||||
}
|
||||
|
||||
$code = wp_remote_retrieve_response_code($response);
|
||||
$body = wp_remote_retrieve_body($response);
|
||||
if ( $code === 200 ) {
|
||||
return json_decode($body);
|
||||
}
|
||||
|
||||
$error = new WP_Error(
|
||||
'puc-gitlab-http-error',
|
||||
sprintf('GitLab API error. URL: "%s", HTTP status code: %d.', $baseUrl, $code)
|
||||
);
|
||||
do_action('puc_api_error', $error, $response, $url, $this->slug);
|
||||
|
||||
return $error;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a fully qualified URL for an API request.
|
||||
*
|
||||
* @param string $url
|
||||
* @param array $queryParams
|
||||
* @return string
|
||||
*/
|
||||
protected function buildApiUrl($url, $queryParams) {
|
||||
$variables = array(
|
||||
'user' => $this->userName,
|
||||
'repo' => $this->repositoryName,
|
||||
'id' => $this->userName . '/' . $this->repositoryName,
|
||||
);
|
||||
|
||||
foreach ($variables as $name => $value) {
|
||||
$url = str_replace("/:{$name}", '/' . urlencode($value), $url);
|
||||
}
|
||||
|
||||
$url = substr($url, 1);
|
||||
$url = sprintf('%1$s://%2$s/api/v4/projects/%3$s', $this->repositoryProtocol, $this->repositoryHost, $url);
|
||||
|
||||
if ( !empty($this->accessToken) ) {
|
||||
$queryParams['private_token'] = $this->accessToken;
|
||||
}
|
||||
|
||||
if ( !empty($queryParams) ) {
|
||||
$url = add_query_arg($queryParams, $url);
|
||||
}
|
||||
|
||||
return $url;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the contents of a file from a specific branch or tag.
|
||||
*
|
||||
* @param string $path File name.
|
||||
* @param string $ref
|
||||
* @return null|string Either the contents of the file, or null if the file doesn't exist or there's an error.
|
||||
*/
|
||||
public function getRemoteFile($path, $ref = 'master') {
|
||||
$response = $this->api('/:id/repository/files/' . $path, array('ref' => $ref));
|
||||
if ( is_wp_error($response) || !isset($response->content) || $response->encoding !== 'base64' ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return base64_decode($response->content);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a URL to download a ZIP archive of the specified branch/tag/etc.
|
||||
*
|
||||
* @param string $ref
|
||||
* @return string
|
||||
*/
|
||||
public function buildArchiveDownloadUrl($ref = 'master') {
|
||||
$url = sprintf(
|
||||
'%1$s://%2$s/api/v4/projects/%3$s/repository/archive.zip',
|
||||
$this->repositoryProtocol,
|
||||
$this->repositoryHost,
|
||||
urlencode($this->userName . '/' . $this->repositoryName)
|
||||
);
|
||||
$url = add_query_arg('sha', urlencode($ref), $url);
|
||||
|
||||
if ( !empty($this->accessToken) ) {
|
||||
$url = add_query_arg('private_token', $this->accessToken, $url);
|
||||
}
|
||||
|
||||
return $url;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a specific tag.
|
||||
*
|
||||
* @param string $tagName
|
||||
* @return void
|
||||
*/
|
||||
public function getTag($tagName) {
|
||||
throw new LogicException('The ' . __METHOD__ . ' method is not implemented and should not be used.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Figure out which reference (i.e tag or branch) contains the latest version.
|
||||
*
|
||||
* @param string $configBranch Start looking in this branch.
|
||||
* @return null|Puc_v4p11_Vcs_Reference
|
||||
*/
|
||||
public function chooseReference($configBranch) {
|
||||
$updateSource = null;
|
||||
|
||||
// GitLab doesn't handle releases the same as GitHub so just use the latest tag
|
||||
if ( $configBranch === 'master' ) {
|
||||
$updateSource = $this->getLatestTag();
|
||||
}
|
||||
|
||||
if ( empty($updateSource) ) {
|
||||
$updateSource = $this->getBranch($configBranch);
|
||||
}
|
||||
|
||||
return $updateSource;
|
||||
}
|
||||
|
||||
public function setAuthentication($credentials) {
|
||||
parent::setAuthentication($credentials);
|
||||
$this->accessToken = is_string($credentials) ? $credentials : null;
|
||||
}
|
||||
}
|
||||
|
||||
endif;
|
218
dependencies/yahnis-elsts/plugin-update-checker/Puc/v4p11/Vcs/PluginUpdateChecker.php
vendored
Normal file
218
dependencies/yahnis-elsts/plugin-update-checker/Puc/v4p11/Vcs/PluginUpdateChecker.php
vendored
Normal file
@ -0,0 +1,218 @@
|
||||
<?php
|
||||
if ( !class_exists('Puc_v4p11_Vcs_PluginUpdateChecker') ):
|
||||
|
||||
class Puc_v4p11_Vcs_PluginUpdateChecker extends Puc_v4p11_Plugin_UpdateChecker implements Puc_v4p11_Vcs_BaseChecker {
|
||||
/**
|
||||
* @var string The branch where to look for updates. Defaults to "master".
|
||||
*/
|
||||
protected $branch = 'master';
|
||||
|
||||
/**
|
||||
* @var Puc_v4p11_Vcs_Api Repository API client.
|
||||
*/
|
||||
protected $api = null;
|
||||
|
||||
/**
|
||||
* Puc_v4p11_Vcs_PluginUpdateChecker constructor.
|
||||
*
|
||||
* @param Puc_v4p11_Vcs_Api $api
|
||||
* @param string $pluginFile
|
||||
* @param string $slug
|
||||
* @param int $checkPeriod
|
||||
* @param string $optionName
|
||||
* @param string $muPluginFile
|
||||
*/
|
||||
public function __construct($api, $pluginFile, $slug = '', $checkPeriod = 12, $optionName = '', $muPluginFile = '') {
|
||||
$this->api = $api;
|
||||
$this->api->setHttpFilterName($this->getUniqueName('request_info_options'));
|
||||
|
||||
parent::__construct($api->getRepositoryUrl(), $pluginFile, $slug, $checkPeriod, $optionName, $muPluginFile);
|
||||
|
||||
$this->api->setSlug($this->slug);
|
||||
}
|
||||
|
||||
public function requestInfo($unusedParameter = null) {
|
||||
//We have to make several remote API requests to gather all the necessary info
|
||||
//which can take a while on slow networks.
|
||||
if ( function_exists('set_time_limit') ) {
|
||||
@set_time_limit(60);
|
||||
}
|
||||
|
||||
$api = $this->api;
|
||||
$api->setLocalDirectory($this->package->getAbsoluteDirectoryPath());
|
||||
|
||||
$info = new Puc_v4p11_Plugin_Info();
|
||||
$info->filename = $this->pluginFile;
|
||||
$info->slug = $this->slug;
|
||||
|
||||
$this->setInfoFromHeader($this->package->getPluginHeader(), $info);
|
||||
|
||||
//Pick a branch or tag.
|
||||
$updateSource = $api->chooseReference($this->branch);
|
||||
if ( $updateSource ) {
|
||||
$ref = $updateSource->name;
|
||||
$info->version = $updateSource->version;
|
||||
$info->last_updated = $updateSource->updated;
|
||||
$info->download_url = $updateSource->downloadUrl;
|
||||
|
||||
if ( !empty($updateSource->changelog) ) {
|
||||
$info->sections['changelog'] = $updateSource->changelog;
|
||||
}
|
||||
if ( isset($updateSource->downloadCount) ) {
|
||||
$info->downloaded = $updateSource->downloadCount;
|
||||
}
|
||||
} else {
|
||||
//There's probably a network problem or an authentication error.
|
||||
do_action(
|
||||
'puc_api_error',
|
||||
new WP_Error(
|
||||
'puc-no-update-source',
|
||||
'Could not retrieve version information from the repository. '
|
||||
. 'This usually means that the update checker either can\'t connect '
|
||||
. 'to the repository or it\'s configured incorrectly.'
|
||||
),
|
||||
null, null, $this->slug
|
||||
);
|
||||
return null;
|
||||
}
|
||||
|
||||
//Get headers from the main plugin file in this branch/tag. Its "Version" header and other metadata
|
||||
//are what the WordPress install will actually see after upgrading, so they take precedence over releases/tags.
|
||||
$mainPluginFile = basename($this->pluginFile);
|
||||
$remotePlugin = $api->getRemoteFile($mainPluginFile, $ref);
|
||||
if ( !empty($remotePlugin) ) {
|
||||
$remoteHeader = $this->package->getFileHeader($remotePlugin);
|
||||
$this->setInfoFromHeader($remoteHeader, $info);
|
||||
}
|
||||
|
||||
//Try parsing readme.txt. If it's formatted according to WordPress.org standards, it will contain
|
||||
//a lot of useful information like the required/tested WP version, changelog, and so on.
|
||||
if ( $this->readmeTxtExistsLocally() ) {
|
||||
$this->setInfoFromRemoteReadme($ref, $info);
|
||||
}
|
||||
|
||||
//The changelog might be in a separate file.
|
||||
if ( empty($info->sections['changelog']) ) {
|
||||
$info->sections['changelog'] = $api->getRemoteChangelog($ref, $this->package->getAbsoluteDirectoryPath());
|
||||
if ( empty($info->sections['changelog']) ) {
|
||||
$info->sections['changelog'] = __('There is no changelog available.', 'plugin-update-checker');
|
||||
}
|
||||
}
|
||||
|
||||
if ( empty($info->last_updated) ) {
|
||||
//Fetch the latest commit that changed the tag or branch and use it as the "last_updated" date.
|
||||
$latestCommitTime = $api->getLatestCommitTime($ref);
|
||||
if ( $latestCommitTime !== null ) {
|
||||
$info->last_updated = $latestCommitTime;
|
||||
}
|
||||
}
|
||||
|
||||
$info = apply_filters($this->getUniqueName('request_info_result'), $info, null);
|
||||
return $info;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the currently installed version has a readme.txt file.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function readmeTxtExistsLocally() {
|
||||
return $this->package->fileExists($this->api->getLocalReadmeName());
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy plugin metadata from a file header to a Plugin Info object.
|
||||
*
|
||||
* @param array $fileHeader
|
||||
* @param Puc_v4p11_Plugin_Info $pluginInfo
|
||||
*/
|
||||
protected function setInfoFromHeader($fileHeader, $pluginInfo) {
|
||||
$headerToPropertyMap = array(
|
||||
'Version' => 'version',
|
||||
'Name' => 'name',
|
||||
'PluginURI' => 'homepage',
|
||||
'Author' => 'author',
|
||||
'AuthorName' => 'author',
|
||||
'AuthorURI' => 'author_homepage',
|
||||
|
||||
'Requires WP' => 'requires',
|
||||
'Tested WP' => 'tested',
|
||||
'Requires at least' => 'requires',
|
||||
'Tested up to' => 'tested',
|
||||
|
||||
'Requires PHP' => 'requires_php',
|
||||
);
|
||||
foreach ($headerToPropertyMap as $headerName => $property) {
|
||||
if ( isset($fileHeader[$headerName]) && !empty($fileHeader[$headerName]) ) {
|
||||
$pluginInfo->$property = $fileHeader[$headerName];
|
||||
}
|
||||
}
|
||||
|
||||
if ( !empty($fileHeader['Description']) ) {
|
||||
$pluginInfo->sections['description'] = $fileHeader['Description'];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy plugin metadata from the remote readme.txt file.
|
||||
*
|
||||
* @param string $ref GitHub tag or branch where to look for the readme.
|
||||
* @param Puc_v4p11_Plugin_Info $pluginInfo
|
||||
*/
|
||||
protected function setInfoFromRemoteReadme($ref, $pluginInfo) {
|
||||
$readme = $this->api->getRemoteReadme($ref);
|
||||
if ( empty($readme) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( isset($readme['sections']) ) {
|
||||
$pluginInfo->sections = array_merge($pluginInfo->sections, $readme['sections']);
|
||||
}
|
||||
if ( !empty($readme['tested_up_to']) ) {
|
||||
$pluginInfo->tested = $readme['tested_up_to'];
|
||||
}
|
||||
if ( !empty($readme['requires_at_least']) ) {
|
||||
$pluginInfo->requires = $readme['requires_at_least'];
|
||||
}
|
||||
if ( !empty($readme['requires_php']) ) {
|
||||
$pluginInfo->requires_php = $readme['requires_php'];
|
||||
}
|
||||
|
||||
if ( isset($readme['upgrade_notice'], $readme['upgrade_notice'][$pluginInfo->version]) ) {
|
||||
$pluginInfo->upgrade_notice = $readme['upgrade_notice'][$pluginInfo->version];
|
||||
}
|
||||
}
|
||||
|
||||
public function setBranch($branch) {
|
||||
$this->branch = $branch;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setAuthentication($credentials) {
|
||||
$this->api->setAuthentication($credentials);
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getVcsApi() {
|
||||
return $this->api;
|
||||
}
|
||||
|
||||
public function getUpdate() {
|
||||
$update = parent::getUpdate();
|
||||
|
||||
if ( isset($update) && !empty($update->download_url) ) {
|
||||
$update->download_url = $this->api->signDownloadUrl($update->download_url);
|
||||
}
|
||||
|
||||
return $update;
|
||||
}
|
||||
|
||||
public function onDisplayConfiguration($panel) {
|
||||
parent::onDisplayConfiguration($panel);
|
||||
$panel->row('Branch', $this->branch);
|
||||
$panel->row('Authentication enabled', $this->api->isAuthenticationEnabled() ? 'Yes' : 'No');
|
||||
$panel->row('API client', get_class($this->api));
|
||||
}
|
||||
}
|
||||
|
||||
endif;
|
49
dependencies/yahnis-elsts/plugin-update-checker/Puc/v4p11/Vcs/Reference.php
vendored
Normal file
49
dependencies/yahnis-elsts/plugin-update-checker/Puc/v4p11/Vcs/Reference.php
vendored
Normal file
@ -0,0 +1,49 @@
|
||||
<?php
|
||||
if ( !class_exists('Puc_v4p11_Vcs_Reference', false) ):
|
||||
|
||||
/**
|
||||
* This class represents a VCS branch or tag. It's intended as a read only, short-lived container
|
||||
* that only exists to provide a limited degree of type checking.
|
||||
*
|
||||
* @property string $name
|
||||
* @property string|null version
|
||||
* @property string $downloadUrl
|
||||
* @property string $updated
|
||||
*
|
||||
* @property string|null $changelog
|
||||
* @property int|null $downloadCount
|
||||
*/
|
||||
class Puc_v4p11_Vcs_Reference {
|
||||
private $properties = array();
|
||||
|
||||
public function __construct($properties = array()) {
|
||||
$this->properties = $properties;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $name
|
||||
* @return mixed|null
|
||||
*/
|
||||
public function __get($name) {
|
||||
return array_key_exists($name, $this->properties) ? $this->properties[$name] : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $name
|
||||
* @param mixed $value
|
||||
*/
|
||||
public function __set($name, $value) {
|
||||
$this->properties[$name] = $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $name
|
||||
* @return bool
|
||||
*/
|
||||
public function __isset($name) {
|
||||
return isset($this->properties[$name]);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
endif;
|
118
dependencies/yahnis-elsts/plugin-update-checker/Puc/v4p11/Vcs/ThemeUpdateChecker.php
vendored
Normal file
118
dependencies/yahnis-elsts/plugin-update-checker/Puc/v4p11/Vcs/ThemeUpdateChecker.php
vendored
Normal file
@ -0,0 +1,118 @@
|
||||
<?php
|
||||
|
||||
if ( !class_exists('Puc_v4p11_Vcs_ThemeUpdateChecker', false) ):
|
||||
|
||||
class Puc_v4p11_Vcs_ThemeUpdateChecker extends Puc_v4p11_Theme_UpdateChecker implements Puc_v4p11_Vcs_BaseChecker {
|
||||
/**
|
||||
* @var string The branch where to look for updates. Defaults to "master".
|
||||
*/
|
||||
protected $branch = 'master';
|
||||
|
||||
/**
|
||||
* @var Puc_v4p11_Vcs_Api Repository API client.
|
||||
*/
|
||||
protected $api = null;
|
||||
|
||||
/**
|
||||
* Puc_v4p11_Vcs_ThemeUpdateChecker constructor.
|
||||
*
|
||||
* @param Puc_v4p11_Vcs_Api $api
|
||||
* @param null $stylesheet
|
||||
* @param null $customSlug
|
||||
* @param int $checkPeriod
|
||||
* @param string $optionName
|
||||
*/
|
||||
public function __construct($api, $stylesheet = null, $customSlug = null, $checkPeriod = 12, $optionName = '') {
|
||||
$this->api = $api;
|
||||
$this->api->setHttpFilterName($this->getUniqueName('request_update_options'));
|
||||
|
||||
parent::__construct($api->getRepositoryUrl(), $stylesheet, $customSlug, $checkPeriod, $optionName);
|
||||
|
||||
$this->api->setSlug($this->slug);
|
||||
}
|
||||
|
||||
public function requestUpdate() {
|
||||
$api = $this->api;
|
||||
$api->setLocalDirectory($this->package->getAbsoluteDirectoryPath());
|
||||
|
||||
$update = new Puc_v4p11_Theme_Update();
|
||||
$update->slug = $this->slug;
|
||||
|
||||
//Figure out which reference (tag or branch) we'll use to get the latest version of the theme.
|
||||
$updateSource = $api->chooseReference($this->branch);
|
||||
if ( $updateSource ) {
|
||||
$ref = $updateSource->name;
|
||||
$update->download_url = $updateSource->downloadUrl;
|
||||
} else {
|
||||
do_action(
|
||||
'puc_api_error',
|
||||
new WP_Error(
|
||||
'puc-no-update-source',
|
||||
'Could not retrieve version information from the repository. '
|
||||
. 'This usually means that the update checker either can\'t connect '
|
||||
. 'to the repository or it\'s configured incorrectly.'
|
||||
),
|
||||
null, null, $this->slug
|
||||
);
|
||||
$ref = $this->branch;
|
||||
}
|
||||
|
||||
//Get headers from the main stylesheet in this branch/tag. Its "Version" header and other metadata
|
||||
//are what the WordPress install will actually see after upgrading, so they take precedence over releases/tags.
|
||||
$remoteHeader = $this->package->getFileHeader($api->getRemoteFile('style.css', $ref));
|
||||
$update->version = Puc_v4p11_Utils::findNotEmpty(array(
|
||||
$remoteHeader['Version'],
|
||||
Puc_v4p11_Utils::get($updateSource, 'version'),
|
||||
));
|
||||
|
||||
//The details URL defaults to the Theme URI header or the repository URL.
|
||||
$update->details_url = Puc_v4p11_Utils::findNotEmpty(array(
|
||||
$remoteHeader['ThemeURI'],
|
||||
$this->package->getHeaderValue('ThemeURI'),
|
||||
$this->metadataUrl,
|
||||
));
|
||||
|
||||
if ( empty($update->version) ) {
|
||||
//It looks like we didn't find a valid update after all.
|
||||
$update = null;
|
||||
}
|
||||
|
||||
$update = $this->filterUpdateResult($update);
|
||||
return $update;
|
||||
}
|
||||
|
||||
//FIXME: This is duplicated code. Both theme and plugin subclasses that use VCS share these methods.
|
||||
|
||||
public function setBranch($branch) {
|
||||
$this->branch = $branch;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setAuthentication($credentials) {
|
||||
$this->api->setAuthentication($credentials);
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getVcsApi() {
|
||||
return $this->api;
|
||||
}
|
||||
|
||||
public function getUpdate() {
|
||||
$update = parent::getUpdate();
|
||||
|
||||
if ( isset($update) && !empty($update->download_url) ) {
|
||||
$update->download_url = $this->api->signDownloadUrl($update->download_url);
|
||||
}
|
||||
|
||||
return $update;
|
||||
}
|
||||
|
||||
public function onDisplayConfiguration($panel) {
|
||||
parent::onDisplayConfiguration($panel);
|
||||
$panel->row('Branch', $this->branch);
|
||||
$panel->row('Authentication enabled', $this->api->isAuthenticationEnabled() ? 'Yes' : 'No');
|
||||
$panel->row('API client', get_class($this->api));
|
||||
}
|
||||
}
|
||||
|
||||
endif;
|
Reference in New Issue
Block a user