From 93981724f3b67aa05668b7e007ea66e43d010c82 Mon Sep 17 00:00:00 2001 From: Paul Kilmurray Date: Sat, 9 Dec 2023 12:48:35 +0100 Subject: [PATCH] update workflow with composer prefix-dependencies --- .github/workflows/release.yml | 7 +- .github/workflows/wp-engine.yml | 1 + .github/workflows/wporg-deploy.yml | 5 +- README.md | 1 + composer.json | 6 +- includes/API/Stores.php | 2 +- includes/{Services => Abstracts}/Store.php | 20 +- includes/Activator.php | 77 +- .../Admin/Updaters/Pro_Plugin_Updater.php | 264 +++++- includes/Deactivator.php | 1 - includes/Init.php | 70 +- includes/Integrations/WPSEO.php | 22 +- includes/Templates/Frontend.php | 5 +- includes/wcpos-store-functions.php | 2 +- php-scoper/composer.json | 9 +- php-scoper/dummy/dummy.php | 2 + php-scoper/{config.php => scoper.inc.php} | 15 +- .../Test_Store_Abstract.php} | 6 +- vendor_prefixed/dummy/dummy.php | 5 + .../php-jwt/src/BeforeValidException.php | 16 - .../firebase/php-jwt/src/CachedKeySet.php | 226 ----- .../firebase/php-jwt/src/ExpiredException.php | 16 - vendor_prefixed/firebase/php-jwt/src/JWK.php | 267 ------ vendor_prefixed/firebase/php-jwt/src/JWT.php | 571 ------------ .../src/JWTExceptionWithPayloadInterface.php | 20 - vendor_prefixed/firebase/php-jwt/src/Key.php | 50 -- .../php-jwt/src/SignatureInvalidException.php | 7 - .../Puc/v5/PucFactory.php | 9 - .../Puc/v5p2/Autoloader.php | 69 -- .../Puc/v5p2/DebugBar/Extension.php | 163 ---- .../Puc/v5p2/DebugBar/Panel.php | 141 --- .../Puc/v5p2/DebugBar/PluginExtension.php | 37 - .../Puc/v5p2/DebugBar/PluginPanel.php | 31 - .../Puc/v5p2/DebugBar/ThemePanel.php | 23 - .../Puc/v5p2/InstalledPackage.php | 92 -- .../Puc/v5p2/Metadata.php | 155 ---- .../Puc/v5p2/OAuthSignature.php | 83 -- .../Puc/v5p2/Plugin/Package.php | 163 ---- .../Puc/v5p2/Plugin/PluginInfo.php | 112 --- .../Puc/v5p2/Plugin/Ui.php | 240 ----- .../Puc/v5p2/Plugin/Update.php | 105 --- .../Puc/v5p2/Plugin/UpdateChecker.php | 387 -------- .../Puc/v5p2/PucFactory.php | 297 ------ .../Puc/v5p2/Scheduler.php | 243 ----- .../Puc/v5p2/StateStore.php | 196 ---- .../Puc/v5p2/Theme/Package.php | 55 -- .../Puc/v5p2/Theme/Update.php | 76 -- .../Puc/v5p2/Theme/UpdateChecker.php | 141 --- .../plugin-update-checker/Puc/v5p2/Update.php | 35 - .../Puc/v5p2/UpdateChecker.php | 849 ------------------ .../Puc/v5p2/UpgraderStatus.php | 196 ---- .../plugin-update-checker/Puc/v5p2/Utils.php | 69 -- .../Puc/v5p2/Vcs/Api.php | 347 ------- .../Puc/v5p2/Vcs/BaseChecker.php | 27 - .../Puc/v5p2/Vcs/BitBucketApi.php | 216 ----- .../Puc/v5p2/Vcs/GitHubApi.php | 387 -------- .../Puc/v5p2/Vcs/GitLabApi.php | 333 ------- .../Puc/v5p2/Vcs/PluginUpdateChecker.php | 204 ----- .../Puc/v5p2/Vcs/Reference.php | 50 -- .../Puc/v5p2/Vcs/ReleaseAssetSupport.php | 78 -- .../Puc/v5p2/Vcs/ReleaseFilteringFeature.php | 91 -- .../Puc/v5p2/Vcs/ThemeUpdateChecker.php | 57 -- .../Puc/v5p2/Vcs/VcsCheckerMethods.php | 55 -- .../plugin-update-checker/load-v5p2.php | 17 - .../plugin-update-checker.php | 12 - 65 files changed, 383 insertions(+), 7151 deletions(-) rename includes/{Services => Abstracts}/Store.php (97%) create mode 100644 php-scoper/dummy/dummy.php rename php-scoper/{config.php => scoper.inc.php} (75%) rename tests/includes/{Services/Test_Store_Service.php => Abstracts/Test_Store_Abstract.php} (98%) create mode 100644 vendor_prefixed/dummy/dummy.php delete mode 100644 vendor_prefixed/firebase/php-jwt/src/BeforeValidException.php delete mode 100644 vendor_prefixed/firebase/php-jwt/src/CachedKeySet.php delete mode 100644 vendor_prefixed/firebase/php-jwt/src/ExpiredException.php delete mode 100644 vendor_prefixed/firebase/php-jwt/src/JWK.php delete mode 100644 vendor_prefixed/firebase/php-jwt/src/JWT.php delete mode 100644 vendor_prefixed/firebase/php-jwt/src/JWTExceptionWithPayloadInterface.php delete mode 100644 vendor_prefixed/firebase/php-jwt/src/Key.php delete mode 100644 vendor_prefixed/firebase/php-jwt/src/SignatureInvalidException.php delete mode 100644 vendor_prefixed/yahnis-elsts/plugin-update-checker/Puc/v5/PucFactory.php delete mode 100644 vendor_prefixed/yahnis-elsts/plugin-update-checker/Puc/v5p2/Autoloader.php delete mode 100644 vendor_prefixed/yahnis-elsts/plugin-update-checker/Puc/v5p2/DebugBar/Extension.php delete mode 100644 vendor_prefixed/yahnis-elsts/plugin-update-checker/Puc/v5p2/DebugBar/Panel.php delete mode 100644 vendor_prefixed/yahnis-elsts/plugin-update-checker/Puc/v5p2/DebugBar/PluginExtension.php delete mode 100644 vendor_prefixed/yahnis-elsts/plugin-update-checker/Puc/v5p2/DebugBar/PluginPanel.php delete mode 100644 vendor_prefixed/yahnis-elsts/plugin-update-checker/Puc/v5p2/DebugBar/ThemePanel.php delete mode 100644 vendor_prefixed/yahnis-elsts/plugin-update-checker/Puc/v5p2/InstalledPackage.php delete mode 100644 vendor_prefixed/yahnis-elsts/plugin-update-checker/Puc/v5p2/Metadata.php delete mode 100644 vendor_prefixed/yahnis-elsts/plugin-update-checker/Puc/v5p2/OAuthSignature.php delete mode 100644 vendor_prefixed/yahnis-elsts/plugin-update-checker/Puc/v5p2/Plugin/Package.php delete mode 100644 vendor_prefixed/yahnis-elsts/plugin-update-checker/Puc/v5p2/Plugin/PluginInfo.php delete mode 100644 vendor_prefixed/yahnis-elsts/plugin-update-checker/Puc/v5p2/Plugin/Ui.php delete mode 100644 vendor_prefixed/yahnis-elsts/plugin-update-checker/Puc/v5p2/Plugin/Update.php delete mode 100644 vendor_prefixed/yahnis-elsts/plugin-update-checker/Puc/v5p2/Plugin/UpdateChecker.php delete mode 100644 vendor_prefixed/yahnis-elsts/plugin-update-checker/Puc/v5p2/PucFactory.php delete mode 100644 vendor_prefixed/yahnis-elsts/plugin-update-checker/Puc/v5p2/Scheduler.php delete mode 100644 vendor_prefixed/yahnis-elsts/plugin-update-checker/Puc/v5p2/StateStore.php delete mode 100644 vendor_prefixed/yahnis-elsts/plugin-update-checker/Puc/v5p2/Theme/Package.php delete mode 100644 vendor_prefixed/yahnis-elsts/plugin-update-checker/Puc/v5p2/Theme/Update.php delete mode 100644 vendor_prefixed/yahnis-elsts/plugin-update-checker/Puc/v5p2/Theme/UpdateChecker.php delete mode 100644 vendor_prefixed/yahnis-elsts/plugin-update-checker/Puc/v5p2/Update.php delete mode 100644 vendor_prefixed/yahnis-elsts/plugin-update-checker/Puc/v5p2/UpdateChecker.php delete mode 100644 vendor_prefixed/yahnis-elsts/plugin-update-checker/Puc/v5p2/UpgraderStatus.php delete mode 100644 vendor_prefixed/yahnis-elsts/plugin-update-checker/Puc/v5p2/Utils.php delete mode 100644 vendor_prefixed/yahnis-elsts/plugin-update-checker/Puc/v5p2/Vcs/Api.php delete mode 100644 vendor_prefixed/yahnis-elsts/plugin-update-checker/Puc/v5p2/Vcs/BaseChecker.php delete mode 100644 vendor_prefixed/yahnis-elsts/plugin-update-checker/Puc/v5p2/Vcs/BitBucketApi.php delete mode 100644 vendor_prefixed/yahnis-elsts/plugin-update-checker/Puc/v5p2/Vcs/GitHubApi.php delete mode 100644 vendor_prefixed/yahnis-elsts/plugin-update-checker/Puc/v5p2/Vcs/GitLabApi.php delete mode 100644 vendor_prefixed/yahnis-elsts/plugin-update-checker/Puc/v5p2/Vcs/PluginUpdateChecker.php delete mode 100644 vendor_prefixed/yahnis-elsts/plugin-update-checker/Puc/v5p2/Vcs/Reference.php delete mode 100644 vendor_prefixed/yahnis-elsts/plugin-update-checker/Puc/v5p2/Vcs/ReleaseAssetSupport.php delete mode 100644 vendor_prefixed/yahnis-elsts/plugin-update-checker/Puc/v5p2/Vcs/ReleaseFilteringFeature.php delete mode 100644 vendor_prefixed/yahnis-elsts/plugin-update-checker/Puc/v5p2/Vcs/ThemeUpdateChecker.php delete mode 100644 vendor_prefixed/yahnis-elsts/plugin-update-checker/Puc/v5p2/Vcs/VcsCheckerMethods.php delete mode 100644 vendor_prefixed/yahnis-elsts/plugin-update-checker/load-v5p2.php delete mode 100644 vendor_prefixed/yahnis-elsts/plugin-update-checker/plugin-update-checker.php diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 71d2a39..990aee0 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -14,12 +14,12 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@v2 + uses: actions/checkout@v4 - name: Check if version has changed id: check_version run: | - VERSION=$(grep -oP "Version:\s*\K\d+(\.\d+)*" woocommerce-pos.php) + VERSION=$(grep -oP "Version:\s*\K\d+(\.\d+)*(-\w+)?(\.\d+)?" woocommerce-pos.php) echo "VERSION=$VERSION" >> $GITHUB_ENV git fetch --prune --unshallow LAST_TAG=$(git describe --tags --abbrev=0) @@ -41,7 +41,7 @@ jobs: tag_name: v${{ env.VERSION }} release_name: Release v${{ env.VERSION }} draft: true - prerelease: false + prerelease: ${{ contains(env.VERSION, '-beta') }} - name: Build if: steps.check_version.outputs.release == 'true' @@ -49,6 +49,7 @@ jobs: YARN_ENABLE_IMMUTABLE_INSTALLS: false run: | yarn install + composer prefix-dependencies composer install --no-dev yarn build:js diff --git a/.github/workflows/wp-engine.yml b/.github/workflows/wp-engine.yml index 2ec7616..c1f59f5 100644 --- a/.github/workflows/wp-engine.yml +++ b/.github/workflows/wp-engine.yml @@ -18,6 +18,7 @@ jobs: YARN_ENABLE_IMMUTABLE_INSTALLS: false run: | yarn install + composer prefix-dependencies composer install --no-dev yarn build:js diff --git a/.github/workflows/wporg-deploy.yml b/.github/workflows/wporg-deploy.yml index a07c4b6..8fba0bc 100644 --- a/.github/workflows/wporg-deploy.yml +++ b/.github/workflows/wporg-deploy.yml @@ -2,7 +2,7 @@ name: Deploy to WordPress.org on: release: - types: [ published ] + types: [ released ] jobs: tag: @@ -12,13 +12,14 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Build env: YARN_ENABLE_IMMUTABLE_INSTALLS: false run: | yarn install + composer prefix-dependencies composer install --no-dev yarn build:js diff --git a/README.md b/README.md index f4856ea..3b194c3 100644 --- a/README.md +++ b/README.md @@ -51,6 +51,7 @@ To prepare the repository for local development you should rename `.env.example` Next you will need to install the required PHP via `composer` and JavaScript packages via `yarn`. ```sh +composer prefix-dependencies composer install yarn install ``` diff --git a/composer.json b/composer.json index e8b5d08..2a4f72a 100644 --- a/composer.json +++ b/composer.json @@ -24,6 +24,7 @@ "require": { "php": ">=7.4", "ext-json": "*", + "erusev/parsedown": "^1.7", "ramsey/uuid": "^4.2", "salesforce/handlebars-php": "3.0.1" }, @@ -47,7 +48,7 @@ "fix": "php-cs-fixer fix .", "prefix-dependencies": [ "@composer --working-dir=php-scoper install", - "php-scoper/vendor/bin/php-scoper add-prefix --config=php-scoper/config.php --output-dir=./vendor_prefixed --force", + "cd php-scoper && vendor/bin/php-scoper --output-dir=../vendor_prefixed add-prefix --force && cd ..", "@composer dump-autoload -o -a" ] }, @@ -56,8 +57,7 @@ "WCPOS\\WooCommercePOS\\": "includes/" }, "classmap": [ - "vendor_prefixed/firebase/php-jwt/src", - "vendor_prefixed/yahnis-elsts/plugin-update-checker" + "vendor_prefixed/vendor/firebase/php-jwt/src" ] }, "autoload-dev": { diff --git a/includes/API/Stores.php b/includes/API/Stores.php index d78e0d5..d04b9a9 100644 --- a/includes/API/Stores.php +++ b/includes/API/Stores.php @@ -9,7 +9,7 @@ } use WP_REST_Controller; -use WCPOS\WooCommercePOS\Services\Store; +use WCPOS\WooCommercePOS\Abstracts\Store; use const WCPOS\WooCommercePOS\SHORT_NAME; class Stores extends WP_REST_Controller { diff --git a/includes/Services/Store.php b/includes/Abstracts/Store.php similarity index 97% rename from includes/Services/Store.php rename to includes/Abstracts/Store.php index 0488cfe..a2e815e 100644 --- a/includes/Services/Store.php +++ b/includes/Abstracts/Store.php @@ -1,14 +1,22 @@ version_check(); - $this->pro_version_check(); + $this->pro_version_check(); // resolve plugin plugins $this->plugin_check(); @@ -89,10 +89,12 @@ public function single_activate(): void { $this->create_pos_roles(); // add pos capabilities to non POS roles - $this->add_pos_capability(array( - 'administrator' => array( 'manage_woocommerce_pos', 'access_woocommerce_pos' ), - 'shop_manager' => array( 'manage_woocommerce_pos', 'access_woocommerce_pos' ), - )); + $this->add_pos_capability( + array( + 'administrator' => array( 'manage_woocommerce_pos', 'access_woocommerce_pos' ), + 'shop_manager' => array( 'manage_woocommerce_pos', 'access_woocommerce_pos' ), + ) + ); // set the auto redirection on next page load // set_transient( 'woocommere_pos_welcome', 1, 30 ); @@ -123,9 +125,9 @@ private function php_check() { } $message = sprintf( - __( 'WooCommerce POS requires PHP %1$s or higher. Read more information about how you can update', 'woocommerce-pos' ), - PHP_MIN_VERSION, - 'http://www.wpupdatephp.com/update/' + __( 'WooCommerce POS requires PHP %1$s or higher. Read more information about how you can update', 'woocommerce-pos' ), + PHP_MIN_VERSION, + 'http://www.wpupdatephp.com/update/' ) . ' »'; Admin\Notices::add( $message ); @@ -140,10 +142,10 @@ private function woocommerce_check() { } $message = sprintf( - __( 'WooCommerce POS requires WooCommerce %2$s or higher. Please install and activate WooCommerce', 'woocommerce-pos' ), - 'http://wordpress.org/plugins/woocommerce/', - WC_MIN_VERSION, - admin_url( 'plugins.php' ) + __( 'WooCommerce POS requires WooCommerce %2$s or higher. Please install and activate WooCommerce', 'woocommerce-pos' ), + 'http://wordpress.org/plugins/woocommerce/', + WC_MIN_VERSION, + admin_url( 'plugins.php' ) ) . ' »'; Admin\Notices::add( $message ); @@ -184,9 +186,9 @@ private function version_check(): void { */ private function plugin_check(): void { // disable NextGEN Gallery resource manager - // if ( ! \defined( 'NGG_DISABLE_RESOURCE_MANAGER' ) ) { - // \define( 'NGG_DISABLE_RESOURCE_MANAGER', true ); - // } + // if ( ! \defined( 'NGG_DISABLE_RESOURCE_MANAGER' ) ) { + // \define( 'NGG_DISABLE_RESOURCE_MANAGER', true ); + // } } /** @@ -222,14 +224,16 @@ private function create_pos_roles(): void { ); add_role( - 'cashier', - __( 'Cashier', 'woocommerce-pos' ), - $cashier_capabilities + 'cashier', + __( 'Cashier', 'woocommerce-pos' ), + $cashier_capabilities ); - $this->add_pos_capability(array( - 'cashier' => array( 'access_woocommerce_pos' ), - )); + $this->add_pos_capability( + array( + 'cashier' => array( 'access_woocommerce_pos' ), + ) + ); } /** @@ -269,33 +273,32 @@ private function db_upgrade( $old, $current ): void { } } - /** - * If \WCPOS\WooCommercePOSPro\ is installed, check the version is above MIN_PRO_VERSION - */ + /** + * If \WCPOS\WooCommercePOSPro\ is installed, check the version is above MIN_PRO_VERSION + */ private function pro_version_check() { - if ( class_exists( '\WCPOS\WooCommercePOSPro\Activator' ) ) { + if ( class_exists( '\WCPOS\WooCommercePOSPro\Activator' ) ) { if ( version_compare( \WCPOS\WooCommercePOSPro\VERSION, MIN_PRO_VERSION, '<' ) ) { - /** - * NOTE: the deactivate_plugins function is not available in the frontend or ajax - * This is an extreme situation where the Pro plugin could crash the site, so we need to deactivate it - */ - if ( ! function_exists( 'deactivate_plugins' ) ) { - require_once( ABSPATH . '/wp-admin/includes/plugin.php' ); - } + /** + * NOTE: the deactivate_plugins function is not available in the frontend or ajax + * This is an extreme situation where the Pro plugin could crash the site, so we need to deactivate it + */ + if ( ! function_exists( 'deactivate_plugins' ) ) { + require_once ABSPATH . '/wp-admin/includes/plugin.php'; + } // WooCommerce POS Pro is activated, but the version is too low deactivate_plugins( 'woocommerce-pos-pro/woocommerce-pos-pro.php' ); $message = sprintf( - __( 'WooCommerce POS requires WooCommerce POS Pro %2$s or higher. Please install and activate WooCommerce POS Pro', 'woocommerce-pos' ), - 'https://wcpos.com/my-account', - MIN_PRO_VERSION, - admin_url( 'plugins.php' ) + __( 'WooCommerce POS requires WooCommerce POS Pro %2$s or higher. Please install and activate WooCommerce POS Pro', 'woocommerce-pos' ), + 'https://wcpos.com/my-account', + MIN_PRO_VERSION, + admin_url( 'plugins.php' ) ) . ' »'; Admin\Notices::add( $message ); } } } - } diff --git a/includes/Admin/Updaters/Pro_Plugin_Updater.php b/includes/Admin/Updaters/Pro_Plugin_Updater.php index 0d66244..39e1a77 100644 --- a/includes/Admin/Updaters/Pro_Plugin_Updater.php +++ b/includes/Admin/Updaters/Pro_Plugin_Updater.php @@ -10,13 +10,22 @@ namespace WCPOS\WooCommercePOS\Admin\Updaters; +use Parsedown; use WCPOS\WooCommercePOS\Logger; use WP_Error; +use WCPOS\WooCommercePOS\Services\Settings; /** * Handles the update checking for the Pro plugin */ class Pro_Plugin_Updater { + /** + * The Pro plugin slug + * + * @var string $pro_plugin_slug + */ + private $pro_plugin_slug = 'woocommerce-pos-pro'; + /** * The path to the Pro plugin * @@ -29,7 +38,7 @@ class Pro_Plugin_Updater { * * @var string $update_server */ - private $update_server = 'https://updates.wcpos.com/pro/'; + private $update_server = 'https://updates.wcpos.com/pro'; /** * Transient key for the update data @@ -38,6 +47,13 @@ class Pro_Plugin_Updater { */ private $update_data_transient_key = 'woocommerce_pos_pro_update_data'; + /** + * Transient key for the update data + * + * @var string $update_data_transient_key + */ + private $license_status_transient_key = 'woocommerce_pos_pro_license_status'; + /** * Whether the Pro plugin is installed * @@ -59,6 +75,20 @@ class Pro_Plugin_Updater { */ private $current_version; + /** + * The license key + * + * @var string $license_key + */ + private $license_key; + + /** + * The instance + * + * @var string $instance + */ + private $instance; + /** * Constructor. */ @@ -77,6 +107,15 @@ public function __construct() { add_action( 'upgrader_process_complete', 'after_plugin_update', 10, 2 ); add_filter( 'plugin_row_meta', array( $this, 'plugin_row_meta' ), 10, 4 ); add_action( 'in_plugin_update_message-' . $this->pro_plugin_path, array( $this, 'plugin_update_message' ), 10, 2 ); + add_action( 'install_plugins_pre_plugin-information', array( $this, 'plugin_information' ), 5 ); + + // get license key from settings. + $settings_service = new Settings(); + $license_settings = $settings_service->get_license_settings(); + if ( isset( $license_settings['license_key'] ) && isset( $license_settings['instance'] ) ) { + $this->license_key = $license_settings['license_key']; + $this->instance = $license_settings['instance']; + } } } @@ -119,15 +158,74 @@ public function check_pro_plugin_status() { public function check_pro_plugin_updates( $force = false ) { $update_data = get_transient( $this->update_data_transient_key ); $is_development = isset( $_ENV['DEVELOPMENT'] ) && $_ENV['DEVELOPMENT']; + $expiration = $is_development ? 1 : 60 * 60 * 12; // 12 hours. + $endpoint = $is_development ? 'http://localhost:8080/pro' : $this->update_server; + + if ( empty( $update_data ) || $force ) { + $url = $endpoint . '/update/' . $this->current_version; + + // make the api call. + $response = wp_remote_get( + $url, + array( + 'timeout' => wp_doing_cron() ? 10 : 3, + 'headers' => array( + 'Accept' => 'application/json', + ), + ) + ); + + $data = $this->validate_api_response( $response ); + + if ( is_wp_error( $data ) ) { + Logger::log( $data ); + $expiration = $is_development ? 1 : 60 * 60 * 1; // try again in an hour if error. + } + + set_transient( $this->update_data_transient_key, $data, $expiration ); + } + + return $update_data; + } + + /** + * Check the license status + * + * @param bool $force Force an update check. + */ + private function check_license_status( $force = false ) { + $license_status = get_transient( $this->license_status_transient_key ); + $is_development = isset( $_ENV['DEVELOPMENT'] ) && $_ENV['DEVELOPMENT']; + $expiration = $is_development ? 1 : 60 * 60 * 12; // 12 hours. + $endpoint = $is_development ? 'http://localhost:8080/pro' : $this->update_server; + + /** + * TODO: How to allow for multisite? + */ + if ( is_multisite() ) { + $error = new WP_Error( 'multisite_update', 'Please go to http://wcpos.com/my-account to download update.' ); + set_transient( $this->license_status_transient_key, $error, $expiration ); + return $error; + } + + /** + * If the Pro plugin is not activated, add a notice + */ + if ( ! $this->license_key || ! $this->instance ) { + // set the transient to an error. + $error = new WP_Error( 'missing_license_key', 'License key is not activated.' ); + set_transient( $this->license_status_transient_key, $error, $expiration ); + return $error; + } if ( empty( $update_data ) || $force ) { // build the request. $url = add_query_arg( array( - 'version' => $this->current_version, - // 'license_key' => $this->license_key, + 'key' => $this->license_key, + 'instance' => $this->instance, ), - $is_development ? 'http://localhost:8080/pro' : $this->update_server + $endpoint . '/license/status' ); // make the api call. @@ -145,25 +243,21 @@ public function check_pro_plugin_updates( $force = false ) { if ( is_wp_error( $data ) ) { Logger::log( $data ); - set_transient( - $this->update_data_transient_key, - array( 'error' => true ), - 60 * 60 * 1 // try again in an hour if error. - ); - } else { - set_transient( - $this->update_data_transient_key, - $data, - 60 * 60 * 12 // check for updates every 12 hours. - ); + $expiration = $is_development ? 1 : 60 * 60 * 1; // try again in an hour if error. } + + set_transient( $this->license_status_transient_key, $data, $expiration ); } + + return $license_status; } /** * Validate the API response * * @param array|WP_Error $response The API response. + * + * @return object|WP_Error $response The validated API response. */ public function validate_api_response( $response ) { if ( is_wp_error( $response ) ) { @@ -184,7 +278,20 @@ public function validate_api_response( $response ) { return new WP_Error( 'invalid_response_code', 'Unexpected response code: ' . $response['response']['code'] ); } - return $decoded_response; + // Ensure $decoded_response has the expected structure. + if ( ! isset( $decoded_response->data ) ) { + return new WP_Error( 'invalid_response_structure', 'Missing expected property: data' ); + } + + $data = $decoded_response->data; + $expected_properties = array( 'version', 'download_url', 'notes' ); + foreach ( $expected_properties as $property ) { + if ( ! property_exists( $data, $property ) ) { + return new WP_Error( 'invalid_response_structure', "Missing expected property: $property" ); + } + } + + return $data; } /** @@ -201,21 +308,32 @@ public function modify_plugin_update_transient( $transient ) { return $transient; } - if ( is_object( $update_data ) && isset( $update_data->new_version ) ) { - $transient->response[ $this->pro_plugin_path ] = $update_data; + if ( ! is_wp_error( $update_data ) && is_object( $update_data ) && isset( $update_data->version ) ) { + $latest_version = $update_data->version; + + if ( version_compare( $this->current_version, $latest_version, '>' ) ) { + return $transient; + } + + $transient->response[ $this->pro_plugin_path ] = $this->create_update_response_object( + array( + 'new_version' => $latest_version, + 'package' => $update_data->download_url, + 'release_notes' => $update_data->notes, + /** + * NOTE: Upgrade Notice only seems to appear on the Dashboard > Updates page + */ + 'upgrade_notice' => $this->maybe_add_upgrade_notice(), + ) + ); } - $transient->response[ $this->pro_plugin_path ] = $this->create_update_data_object( - array( - 'new_version' => '1.2.3', - 'package' => 'https://example.com/downloads/plugin-slug-2.0.0.zip', - ) - ); return $transient; } /** - * Create an update data object + * Create an update response object for the WordPress transient + * NOTE: this transient is different to the one we use to store the update data * * @param array $args { * Arguments for creating the update data object. @@ -240,7 +358,7 @@ public function modify_plugin_update_transient( $transient ) { * @type string $requires_php The version of PHP which the plugin requires. * } */ - private function create_update_data_object( $args = array() ) { + private function create_update_response_object( $args = array() ) { $defaults = array( 'id' => '0', 'slug' => 'woocommerce-pos-pro', @@ -249,6 +367,9 @@ private function create_update_data_object( $args = array() ) { 'requires' => '5.6', 'tested' => '6.5', 'requires_php' => '7.4', + 'icons' => array( + '1x' => 'https://wcpos.com/wp-content/uploads/2014/06/woopos-pro.png', + ), ); $parsed_args = wp_parse_args( $args, $defaults ); @@ -329,8 +450,8 @@ public function plugin_row_meta( $plugin_meta, $plugin_file, $plugin_data, $stat return $plugin_meta; } - $license_status = 'Your license status here'; // Fetch or generate your license status message - $plugin_meta[] = '' . esc_html( $license_status ) . ''; + // $license_status = 'Your license status here'; // Fetch or generate your license status message + // $plugin_meta[] = '' . esc_html( $license_status ) . ''; return $plugin_meta; } @@ -369,7 +490,90 @@ public function plugin_update_message( $plugin_data, $response ) { return; } - $license_status = 'Your license status here'; // Fetch or generate your license status message - echo '
' . esc_html( $license_status ) . ''; + $license_status = $this->check_license_status(); + if ( ! is_wp_error( $license_status ) ) { + return; + } + + $message = 'Your license has expired. Please renew to update.'; + + if ( $license_status->get_error_code() === 'missing_license_key' ) { + $message = $license_status->get_error_message(); + } + + echo '
' . wp_kses_post( $message ) . ''; + } + + /** + * Display the plugin information iframe for the Pro plugin + */ + public function plugin_information() { + global $tab; + + if ( empty( $_REQUEST['plugin'] ) || $this->pro_plugin_slug !== $_REQUEST['plugin'] ) { + return; + } + + $update_data = get_transient( $this->update_data_transient_key ); + $message = 'Something went wrong. Please try again later.'; + + if ( is_wp_error( $update_data ) ) { + $message = $update_data->get_error_message(); + } + + if ( is_object( $update_data ) && isset( $update_data->notes ) ) { + $parsedown = new Parsedown(); + $message = $parsedown->text( $update_data->notes ); + } + + iframe_header( __( 'Plugin Installation' ) ); + + /** + * Output some custom CSS to make the release notes look nice. + */ + echo ''; + + /** + * Output release notes from GitHub, markdown -> HTML + */ + echo '
'; + echo '
'; + echo wp_kses_post( $message ); + echo '
'; + echo "
\n"; + + /** + * Make sure any links in the content open in a new tab. + */ + echo ""; + + iframe_footer(); + exit; + } + + /** + * Maybe add a notice to the plugin update table on Dashboard > Updates + */ + private function maybe_add_upgrade_notice() { + $license_status = $this->check_license_status(); + if ( ! is_wp_error( $license_status ) ) { + return; + } + + if ( $license_status->get_error_code() === 'missing_license_key' ) { + return $license_status->get_error_message(); + } + + return 'Your license has expired. Please renew to update.'; } } diff --git a/includes/Deactivator.php b/includes/Deactivator.php index 9187c27..3d3890e 100644 --- a/includes/Deactivator.php +++ b/includes/Deactivator.php @@ -1,5 +1,4 @@ init_common(); + $this->init_frontend(); + $this->init_admin(); + $this->init_integrations(); + } + + /** + * Common initializations + */ + private function init_common() { new i18n(); new Gateways(); new Products(); // new Customers(); new Orders(); + } - // frontend only + /** + * Frontend specific initializations + */ + private function init_frontend() { if ( ! is_admin() ) { - new Templates(); - new Form_Handler(); + new Templates(); + new Form_Handler(); } + } - // NOTE: admin_menu runs before admin_init, so we need to load the Admin class here + /** + * Admin specific initializations + */ + private function init_admin() { if ( is_admin() ) { - if ( \defined( '\DOING_AJAX' ) && DOING_AJAX ) { - // AJAX requests - new AJAX(); + if ( defined( 'DOING_AJAX' ) && DOING_AJAX ) { + new AJAX(); } else { - // Non-AJAX (Admin) requests - new Admin(); + new Admin(); } } + } + + /** + * Integrations + */ + private function init_integrations() { + // WooCommerce Bookings - http://www.woothemes.com/products/woocommerce-bookings/ + // if ( class_exists( 'WC-Bookings' ) ) { + // new Integrations\Bookings(); + // } - // load integrations - $this->integrations(); + // Yoast SEO - https://wordpress.org/plugins/wordpress-seo/ + if ( class_exists( 'WPSEO_Options' ) ) { + new Integrations\WPSEO(); + } } /** * Loads the POS API and duck punches the WC REST API. */ - public function rest_api_init(): void { + public function init_rest_api(): void { if ( woocommerce_pos_request() ) { new API(); } @@ -138,19 +165,4 @@ public function send_headers(): void { header( 'Access-Control-Expose-Headers: Link' ); } } - - /** - * Loads POS integrations with third party plugins. - */ - private function integrations(): void { - // WooCommerce Bookings - http://www.woothemes.com/products/woocommerce-bookings/ - // if ( class_exists( 'WC-Bookings' ) ) { - // new Integrations\Bookings(); - // } - - // Yoast SEO - https://wordpress.org/plugins/wordpress-seo/ - if ( class_exists( 'WPSEO_Options' ) ) { - new Integrations\WPSEO(); - } - } } diff --git a/includes/Integrations/WPSEO.php b/includes/Integrations/WPSEO.php index 73eb357..7ffcae6 100644 --- a/includes/Integrations/WPSEO.php +++ b/includes/Integrations/WPSEO.php @@ -6,19 +6,19 @@ * Yoast SEO Integration */ class WPSEO { - public function __construct() { - add_filter( 'option_wpseo', array( $this, 'remove_wpseo_rest_api_links' ), 10, 1 ); - } + public function __construct() { + add_filter( 'option_wpseo', array( $this, 'remove_wpseo_rest_api_links' ), 10, 1 ); + } - /** - * Yoast SEO adds SEO to the WC REST API by default, this adds to the download weight and can cause problems - * It is programmatically turned off here for POS requests - * This gets loaded and cached before the rest_api init hook, so we can't use the filter - * - * QUESTION: How long is the WPSEO_Options cache persisted? - */ + /** + * Yoast SEO adds SEO to the WC REST API by default, this adds to the download weight and can cause problems + * It is programmatically turned off here for POS requests + * This gets loaded and cached before the rest_api init hook, so we can't use the filter + * + * QUESTION: How long is the WPSEO_Options cache persisted? + */ public function remove_wpseo_rest_api_links( $wpseo_options ) { - $wpseo_options['remove_rest_api_links'] = false; // needed for WC API discovery + $wpseo_options['remove_rest_api_links'] = false; // needed for WC API discovery if ( woocommerce_pos_request() ) { $wpseo_options['remove_rest_api_links'] = true; diff --git a/includes/Templates/Frontend.php b/includes/Templates/Frontend.php index 625525d..29e0132 100644 --- a/includes/Templates/Frontend.php +++ b/includes/Templates/Frontend.php @@ -3,16 +3,19 @@ * @author Paul Kilmurray * * @see http://wcpos.com + * @package WooCommerce POS */ namespace WCPOS\WooCommercePOS\Templates; use Ramsey\Uuid\Uuid; -use WCPOS\WooCommercePOS\Services\Store; use WCPOS\WooCommercePOS\Services\Auth; use const WCPOS\WooCommercePOS\SHORT_NAME; use const WCPOS\WooCommercePOS\VERSION; +/** + * Frontend class. + */ class Frontend { /** diff --git a/includes/wcpos-store-functions.php b/includes/wcpos-store-functions.php index fc88c60..c0e4c10 100644 --- a/includes/wcpos-store-functions.php +++ b/includes/wcpos-store-functions.php @@ -7,7 +7,7 @@ defined( 'ABSPATH' ) || exit; -use WCPOS\WooCommercePOS\Services\Store; +use WCPOS\WooCommercePOS\Abstracts\Store; /** * Standard way of retrieving stores based on certain parameters. diff --git a/php-scoper/composer.json b/php-scoper/composer.json index 8f88f5b..a3e218e 100644 --- a/php-scoper/composer.json +++ b/php-scoper/composer.json @@ -1,15 +1,11 @@ { "require-dev": { - "humbug/php-scoper": "0.13.10" + "humbug/php-scoper": "0.16.2" }, "require": { "php": ">=7.4", "ext-json": "*", - "firebase/php-jwt": "v6.9.0", - "ramsey/uuid": "4.2.3", - "salesforce/handlebars-php": "3.0.1", - "vlucas/phpdotenv": "v5.5.0", - "yahnis-elsts/plugin-update-checker": "v5.2" + "firebase/php-jwt": "v6.9.0" }, "minimum-stability": "dev", "prefer-stable": true, @@ -21,3 +17,4 @@ "sort-packages": true } } + diff --git a/php-scoper/dummy/dummy.php b/php-scoper/dummy/dummy.php new file mode 100644 index 0000000..a6d0f86 --- /dev/null +++ b/php-scoper/dummy/dummy.php @@ -0,0 +1,2 @@ +in( 'vendor/firebase/php-jwt' ) ->name( '*.php' ), // Scope only PHP files. - // Add Finder for yahnis-elsts/plugin-update-checker + /** + * If there is only one library the folder structure is not maintained :( + * By creating a dummy folder we can maintain the folder structure. + */ Finder::create()->files() - ->in( 'vendor/yahnis-elsts/plugin-update-checker' ) - ->name( '*.php' ), // Scope only PHP files. + ->in( 'dummy' ) + ->name( '*.php' ), + + // Add Finder for yahnis-elsts/plugin-update-checker + // NOTE: I ended up rolling my own update checker, so this is no longer needed. + // Finder::create()->files() + // ->in( 'vendor/yahnis-elsts/plugin-update-checker' ) + // ->name( '*.php' ), // Scope only PHP files. // Add Finder for ramsey/uuid // NOTE: UUID has too many dependencies to scope, so we'll just leave it alone. diff --git a/tests/includes/Services/Test_Store_Service.php b/tests/includes/Abstracts/Test_Store_Abstract.php similarity index 98% rename from tests/includes/Services/Test_Store_Service.php rename to tests/includes/Abstracts/Test_Store_Abstract.php index f13d9cf..9472053 100644 --- a/tests/includes/Services/Test_Store_Service.php +++ b/tests/includes/Abstracts/Test_Store_Abstract.php @@ -1,8 +1,8 @@ payload = $payload; - } - public function getPayload() : object - { - return $this->payload; - } -} diff --git a/vendor_prefixed/firebase/php-jwt/src/CachedKeySet.php b/vendor_prefixed/firebase/php-jwt/src/CachedKeySet.php deleted file mode 100644 index 87c8aa9..0000000 --- a/vendor_prefixed/firebase/php-jwt/src/CachedKeySet.php +++ /dev/null @@ -1,226 +0,0 @@ - - */ -class CachedKeySet implements \ArrayAccess -{ - /** - * @var string - */ - private $jwksUri; - /** - * @var ClientInterface - */ - private $httpClient; - /** - * @var RequestFactoryInterface - */ - private $httpFactory; - /** - * @var CacheItemPoolInterface - */ - private $cache; - /** - * @var ?int - */ - private $expiresAfter; - /** - * @var ?CacheItemInterface - */ - private $cacheItem; - /** - * @var array> - */ - private $keySet; - /** - * @var string - */ - private $cacheKey; - /** - * @var string - */ - private $cacheKeyPrefix = 'jwks'; - /** - * @var int - */ - private $maxKeyLength = 64; - /** - * @var bool - */ - private $rateLimit; - /** - * @var string - */ - private $rateLimitCacheKey; - /** - * @var int - */ - private $maxCallsPerMinute = 10; - /** - * @var string|null - */ - private $defaultAlg; - public function __construct(string $jwksUri, \WCPOS\Vendor\Psr\Http\Client\ClientInterface $httpClient, \WCPOS\Vendor\Psr\Http\Message\RequestFactoryInterface $httpFactory, \WCPOS\Vendor\Psr\Cache\CacheItemPoolInterface $cache, int $expiresAfter = null, bool $rateLimit = \false, string $defaultAlg = null) - { - $this->jwksUri = $jwksUri; - $this->httpClient = $httpClient; - $this->httpFactory = $httpFactory; - $this->cache = $cache; - $this->expiresAfter = $expiresAfter; - $this->rateLimit = $rateLimit; - $this->defaultAlg = $defaultAlg; - $this->setCacheKeys(); - } - /** - * @param string $keyId - * @return Key - */ - public function offsetGet($keyId) : \WCPOS\Vendor\Firebase\JWT\Key - { - if (!$this->keyIdExists($keyId)) { - throw new \OutOfBoundsException('Key ID not found'); - } - return \WCPOS\Vendor\Firebase\JWT\JWK::parseKey($this->keySet[$keyId], $this->defaultAlg); - } - /** - * @param string $keyId - * @return bool - */ - public function offsetExists($keyId) : bool - { - return $this->keyIdExists($keyId); - } - /** - * @param string $offset - * @param Key $value - */ - public function offsetSet($offset, $value) : void - { - throw new \LogicException('Method not implemented'); - } - /** - * @param string $offset - */ - public function offsetUnset($offset) : void - { - throw new \LogicException('Method not implemented'); - } - /** - * @return array - */ - private function formatJwksForCache(string $jwks) : array - { - $jwks = \json_decode($jwks, \true); - if (!isset($jwks['keys'])) { - throw new \UnexpectedValueException('"keys" member must exist in the JWK Set'); - } - if (empty($jwks['keys'])) { - throw new \InvalidArgumentException('JWK Set did not contain any keys'); - } - $keys = []; - foreach ($jwks['keys'] as $k => $v) { - $kid = isset($v['kid']) ? $v['kid'] : $k; - $keys[(string) $kid] = $v; - } - return $keys; - } - private function keyIdExists(string $keyId) : bool - { - if (null === $this->keySet) { - $item = $this->getCacheItem(); - // Try to load keys from cache - if ($item->isHit()) { - // item found! retrieve it - $this->keySet = $item->get(); - // If the cached item is a string, the JWKS response was cached (previous behavior). - // Parse this into expected format array instead. - if (\is_string($this->keySet)) { - $this->keySet = $this->formatJwksForCache($this->keySet); - } - } - } - if (!isset($this->keySet[$keyId])) { - if ($this->rateLimitExceeded()) { - return \false; - } - $request = $this->httpFactory->createRequest('GET', $this->jwksUri); - $jwksResponse = $this->httpClient->sendRequest($request); - if ($jwksResponse->getStatusCode() !== 200) { - throw new \UnexpectedValueException(\sprintf('HTTP Error: %d %s for URI "%s"', $jwksResponse->getStatusCode(), $jwksResponse->getReasonPhrase(), $this->jwksUri), $jwksResponse->getStatusCode()); - } - $this->keySet = $this->formatJwksForCache((string) $jwksResponse->getBody()); - if (!isset($this->keySet[$keyId])) { - return \false; - } - $item = $this->getCacheItem(); - $item->set($this->keySet); - if ($this->expiresAfter) { - $item->expiresAfter($this->expiresAfter); - } - $this->cache->save($item); - } - return \true; - } - private function rateLimitExceeded() : bool - { - if (!$this->rateLimit) { - return \false; - } - $cacheItem = $this->cache->getItem($this->rateLimitCacheKey); - if (!$cacheItem->isHit()) { - $cacheItem->expiresAfter(1); - // # of calls are cached each minute - } - $callsPerMinute = (int) $cacheItem->get(); - if (++$callsPerMinute > $this->maxCallsPerMinute) { - return \true; - } - $cacheItem->set($callsPerMinute); - $this->cache->save($cacheItem); - return \false; - } - private function getCacheItem() : \WCPOS\Vendor\Psr\Cache\CacheItemInterface - { - if (\is_null($this->cacheItem)) { - $this->cacheItem = $this->cache->getItem($this->cacheKey); - } - return $this->cacheItem; - } - private function setCacheKeys() : void - { - if (empty($this->jwksUri)) { - throw new \RuntimeException('JWKS URI is empty'); - } - // ensure we do not have illegal characters - $key = \preg_replace('|[^a-zA-Z0-9_\\.!]|', '', $this->jwksUri); - // add prefix - $key = $this->cacheKeyPrefix . $key; - // Hash keys if they exceed $maxKeyLength of 64 - if (\strlen($key) > $this->maxKeyLength) { - $key = \substr(\hash('sha256', $key), 0, $this->maxKeyLength); - } - $this->cacheKey = $key; - if ($this->rateLimit) { - // add prefix - $rateLimitKey = $this->cacheKeyPrefix . 'ratelimit' . $key; - // Hash keys if they exceed $maxKeyLength of 64 - if (\strlen($rateLimitKey) > $this->maxKeyLength) { - $rateLimitKey = \substr(\hash('sha256', $rateLimitKey), 0, $this->maxKeyLength); - } - $this->rateLimitCacheKey = $rateLimitKey; - } - } -} diff --git a/vendor_prefixed/firebase/php-jwt/src/ExpiredException.php b/vendor_prefixed/firebase/php-jwt/src/ExpiredException.php deleted file mode 100644 index b5338b0..0000000 --- a/vendor_prefixed/firebase/php-jwt/src/ExpiredException.php +++ /dev/null @@ -1,16 +0,0 @@ -payload = $payload; - } - public function getPayload() : object - { - return $this->payload; - } -} diff --git a/vendor_prefixed/firebase/php-jwt/src/JWK.php b/vendor_prefixed/firebase/php-jwt/src/JWK.php deleted file mode 100644 index 87c87b4..0000000 --- a/vendor_prefixed/firebase/php-jwt/src/JWK.php +++ /dev/null @@ -1,267 +0,0 @@ - - * @license http://opensource.org/licenses/BSD-3-Clause 3-clause BSD - * @link https://github.com/firebase/php-jwt - */ -class JWK -{ - private const OID = '1.2.840.10045.2.1'; - private const ASN1_OBJECT_IDENTIFIER = 0x6; - private const ASN1_SEQUENCE = 0x10; - // also defined in JWT - private const ASN1_BIT_STRING = 0x3; - private const EC_CURVES = [ - 'P-256' => '1.2.840.10045.3.1.7', - // Len: 64 - 'secp256k1' => '1.3.132.0.10', - // Len: 64 - 'P-384' => '1.3.132.0.34', - ]; - // For keys with "kty" equal to "OKP" (Octet Key Pair), the "crv" parameter must contain the key subtype. - // This library supports the following subtypes: - private const OKP_SUBTYPES = ['Ed25519' => \true]; - /** - * Parse a set of JWK keys - * - * @param array $jwks The JSON Web Key Set as an associative array - * @param string $defaultAlg The algorithm for the Key object if "alg" is not set in the - * JSON Web Key Set - * - * @return array An associative array of key IDs (kid) to Key objects - * - * @throws InvalidArgumentException Provided JWK Set is empty - * @throws UnexpectedValueException Provided JWK Set was invalid - * @throws DomainException OpenSSL failure - * - * @uses parseKey - */ - public static function parseKeySet(array $jwks, string $defaultAlg = null) : array - { - $keys = []; - if (!isset($jwks['keys'])) { - throw new \UnexpectedValueException('"keys" member must exist in the JWK Set'); - } - if (empty($jwks['keys'])) { - throw new \InvalidArgumentException('JWK Set did not contain any keys'); - } - foreach ($jwks['keys'] as $k => $v) { - $kid = isset($v['kid']) ? $v['kid'] : $k; - if ($key = self::parseKey($v, $defaultAlg)) { - $keys[(string) $kid] = $key; - } - } - if (0 === \count($keys)) { - throw new \UnexpectedValueException('No supported algorithms found in JWK Set'); - } - return $keys; - } - /** - * Parse a JWK key - * - * @param array $jwk An individual JWK - * @param string $defaultAlg The algorithm for the Key object if "alg" is not set in the - * JSON Web Key Set - * - * @return Key The key object for the JWK - * - * @throws InvalidArgumentException Provided JWK is empty - * @throws UnexpectedValueException Provided JWK was invalid - * @throws DomainException OpenSSL failure - * - * @uses createPemFromModulusAndExponent - */ - public static function parseKey(array $jwk, string $defaultAlg = null) : ?\WCPOS\Vendor\Firebase\JWT\Key - { - if (empty($jwk)) { - throw new \InvalidArgumentException('JWK must not be empty'); - } - if (!isset($jwk['kty'])) { - throw new \UnexpectedValueException('JWK must contain a "kty" parameter'); - } - if (!isset($jwk['alg'])) { - if (\is_null($defaultAlg)) { - // The "alg" parameter is optional in a KTY, but an algorithm is required - // for parsing in this library. Use the $defaultAlg parameter when parsing the - // key set in order to prevent this error. - // @see https://datatracker.ietf.org/doc/html/rfc7517#section-4.4 - throw new \UnexpectedValueException('JWK must contain an "alg" parameter'); - } - $jwk['alg'] = $defaultAlg; - } - switch ($jwk['kty']) { - case 'RSA': - if (!empty($jwk['d'])) { - throw new \UnexpectedValueException('RSA private keys are not supported'); - } - if (!isset($jwk['n']) || !isset($jwk['e'])) { - throw new \UnexpectedValueException('RSA keys must contain values for both "n" and "e"'); - } - $pem = self::createPemFromModulusAndExponent($jwk['n'], $jwk['e']); - $publicKey = \openssl_pkey_get_public($pem); - if (\false === $publicKey) { - throw new \DomainException('OpenSSL error: ' . \openssl_error_string()); - } - return new \WCPOS\Vendor\Firebase\JWT\Key($publicKey, $jwk['alg']); - case 'EC': - if (isset($jwk['d'])) { - // The key is actually a private key - throw new \UnexpectedValueException('Key data must be for a public key'); - } - if (empty($jwk['crv'])) { - throw new \UnexpectedValueException('crv not set'); - } - if (!isset(self::EC_CURVES[$jwk['crv']])) { - throw new \DomainException('Unrecognised or unsupported EC curve'); - } - if (empty($jwk['x']) || empty($jwk['y'])) { - throw new \UnexpectedValueException('x and y not set'); - } - $publicKey = self::createPemFromCrvAndXYCoordinates($jwk['crv'], $jwk['x'], $jwk['y']); - return new \WCPOS\Vendor\Firebase\JWT\Key($publicKey, $jwk['alg']); - case 'OKP': - if (isset($jwk['d'])) { - // The key is actually a private key - throw new \UnexpectedValueException('Key data must be for a public key'); - } - if (!isset($jwk['crv'])) { - throw new \UnexpectedValueException('crv not set'); - } - if (empty(self::OKP_SUBTYPES[$jwk['crv']])) { - throw new \DomainException('Unrecognised or unsupported OKP key subtype'); - } - if (empty($jwk['x'])) { - throw new \UnexpectedValueException('x not set'); - } - // This library works internally with EdDSA keys (Ed25519) encoded in standard base64. - $publicKey = \WCPOS\Vendor\Firebase\JWT\JWT::convertBase64urlToBase64($jwk['x']); - return new \WCPOS\Vendor\Firebase\JWT\Key($publicKey, $jwk['alg']); - default: - break; - } - return null; - } - /** - * Converts the EC JWK values to pem format. - * - * @param string $crv The EC curve (only P-256 & P-384 is supported) - * @param string $x The EC x-coordinate - * @param string $y The EC y-coordinate - * - * @return string - */ - private static function createPemFromCrvAndXYCoordinates(string $crv, string $x, string $y) : string - { - $pem = self::encodeDER(self::ASN1_SEQUENCE, self::encodeDER(self::ASN1_SEQUENCE, self::encodeDER(self::ASN1_OBJECT_IDENTIFIER, self::encodeOID(self::OID)) . self::encodeDER(self::ASN1_OBJECT_IDENTIFIER, self::encodeOID(self::EC_CURVES[$crv]))) . self::encodeDER(self::ASN1_BIT_STRING, \chr(0x0) . \chr(0x4) . \WCPOS\Vendor\Firebase\JWT\JWT::urlsafeB64Decode($x) . \WCPOS\Vendor\Firebase\JWT\JWT::urlsafeB64Decode($y))); - return \sprintf("-----BEGIN PUBLIC KEY-----\n%s\n-----END PUBLIC KEY-----\n", \wordwrap(\base64_encode($pem), 64, "\n", \true)); - } - /** - * Create a public key represented in PEM format from RSA modulus and exponent information - * - * @param string $n The RSA modulus encoded in Base64 - * @param string $e The RSA exponent encoded in Base64 - * - * @return string The RSA public key represented in PEM format - * - * @uses encodeLength - */ - private static function createPemFromModulusAndExponent(string $n, string $e) : string - { - $mod = \WCPOS\Vendor\Firebase\JWT\JWT::urlsafeB64Decode($n); - $exp = \WCPOS\Vendor\Firebase\JWT\JWT::urlsafeB64Decode($e); - $modulus = \pack('Ca*a*', 2, self::encodeLength(\strlen($mod)), $mod); - $publicExponent = \pack('Ca*a*', 2, self::encodeLength(\strlen($exp)), $exp); - $rsaPublicKey = \pack('Ca*a*a*', 48, self::encodeLength(\strlen($modulus) + \strlen($publicExponent)), $modulus, $publicExponent); - // sequence(oid(1.2.840.113549.1.1.1), null)) = rsaEncryption. - $rsaOID = \pack('H*', '300d06092a864886f70d0101010500'); - // hex version of MA0GCSqGSIb3DQEBAQUA - $rsaPublicKey = \chr(0) . $rsaPublicKey; - $rsaPublicKey = \chr(3) . self::encodeLength(\strlen($rsaPublicKey)) . $rsaPublicKey; - $rsaPublicKey = \pack('Ca*a*', 48, self::encodeLength(\strlen($rsaOID . $rsaPublicKey)), $rsaOID . $rsaPublicKey); - return "-----BEGIN PUBLIC KEY-----\r\n" . \chunk_split(\base64_encode($rsaPublicKey), 64) . '-----END PUBLIC KEY-----'; - } - /** - * DER-encode the length - * - * DER supports lengths up to (2**8)**127, however, we'll only support lengths up to (2**8)**4. See - * {@link http://itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf#p=13 X.690 paragraph 8.1.3} for more information. - * - * @param int $length - * @return string - */ - private static function encodeLength(int $length) : string - { - if ($length <= 0x7f) { - return \chr($length); - } - $temp = \ltrim(\pack('N', $length), \chr(0)); - return \pack('Ca*', 0x80 | \strlen($temp), $temp); - } - /** - * Encodes a value into a DER object. - * Also defined in Firebase\JWT\JWT - * - * @param int $type DER tag - * @param string $value the value to encode - * @return string the encoded object - */ - private static function encodeDER(int $type, string $value) : string - { - $tag_header = 0; - if ($type === self::ASN1_SEQUENCE) { - $tag_header |= 0x20; - } - // Type - $der = \chr($tag_header | $type); - // Length - $der .= \chr(\strlen($value)); - return $der . $value; - } - /** - * Encodes a string into a DER-encoded OID. - * - * @param string $oid the OID string - * @return string the binary DER-encoded OID - */ - private static function encodeOID(string $oid) : string - { - $octets = \explode('.', $oid); - // Get the first octet - $first = (int) \array_shift($octets); - $second = (int) \array_shift($octets); - $oid = \chr($first * 40 + $second); - // Iterate over subsequent octets - foreach ($octets as $octet) { - if ($octet == 0) { - $oid .= \chr(0x0); - continue; - } - $bin = ''; - while ($octet) { - $bin .= \chr(0x80 | $octet & 0x7f); - $octet >>= 7; - } - $bin[0] = $bin[0] & \chr(0x7f); - // Convert to big endian if necessary - if (\pack('V', 65534) == \pack('L', 65534)) { - $oid .= \strrev($bin); - } else { - $oid .= $bin; - } - } - return $oid; - } -} diff --git a/vendor_prefixed/firebase/php-jwt/src/JWT.php b/vendor_prefixed/firebase/php-jwt/src/JWT.php deleted file mode 100644 index ad2da08..0000000 --- a/vendor_prefixed/firebase/php-jwt/src/JWT.php +++ /dev/null @@ -1,571 +0,0 @@ - - * @author Anant Narayanan - * @license http://opensource.org/licenses/BSD-3-Clause 3-clause BSD - * @link https://github.com/firebase/php-jwt - */ -class JWT -{ - private const ASN1_INTEGER = 0x2; - private const ASN1_SEQUENCE = 0x10; - private const ASN1_BIT_STRING = 0x3; - /** - * When checking nbf, iat or expiration times, - * we want to provide some extra leeway time to - * account for clock skew. - * - * @var int - */ - public static $leeway = 0; - /** - * Allow the current timestamp to be specified. - * Useful for fixing a value within unit testing. - * Will default to PHP time() value if null. - * - * @var ?int - */ - public static $timestamp = null; - /** - * @var array - */ - public static $supported_algs = ['ES384' => ['openssl', 'SHA384'], 'ES256' => ['openssl', 'SHA256'], 'ES256K' => ['openssl', 'SHA256'], 'HS256' => ['hash_hmac', 'SHA256'], 'HS384' => ['hash_hmac', 'SHA384'], 'HS512' => ['hash_hmac', 'SHA512'], 'RS256' => ['openssl', 'SHA256'], 'RS384' => ['openssl', 'SHA384'], 'RS512' => ['openssl', 'SHA512'], 'EdDSA' => ['sodium_crypto', 'EdDSA']]; - /** - * Decodes a JWT string into a PHP object. - * - * @param string $jwt The JWT - * @param Key|ArrayAccess|array $keyOrKeyArray The Key or associative array of key IDs - * (kid) to Key objects. - * If the algorithm used is asymmetric, this is - * the public key. - * Each Key object contains an algorithm and - * matching key. - * Supported algorithms are 'ES384','ES256', - * 'HS256', 'HS384', 'HS512', 'RS256', 'RS384' - * and 'RS512'. - * @param stdClass $headers Optional. Populates stdClass with headers. - * - * @return stdClass The JWT's payload as a PHP object - * - * @throws InvalidArgumentException Provided key/key-array was empty or malformed - * @throws DomainException Provided JWT is malformed - * @throws UnexpectedValueException Provided JWT was invalid - * @throws SignatureInvalidException Provided JWT was invalid because the signature verification failed - * @throws BeforeValidException Provided JWT is trying to be used before it's eligible as defined by 'nbf' - * @throws BeforeValidException Provided JWT is trying to be used before it's been created as defined by 'iat' - * @throws ExpiredException Provided JWT has since expired, as defined by the 'exp' claim - * - * @uses jsonDecode - * @uses urlsafeB64Decode - */ - public static function decode(string $jwt, $keyOrKeyArray, \stdClass &$headers = null) : \stdClass - { - // Validate JWT - $timestamp = \is_null(static::$timestamp) ? \time() : static::$timestamp; - if (empty($keyOrKeyArray)) { - throw new \InvalidArgumentException('Key may not be empty'); - } - $tks = \explode('.', $jwt); - if (\count($tks) !== 3) { - throw new \UnexpectedValueException('Wrong number of segments'); - } - list($headb64, $bodyb64, $cryptob64) = $tks; - $headerRaw = static::urlsafeB64Decode($headb64); - if (null === ($header = static::jsonDecode($headerRaw))) { - throw new \UnexpectedValueException('Invalid header encoding'); - } - if ($headers !== null) { - $headers = $header; - } - $payloadRaw = static::urlsafeB64Decode($bodyb64); - if (null === ($payload = static::jsonDecode($payloadRaw))) { - throw new \UnexpectedValueException('Invalid claims encoding'); - } - if (\is_array($payload)) { - // prevent PHP Fatal Error in edge-cases when payload is empty array - $payload = (object) $payload; - } - if (!$payload instanceof \stdClass) { - throw new \UnexpectedValueException('Payload must be a JSON object'); - } - $sig = static::urlsafeB64Decode($cryptob64); - if (empty($header->alg)) { - throw new \UnexpectedValueException('Empty algorithm'); - } - if (empty(static::$supported_algs[$header->alg])) { - throw new \UnexpectedValueException('Algorithm not supported'); - } - $key = self::getKey($keyOrKeyArray, \property_exists($header, 'kid') ? $header->kid : null); - // Check the algorithm - if (!self::constantTimeEquals($key->getAlgorithm(), $header->alg)) { - // See issue #351 - throw new \UnexpectedValueException('Incorrect key for this algorithm'); - } - if (\in_array($header->alg, ['ES256', 'ES256K', 'ES384'], \true)) { - // OpenSSL expects an ASN.1 DER sequence for ES256/ES256K/ES384 signatures - $sig = self::signatureToDER($sig); - } - if (!self::verify("{$headb64}.{$bodyb64}", $sig, $key->getKeyMaterial(), $header->alg)) { - throw new \WCPOS\Vendor\Firebase\JWT\SignatureInvalidException('Signature verification failed'); - } - // Check the nbf if it is defined. This is the time that the - // token can actually be used. If it's not yet that time, abort. - if (isset($payload->nbf) && \floor($payload->nbf) > $timestamp + static::$leeway) { - $ex = new \WCPOS\Vendor\Firebase\JWT\BeforeValidException('Cannot handle token with nbf prior to ' . \date(\DateTime::ISO8601, (int) $payload->nbf)); - $ex->setPayload($payload); - throw $ex; - } - // Check that this token has been created before 'now'. This prevents - // using tokens that have been created for later use (and haven't - // correctly used the nbf claim). - if (!isset($payload->nbf) && isset($payload->iat) && \floor($payload->iat) > $timestamp + static::$leeway) { - $ex = new \WCPOS\Vendor\Firebase\JWT\BeforeValidException('Cannot handle token with iat prior to ' . \date(\DateTime::ISO8601, (int) $payload->iat)); - $ex->setPayload($payload); - throw $ex; - } - // Check if this token has expired. - if (isset($payload->exp) && $timestamp - static::$leeway >= $payload->exp) { - $ex = new \WCPOS\Vendor\Firebase\JWT\ExpiredException('Expired token'); - $ex->setPayload($payload); - throw $ex; - } - return $payload; - } - /** - * Converts and signs a PHP array into a JWT string. - * - * @param array $payload PHP array - * @param string|resource|OpenSSLAsymmetricKey|OpenSSLCertificate $key The secret key. - * @param string $alg Supported algorithms are 'ES384','ES256', 'ES256K', 'HS256', - * 'HS384', 'HS512', 'RS256', 'RS384', and 'RS512' - * @param string $keyId - * @param array $head An array with header elements to attach - * - * @return string A signed JWT - * - * @uses jsonEncode - * @uses urlsafeB64Encode - */ - public static function encode(array $payload, $key, string $alg, string $keyId = null, array $head = null) : string - { - $header = ['typ' => 'JWT', 'alg' => $alg]; - if ($keyId !== null) { - $header['kid'] = $keyId; - } - if (isset($head) && \is_array($head)) { - $header = \array_merge($head, $header); - } - $segments = []; - $segments[] = static::urlsafeB64Encode((string) static::jsonEncode($header)); - $segments[] = static::urlsafeB64Encode((string) static::jsonEncode($payload)); - $signing_input = \implode('.', $segments); - $signature = static::sign($signing_input, $key, $alg); - $segments[] = static::urlsafeB64Encode($signature); - return \implode('.', $segments); - } - /** - * Sign a string with a given key and algorithm. - * - * @param string $msg The message to sign - * @param string|resource|OpenSSLAsymmetricKey|OpenSSLCertificate $key The secret key. - * @param string $alg Supported algorithms are 'EdDSA', 'ES384', 'ES256', 'ES256K', 'HS256', - * 'HS384', 'HS512', 'RS256', 'RS384', and 'RS512' - * - * @return string An encrypted message - * - * @throws DomainException Unsupported algorithm or bad key was specified - */ - public static function sign(string $msg, $key, string $alg) : string - { - if (empty(static::$supported_algs[$alg])) { - throw new \DomainException('Algorithm not supported'); - } - list($function, $algorithm) = static::$supported_algs[$alg]; - switch ($function) { - case 'hash_hmac': - if (!\is_string($key)) { - throw new \InvalidArgumentException('key must be a string when using hmac'); - } - return \hash_hmac($algorithm, $msg, $key, \true); - case 'openssl': - $signature = ''; - $success = \openssl_sign($msg, $signature, $key, $algorithm); - // @phpstan-ignore-line - if (!$success) { - throw new \DomainException('OpenSSL unable to sign data'); - } - if ($alg === 'ES256' || $alg === 'ES256K') { - $signature = self::signatureFromDER($signature, 256); - } elseif ($alg === 'ES384') { - $signature = self::signatureFromDER($signature, 384); - } - return $signature; - case 'sodium_crypto': - if (!\function_exists('sodium_crypto_sign_detached')) { - throw new \DomainException('libsodium is not available'); - } - if (!\is_string($key)) { - throw new \InvalidArgumentException('key must be a string when using EdDSA'); - } - try { - // The last non-empty line is used as the key. - $lines = \array_filter(\explode("\n", $key)); - $key = \base64_decode((string) \end($lines)); - if (\strlen($key) === 0) { - throw new \DomainException('Key cannot be empty string'); - } - return \sodium_crypto_sign_detached($msg, $key); - } catch (\Exception $e) { - throw new \DomainException($e->getMessage(), 0, $e); - } - } - throw new \DomainException('Algorithm not supported'); - } - /** - * Verify a signature with the message, key and method. Not all methods - * are symmetric, so we must have a separate verify and sign method. - * - * @param string $msg The original message (header and body) - * @param string $signature The original signature - * @param string|resource|OpenSSLAsymmetricKey|OpenSSLCertificate $keyMaterial For Ed*, ES*, HS*, a string key works. for RS*, must be an instance of OpenSSLAsymmetricKey - * @param string $alg The algorithm - * - * @return bool - * - * @throws DomainException Invalid Algorithm, bad key, or OpenSSL failure - */ - private static function verify(string $msg, string $signature, $keyMaterial, string $alg) : bool - { - if (empty(static::$supported_algs[$alg])) { - throw new \DomainException('Algorithm not supported'); - } - list($function, $algorithm) = static::$supported_algs[$alg]; - switch ($function) { - case 'openssl': - $success = \openssl_verify($msg, $signature, $keyMaterial, $algorithm); - // @phpstan-ignore-line - if ($success === 1) { - return \true; - } - if ($success === 0) { - return \false; - } - // returns 1 on success, 0 on failure, -1 on error. - throw new \DomainException('OpenSSL error: ' . \openssl_error_string()); - case 'sodium_crypto': - if (!\function_exists('sodium_crypto_sign_verify_detached')) { - throw new \DomainException('libsodium is not available'); - } - if (!\is_string($keyMaterial)) { - throw new \InvalidArgumentException('key must be a string when using EdDSA'); - } - try { - // The last non-empty line is used as the key. - $lines = \array_filter(\explode("\n", $keyMaterial)); - $key = \base64_decode((string) \end($lines)); - if (\strlen($key) === 0) { - throw new \DomainException('Key cannot be empty string'); - } - if (\strlen($signature) === 0) { - throw new \DomainException('Signature cannot be empty string'); - } - return \sodium_crypto_sign_verify_detached($signature, $msg, $key); - } catch (\Exception $e) { - throw new \DomainException($e->getMessage(), 0, $e); - } - case 'hash_hmac': - default: - if (!\is_string($keyMaterial)) { - throw new \InvalidArgumentException('key must be a string when using hmac'); - } - $hash = \hash_hmac($algorithm, $msg, $keyMaterial, \true); - return self::constantTimeEquals($hash, $signature); - } - } - /** - * Decode a JSON string into a PHP object. - * - * @param string $input JSON string - * - * @return mixed The decoded JSON string - * - * @throws DomainException Provided string was invalid JSON - */ - public static function jsonDecode(string $input) - { - $obj = \json_decode($input, \false, 512, \JSON_BIGINT_AS_STRING); - if ($errno = \json_last_error()) { - self::handleJsonError($errno); - } elseif ($obj === null && $input !== 'null') { - throw new \DomainException('Null result with non-null input'); - } - return $obj; - } - /** - * Encode a PHP array into a JSON string. - * - * @param array $input A PHP array - * - * @return string JSON representation of the PHP array - * - * @throws DomainException Provided object could not be encoded to valid JSON - */ - public static function jsonEncode(array $input) : string - { - if (\PHP_VERSION_ID >= 50400) { - $json = \json_encode($input, \JSON_UNESCAPED_SLASHES); - } else { - // PHP 5.3 only - $json = \json_encode($input); - } - if ($errno = \json_last_error()) { - self::handleJsonError($errno); - } elseif ($json === 'null') { - throw new \DomainException('Null result with non-null input'); - } - if ($json === \false) { - throw new \DomainException('Provided object could not be encoded to valid JSON'); - } - return $json; - } - /** - * Decode a string with URL-safe Base64. - * - * @param string $input A Base64 encoded string - * - * @return string A decoded string - * - * @throws InvalidArgumentException invalid base64 characters - */ - public static function urlsafeB64Decode(string $input) : string - { - return \base64_decode(self::convertBase64UrlToBase64($input)); - } - /** - * Convert a string in the base64url (URL-safe Base64) encoding to standard base64. - * - * @param string $input A Base64 encoded string with URL-safe characters (-_ and no padding) - * - * @return string A Base64 encoded string with standard characters (+/) and padding (=), when - * needed. - * - * @see https://www.rfc-editor.org/rfc/rfc4648 - */ - public static function convertBase64UrlToBase64(string $input) : string - { - $remainder = \strlen($input) % 4; - if ($remainder) { - $padlen = 4 - $remainder; - $input .= \str_repeat('=', $padlen); - } - return \strtr($input, '-_', '+/'); - } - /** - * Encode a string with URL-safe Base64. - * - * @param string $input The string you want encoded - * - * @return string The base64 encode of what you passed in - */ - public static function urlsafeB64Encode(string $input) : string - { - return \str_replace('=', '', \strtr(\base64_encode($input), '+/', '-_')); - } - /** - * Determine if an algorithm has been provided for each Key - * - * @param Key|ArrayAccess|array $keyOrKeyArray - * @param string|null $kid - * - * @throws UnexpectedValueException - * - * @return Key - */ - private static function getKey($keyOrKeyArray, ?string $kid) : \WCPOS\Vendor\Firebase\JWT\Key - { - if ($keyOrKeyArray instanceof \WCPOS\Vendor\Firebase\JWT\Key) { - return $keyOrKeyArray; - } - if (empty($kid) && $kid !== '0') { - throw new \UnexpectedValueException('"kid" empty, unable to lookup correct key'); - } - if ($keyOrKeyArray instanceof \WCPOS\Vendor\Firebase\JWT\CachedKeySet) { - // Skip "isset" check, as this will automatically refresh if not set - return $keyOrKeyArray[$kid]; - } - if (!isset($keyOrKeyArray[$kid])) { - throw new \UnexpectedValueException('"kid" invalid, unable to lookup correct key'); - } - return $keyOrKeyArray[$kid]; - } - /** - * @param string $left The string of known length to compare against - * @param string $right The user-supplied string - * @return bool - */ - public static function constantTimeEquals(string $left, string $right) : bool - { - if (\function_exists('hash_equals')) { - return \hash_equals($left, $right); - } - $len = \min(self::safeStrlen($left), self::safeStrlen($right)); - $status = 0; - for ($i = 0; $i < $len; $i++) { - $status |= \ord($left[$i]) ^ \ord($right[$i]); - } - $status |= self::safeStrlen($left) ^ self::safeStrlen($right); - return $status === 0; - } - /** - * Helper method to create a JSON error. - * - * @param int $errno An error number from json_last_error() - * - * @throws DomainException - * - * @return void - */ - private static function handleJsonError(int $errno) : void - { - $messages = [\JSON_ERROR_DEPTH => 'Maximum stack depth exceeded', \JSON_ERROR_STATE_MISMATCH => 'Invalid or malformed JSON', \JSON_ERROR_CTRL_CHAR => 'Unexpected control character found', \JSON_ERROR_SYNTAX => 'Syntax error, malformed JSON', \JSON_ERROR_UTF8 => 'Malformed UTF-8 characters']; - throw new \DomainException(isset($messages[$errno]) ? $messages[$errno] : 'Unknown JSON error: ' . $errno); - } - /** - * Get the number of bytes in cryptographic strings. - * - * @param string $str - * - * @return int - */ - private static function safeStrlen(string $str) : int - { - if (\function_exists('mb_strlen')) { - return \mb_strlen($str, '8bit'); - } - return \strlen($str); - } - /** - * Convert an ECDSA signature to an ASN.1 DER sequence - * - * @param string $sig The ECDSA signature to convert - * @return string The encoded DER object - */ - private static function signatureToDER(string $sig) : string - { - // Separate the signature into r-value and s-value - $length = \max(1, (int) (\strlen($sig) / 2)); - list($r, $s) = \str_split($sig, $length); - // Trim leading zeros - $r = \ltrim($r, "\x00"); - $s = \ltrim($s, "\x00"); - // Convert r-value and s-value from unsigned big-endian integers to - // signed two's complement - if (\ord($r[0]) > 0x7f) { - $r = "\x00" . $r; - } - if (\ord($s[0]) > 0x7f) { - $s = "\x00" . $s; - } - return self::encodeDER(self::ASN1_SEQUENCE, self::encodeDER(self::ASN1_INTEGER, $r) . self::encodeDER(self::ASN1_INTEGER, $s)); - } - /** - * Encodes a value into a DER object. - * - * @param int $type DER tag - * @param string $value the value to encode - * - * @return string the encoded object - */ - private static function encodeDER(int $type, string $value) : string - { - $tag_header = 0; - if ($type === self::ASN1_SEQUENCE) { - $tag_header |= 0x20; - } - // Type - $der = \chr($tag_header | $type); - // Length - $der .= \chr(\strlen($value)); - return $der . $value; - } - /** - * Encodes signature from a DER object. - * - * @param string $der binary signature in DER format - * @param int $keySize the number of bits in the key - * - * @return string the signature - */ - private static function signatureFromDER(string $der, int $keySize) : string - { - // OpenSSL returns the ECDSA signatures as a binary ASN.1 DER SEQUENCE - list($offset, $_) = self::readDER($der); - list($offset, $r) = self::readDER($der, $offset); - list($offset, $s) = self::readDER($der, $offset); - // Convert r-value and s-value from signed two's compliment to unsigned - // big-endian integers - $r = \ltrim($r, "\x00"); - $s = \ltrim($s, "\x00"); - // Pad out r and s so that they are $keySize bits long - $r = \str_pad($r, $keySize / 8, "\x00", \STR_PAD_LEFT); - $s = \str_pad($s, $keySize / 8, "\x00", \STR_PAD_LEFT); - return $r . $s; - } - /** - * Reads binary DER-encoded data and decodes into a single object - * - * @param string $der the binary data in DER format - * @param int $offset the offset of the data stream containing the object - * to decode - * - * @return array{int, string|null} the new offset and the decoded object - */ - private static function readDER(string $der, int $offset = 0) : array - { - $pos = $offset; - $size = \strlen($der); - $constructed = \ord($der[$pos]) >> 5 & 0x1; - $type = \ord($der[$pos++]) & 0x1f; - // Length - $len = \ord($der[$pos++]); - if ($len & 0x80) { - $n = $len & 0x1f; - $len = 0; - while ($n-- && $pos < $size) { - $len = $len << 8 | \ord($der[$pos++]); - } - } - // Value - if ($type === self::ASN1_BIT_STRING) { - $pos++; - // Skip the first contents octet (padding indicator) - $data = \substr($der, $pos, $len - 1); - $pos += $len - 1; - } elseif (!$constructed) { - $data = \substr($der, $pos, $len); - $pos += $len; - } else { - $data = null; - } - return [$pos, $data]; - } -} diff --git a/vendor_prefixed/firebase/php-jwt/src/JWTExceptionWithPayloadInterface.php b/vendor_prefixed/firebase/php-jwt/src/JWTExceptionWithPayloadInterface.php deleted file mode 100644 index c28679a..0000000 --- a/vendor_prefixed/firebase/php-jwt/src/JWTExceptionWithPayloadInterface.php +++ /dev/null @@ -1,20 +0,0 @@ -keyMaterial = $keyMaterial; - $this->algorithm = $algorithm; - } - /** - * Return the algorithm valid for this key - * - * @return string - */ - public function getAlgorithm() : string - { - return $this->algorithm; - } - /** - * @return string|resource|OpenSSLAsymmetricKey|OpenSSLCertificate - */ - public function getKeyMaterial() - { - return $this->keyMaterial; - } -} diff --git a/vendor_prefixed/firebase/php-jwt/src/SignatureInvalidException.php b/vendor_prefixed/firebase/php-jwt/src/SignatureInvalidException.php deleted file mode 100644 index db82bf3..0000000 --- a/vendor_prefixed/firebase/php-jwt/src/SignatureInvalidException.php +++ /dev/null @@ -1,7 +0,0 @@ -rootDir = \dirname(__FILE__) . '/'; - $namespaceWithSlash = __NAMESPACE__ . '\\'; - $this->prefix = $namespaceWithSlash; - $this->libraryDir = $this->rootDir . '../..'; - if (!self::isPhar()) { - $this->libraryDir = \realpath($this->libraryDir); - } - $this->libraryDir = $this->libraryDir . '/'; - //Usually, dependencies like Parsedown are in the global namespace, - //but if someone adds a custom namespace to the entire library, they - //will be in the same namespace as this class. - $isCustomNamespace = \substr($namespaceWithSlash, 0, \strlen(self::DEFAULT_NS_PREFIX)) !== self::DEFAULT_NS_PREFIX; - $libraryPrefix = $isCustomNamespace ? $namespaceWithSlash : ''; - $this->staticMap = array($libraryPrefix . 'PucReadmeParser' => 'vendor/PucReadmeParser.php', $libraryPrefix . 'Parsedown' => 'vendor/Parsedown.php'); - //Add the generic, major-version-only factory class to the static map. - $versionSeparatorPos = \strrpos(__NAMESPACE__, '\\v'); - if ($versionSeparatorPos !== \false) { - $versionSegment = \substr(__NAMESPACE__, $versionSeparatorPos + 1); - $pointPos = \strpos($versionSegment, 'p'); - if ($pointPos !== \false && $pointPos > 1) { - $majorVersionSegment = \substr($versionSegment, 0, $pointPos); - $majorVersionNs = __NAMESPACE__ . '\\' . $majorVersionSegment; - $this->staticMap[$majorVersionNs . '\\PucFactory'] = 'Puc/' . $majorVersionSegment . '/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])) { - include $this->libraryDir . $this->staticMap[$className]; - return; - } - if (\strpos($className, $this->prefix) === 0) { - $path = \substr($className, \strlen($this->prefix)); - $path = \str_replace(array('_', '\\'), '/', $path); - $path = $this->rootDir . $path . '.php'; - if (\file_exists($path)) { - include $path; - } - } - } - } -} diff --git a/vendor_prefixed/yahnis-elsts/plugin-update-checker/Puc/v5p2/DebugBar/Extension.php b/vendor_prefixed/yahnis-elsts/plugin-update-checker/Puc/v5p2/DebugBar/Extension.php deleted file mode 100644 index 3282e72..0000000 --- a/vendor_prefixed/yahnis-elsts/plugin-update-checker/Puc/v5p2/DebugBar/Extension.php +++ /dev/null @@ -1,163 +0,0 @@ -updateChecker = $updateChecker; - if (isset($panelClass)) { - $this->panelClass = $panelClass; - } - if (\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_v5_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-v5', $this->getLibraryUrl("/css/puc-debug-bar.css"), array('debug-bar'), '20221008'); - wp_enqueue_script('puc-debug-bar-js-v5', $this->getLibraryUrl("/js/debug-bar.js"), array('jquery'), '20221008'); - } - /** - * Run an update check and output the result. Useful for making sure that - * the update checking process works as expected. - */ - public function ajaxCheckNow() - { - //phpcs:ignore WordPress.Security.NonceVerification.Missing -- Nonce is checked in preAjaxRequest(). - if (!isset($_POST['uid']) || $_POST['uid'] !== $this->updateChecker->getUniqueName('uid')) { - return; - } - $this->preAjaxRequest(); - $update = $this->updateChecker->checkForUpdates(); - if ($update !== null) { - echo "An update is available:"; - //phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_print_r -- For debugging output. - echo '
', esc_html(\print_r($update, \true)), '
'; - } else { - echo 'No updates found.'; - } - $errors = $this->updateChecker->getLastRequestApiErrors(); - if (!empty($errors)) { - \printf('

The update checker encountered %d API error%s.

', \count($errors), \count($errors) > 1 ? 's' : ''); - foreach (\array_values($errors) as $num => $item) { - $wpError = $item['error']; - /** @var \WP_Error $wpError */ - \printf('

%d) %s

', \intval($num + 1), esc_html($wpError->get_error_message())); - echo '
'; - \printf('
Error code:
%s
', esc_html($wpError->get_error_code())); - if (isset($item['url'])) { - \printf('
Requested URL:
%s
', esc_html($item['url'])); - } - if (isset($item['httpResponse'])) { - if (is_wp_error($item['httpResponse'])) { - $httpError = $item['httpResponse']; - /** @var \WP_Error $httpError */ - \printf('
WordPress HTTP API error:
%s (%s)
', esc_html($httpError->get_error_message()), esc_html($httpError->get_error_code())); - } else { - //Status code. - \printf('
HTTP status:
%d %s
', esc_html(wp_remote_retrieve_response_code($item['httpResponse'])), esc_html(wp_remote_retrieve_response_message($item['httpResponse']))); - //Headers. - echo '
Response headers:
';
-                            foreach (wp_remote_retrieve_headers($item['httpResponse']) as $name => $value) {
-                                \printf("%s: %s\n", esc_html($name), esc_html($value));
-                            }
-                            echo '
'; - //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('
Response body:
%s
', esc_html($body)); - } - } - echo '
'; - } - } - 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'); - //phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.runtime_configuration_error_reporting -- Part of a debugging feature. - \error_reporting(\E_ALL); - //phpcs:ignore WordPress.PHP.IniSet.display_errors_Blacklisted - @\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_v5_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 = \WCPOS\Vendor\YahnisElsts\PluginUpdateChecker\v5p2\PucFactory::normalizePath($absolutePath); - $pluginDir = \WCPOS\Vendor\YahnisElsts\PluginUpdateChecker\v5p2\PucFactory::normalizePath(WP_PLUGIN_DIR); - $muPluginDir = \WCPOS\Vendor\YahnisElsts\PluginUpdateChecker\v5p2\PucFactory::normalizePath(WPMU_PLUGIN_DIR); - $themeDir = \WCPOS\Vendor\YahnisElsts\PluginUpdateChecker\v5p2\PucFactory::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 ''; - } - } -} diff --git a/vendor_prefixed/yahnis-elsts/plugin-update-checker/Puc/v5p2/DebugBar/Panel.php b/vendor_prefixed/yahnis-elsts/plugin-update-checker/Puc/v5p2/DebugBar/Panel.php deleted file mode 100644 index 38322be..0000000 --- a/vendor_prefixed/yahnis-elsts/plugin-update-checker/Puc/v5p2/DebugBar/Panel.php +++ /dev/null @@ -1,141 +0,0 @@ -'; - public function __construct($updateChecker) - { - $this->updateChecker = $updateChecker; - $title = \sprintf('PUC (%s)', esc_attr($this->updateChecker->getUniqueName('uid')), $this->updateChecker->slug); - parent::__construct($title); - } - public function render() - { - \printf('
', 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 '
'; - } - private function displayConfiguration() - { - echo '

Configuration

'; - echo ''; - $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 '
'; - } - protected function displayConfigHeader() - { - //Do nothing. This should be implemented in subclasses. - } - protected function getMetadataButton() - { - return ''; - } - private function displayStatus() - { - echo '

Status

'; - echo ''; - $state = $this->updateChecker->getUpdateState(); - $checkNowButton = ''; - if (\function_exists('WCPOS\\Vendor\\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 '
'; - } - private function displayCurrentUpdate() - { - $update = $this->updateChecker->getUpdate(); - if ($update !== null) { - echo '

An Update Is Available

'; - echo ''; - $fields = $this->getUpdateFields(); - foreach ($fields as $field) { - if (\property_exists($update, $field)) { - $this->row(\ucwords(\str_replace('_', ' ', $field)), isset($update->{$field}) ? \htmlentities($update->{$field}) : null); - } - } - echo '
'; - } else { - echo '

No updates currently available

'; - } - } - 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)) { - //This is specifically for debugging, so print_r() is fine. - //phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_print_r - $value = '
' . \htmlentities(\print_r($value, \true)) . '
'; - } else { - if ($value === null) { - $value = 'null'; - } - } - \printf( - '%1$s %2$s', - esc_html($name), - //phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Escaped above. - $value - ); - } - } -} diff --git a/vendor_prefixed/yahnis-elsts/plugin-update-checker/Puc/v5p2/DebugBar/PluginExtension.php b/vendor_prefixed/yahnis-elsts/plugin-update-checker/Puc/v5p2/DebugBar/PluginExtension.php deleted file mode 100644 index 45964d3..0000000 --- a/vendor_prefixed/yahnis-elsts/plugin-update-checker/Puc/v5p2/DebugBar/PluginExtension.php +++ /dev/null @@ -1,37 +0,0 @@ -updateChecker->getUniqueName('uid')) { - return; - } - $this->preAjaxRequest(); - $info = $this->updateChecker->requestInfo(); - if ($info !== null) { - echo 'Successfully retrieved plugin info from the metadata URL:'; - //phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_print_r -- For debugging output. - echo '
', esc_html(\print_r($info, \true)), '
'; - } else { - echo 'Failed to retrieve plugin info from the metadata URL.'; - } - exit; - } - } -} diff --git a/vendor_prefixed/yahnis-elsts/plugin-update-checker/Puc/v5p2/DebugBar/PluginPanel.php b/vendor_prefixed/yahnis-elsts/plugin-update-checker/Puc/v5p2/DebugBar/PluginPanel.php deleted file mode 100644 index b9e8112..0000000 --- a/vendor_prefixed/yahnis-elsts/plugin-update-checker/Puc/v5p2/DebugBar/PluginPanel.php +++ /dev/null @@ -1,31 +0,0 @@ -row('Plugin file', \htmlentities($this->updateChecker->pluginFile)); - parent::displayConfigHeader(); - } - protected function getMetadataButton() - { - $requestInfoButton = ''; - if (\function_exists('WCPOS\\Vendor\\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')); - } - } -} diff --git a/vendor_prefixed/yahnis-elsts/plugin-update-checker/Puc/v5p2/DebugBar/ThemePanel.php b/vendor_prefixed/yahnis-elsts/plugin-update-checker/Puc/v5p2/DebugBar/ThemePanel.php deleted file mode 100644 index 99b3f96..0000000 --- a/vendor_prefixed/yahnis-elsts/plugin-update-checker/Puc/v5p2/DebugBar/ThemePanel.php +++ /dev/null @@ -1,23 +0,0 @@ -row('Theme directory', \htmlentities($this->updateChecker->directoryName)); - parent::displayConfigHeader(); - } - protected function getUpdateFields() - { - return \array_merge(parent::getUpdateFields(), array('details_url')); - } - } -} diff --git a/vendor_prefixed/yahnis-elsts/plugin-update-checker/Puc/v5p2/InstalledPackage.php b/vendor_prefixed/yahnis-elsts/plugin-update-checker/Puc/v5p2/InstalledPackage.php deleted file mode 100644 index 81d9430..0000000 --- a/vendor_prefixed/yahnis-elsts/plugin-update-checker/Puc/v5p2/InstalledPackage.php +++ /dev/null @@ -1,92 +0,0 @@ -updateChecker = $updateChecker; - } - /** - * Get the currently installed version of the plugin or theme. - * - * @return string|null Version number. - */ - public abstract function getInstalledVersion(); - /** - * Get the full path of the plugin or theme directory (without a trailing slash). - * - * @return string - */ - public abstract 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('WCPOS\\Vendor\\_cleanup_header_comment')) { - $value = _cleanup_header_comment($value); - } - $results[$field] = $value; - } else { - $results[$field] = ''; - } - } - return $results; - } - /** - * @return array Format: ['HeaderKey' => 'Header Name'] - */ - protected abstract 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. - */ - public abstract function getHeaderValue($headerName); - } -} diff --git a/vendor_prefixed/yahnis-elsts/plugin-update-checker/Puc/v5p2/Metadata.php b/vendor_prefixed/yahnis-elsts/plugin-update-checker/Puc/v5p2/Metadata.php deleted file mode 100644 index b01f349..0000000 --- a/vendor_prefixed/yahnis-elsts/plugin-update-checker/Puc/v5p2/Metadata.php +++ /dev/null @@ -1,155 +0,0 @@ - - */ - protected $extraProperties = array(); - /** - * Create an instance of this class from a JSON document. - * - * @abstract - * @param string $json - * @return self - */ - public static function fromJson($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 https://jsonlint.com/"; - do_action('puc_api_error', new \WCPOS\Vendor\WP_Error('puc-invalid-json', $errorMessage)); - //phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_trigger_error -- For plugin developers. - \trigger_error(esc_html($errorMessage), \E_USER_NOTICE); - return \false; - } - $valid = $target->validateMetadata($apiResponse); - if (is_wp_error($valid)) { - do_action('puc_api_error', $valid); - //phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_trigger_error -- For plugin developers. - \trigger_error(esc_html($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($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($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 - */ - public abstract 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; - } - public function __set($name, $value) - { - $this->extraProperties[$name] = $value; - } - public function __get($name) - { - return isset($this->extraProperties[$name]) ? $this->extraProperties[$name] : null; - } - public function __isset($name) - { - return isset($this->extraProperties[$name]); - } - public function __unset($name) - { - unset($this->extraProperties[$name]); - } - } -} diff --git a/vendor_prefixed/yahnis-elsts/plugin-update-checker/Puc/v5p2/OAuthSignature.php b/vendor_prefixed/yahnis-elsts/plugin-update-checker/Puc/v5p2/OAuthSignature.php deleted file mode 100644 index 65c6479..0000000 --- a/vendor_prefixed/yahnis-elsts/plugin-update-checker/Puc/v5p2/OAuthSignature.php +++ /dev/null @@ -1,83 +0,0 @@ -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 = wp_parse_url($url, \PHP_URL_QUERY); - if (!empty($query)) { - \parse_str($query, $parsedParams); - if (\is_array($parsedParams)) { - $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) { - //phpcs:ignore WordPress.WP.AlternativeFunctions.rand_mt_rand - $rand = \function_exists('WCPOS\\Vendor\\wp_rand') ? wp_rand() : \mt_rand(); - } - return \md5($mt . '_' . $rand); - } - } -} diff --git a/vendor_prefixed/yahnis-elsts/plugin-update-checker/Puc/v5p2/Plugin/Package.php b/vendor_prefixed/yahnis-elsts/plugin-update-checker/Puc/v5p2/Plugin/Package.php deleted file mode 100644 index 0b36f13..0000000 --- a/vendor_prefixed/yahnis-elsts/plugin-update-checker/Puc/v5p2/Plugin/Package.php +++ /dev/null @@ -1,163 +0,0 @@ -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("Cannot 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('WCPOS\\Vendor\\get_plugin_data')) { - 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 = \WCPOS\Vendor\YahnisElsts\PluginUpdateChecker\v5p2\PucFactory::normalizePath(WPMU_PLUGIN_DIR); - $pluginPath = \WCPOS\Vendor\YahnisElsts\PluginUpdateChecker\v5p2\PucFactory::normalizePath($this->pluginAbsolutePath); - } - $cachedResult = \strpos($pluginPath, $muPluginDir) === 0; - } - return $cachedResult; - } - } -} diff --git a/vendor_prefixed/yahnis-elsts/plugin-update-checker/Puc/v5p2/Plugin/PluginInfo.php b/vendor_prefixed/yahnis-elsts/plugin-update-checker/Puc/v5p2/Plugin/PluginInfo.php deleted file mode 100644 index d80e3fd..0000000 --- a/vendor_prefixed/yahnis-elsts/plugin-update-checker/Puc/v5p2/Plugin/PluginInfo.php +++ /dev/null @@ -1,112 +0,0 @@ -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 \WCPOS\Vendor\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('
%s', $this->author_homepage, $this->author); - } - return $this->author; - } - } -} diff --git a/vendor_prefixed/yahnis-elsts/plugin-update-checker/Puc/v5p2/Plugin/Ui.php b/vendor_prefixed/yahnis-elsts/plugin-update-checker/Puc/v5p2/Plugin/Ui.php deleted file mode 100644 index d2430e5..0000000 --- a/vendor_prefixed/yahnis-elsts/plugin-update-checker/Puc/v5p2/Plugin/Ui.php +++ /dev/null @@ -1,240 +0,0 @@ -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('%s', 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('%s', 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() - { - //phpcs:disable WordPress.Security.NonceVerification.Recommended -- Just displaying a message. - if (isset($_GET['puc_update_check_result'], $_GET['puc_slug']) && $_GET['puc_slug'] == $this->updateChecker->slug) { - $status = sanitize_key($_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'), $status); - $noticeClass = 'error notice-error'; - } - } - } - $message = esc_html($message); - //Plugins can replace the message with their own, including adding HTML. - $message = apply_filters($this->updateChecker->getUniqueName('manual_check_message'), $message, $status); - \printf( - '

%s

%s
', - esc_attr($noticeClass), - //phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Was escaped above, and plugins can add HTML. - $message, - //phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Contains HTML. Content should already be escaped. - $details - ); - } - //phpcs:enable - } - /** - * 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 .= '
    '; - $formatString = '
  1. %1$s %2$s
  2. '; - } else { - $formatString = '

    %1$s %2$s

    '; - } - foreach ($errors as $item) { - $wpError = $item['error']; - /** @var \WP_Error $wpError */ - $output .= \sprintf($formatString, esc_html($wpError->get_error_message()), esc_html($wpError->get_error_code())); - } - if ($showAsList) { - $output .= '
'; - } - 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')); - } - } -} diff --git a/vendor_prefixed/yahnis-elsts/plugin-update-checker/Puc/v5p2/Plugin/Update.php b/vendor_prefixed/yahnis-elsts/plugin-update-checker/Puc/v5p2/Plugin/Update.php deleted file mode 100644 index 8b0af48..0000000 --- a/vendor_prefixed/yahnis-elsts/plugin-update-checker/Puc/v5p2/Plugin/Update.php +++ /dev/null @@ -1,105 +0,0 @@ -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; - } - } -} diff --git a/vendor_prefixed/yahnis-elsts/plugin-update-checker/Puc/v5p2/Plugin/UpdateChecker.php b/vendor_prefixed/yahnis-elsts/plugin-update-checker/Puc/v5p2/Plugin/UpdateChecker.php deleted file mode 100644 index 59cf6d7..0000000 --- a/vendor_prefixed/yahnis-elsts/plugin-update-checker/Puc/v5p2/Plugin/UpdateChecker.php +++ /dev/null @@ -1,387 +0,0 @@ -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.', $slug, $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 \WCPOS\Vendor\YahnisElsts\PluginUpdateChecker\v5p2\Plugin\Ui($this); - } - /** - * Create an instance of the scheduler. - * - * @param int $checkPeriod - * @return Scheduler - */ - protected function createScheduler($checkPeriod) - { - $scheduler = new \WCPOS\Vendor\YahnisElsts\PluginUpdateChecker\v5p2\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 PluginInfo - */ - public function requestInfo($queryArgs = array()) - { - list($pluginInfo, $result) = $this->requestMetadata(\WCPOS\Vendor\YahnisElsts\PluginUpdateChecker\v5p2\Plugin\PluginInfo::class, 'request_info', $queryArgs); - if ($pluginInfo !== null) { - /** @var PluginInfo $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 UpdateChecker::requestInfo() - * - * @return 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 = \WCPOS\Vendor\YahnisElsts\PluginUpdateChecker\v5p2\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 Update|null - */ - public function getUpdate() - { - $update = parent::getUpdate(); - if (isset($update)) { - /** @var 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 \WCPOS\Vendor\YahnisElsts\PluginUpdateChecker\v5p2\DebugBar\PluginExtension($this); - } - /** - * Create a package instance that represents this plugin or theme. - * - * @return InstalledPackage - */ - protected function createInstalledPackage() - { - return new \WCPOS\Vendor\YahnisElsts\PluginUpdateChecker\v5p2\Plugin\Package($this->pluginAbsolutePath, $this); - } - /** - * @return Package - */ - public function getInstalledPackage() - { - return $this->package; - } - } -} diff --git a/vendor_prefixed/yahnis-elsts/plugin-update-checker/Puc/v5p2/PucFactory.php b/vendor_prefixed/yahnis-elsts/plugin-update-checker/Puc/v5p2/PucFactory.php deleted file mode 100644 index b5c51ff..0000000 --- a/vendor_prefixed/yahnis-elsts/plugin-update-checker/Puc/v5p2/PucFactory.php +++ /dev/null @@ -1,297 +0,0 @@ - '', '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); - } - 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 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 bookkeeping info about update checks. - * @param string $muPluginFile The plugin filename relative to the mu-plugins directory. - * @return Plugin\UpdateChecker|Theme\UpdateChecker|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) { - //phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_trigger_error - \trigger_error(esc_html(\sprintf('PUC %s does not support updates for %ss %s', self::$latestCompatibleVersion, \strtolower($type), $service ? 'hosted on ' . $service : 'using JSON metadata')), \E_USER_ERROR); - } - 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) { - //phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_trigger_error - \trigger_error(esc_html(\sprintf('PUC %s does not support %s', self::$latestCompatibleVersion, $service)), \E_USER_ERROR); - } - 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.2) - * - * @param string $path Path to normalize. - * @return string Normalized path. - */ - public static function normalizePath($path) - { - if (\function_exists('WCPOS\\Vendor\\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('WCPOS\\Vendor\\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 = (string) wp_parse_url($metadataUrl, \PHP_URL_HOST); - $path = (string) wp_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)) { - $lastNamespaceSegment = \substr(__NAMESPACE__, \strrpos(__NAMESPACE__, '\\') + 1); - self::$myMajorVersion = \substr(\ltrim($lastNamespaceSegment, '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; - } - } -} diff --git a/vendor_prefixed/yahnis-elsts/plugin-update-checker/Puc/v5p2/Scheduler.php b/vendor_prefixed/yahnis-elsts/plugin-update-checker/Puc/v5p2/Scheduler.php deleted file mode 100644 index cc4eb12..0000000 --- a/vendor_prefixed/yahnis-elsts/plugin-update-checker/Puc/v5p2/Scheduler.php +++ /dev/null @@ -1,243 +0,0 @@ -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'; - //phpcs:ignore WordPress.WP.CronInterval.ChangeDetected -- WPCS fails to parse the callback. - 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. - $upperLimit = \max($this->checkPeriod * 3600 - 15 * 60, 1); - if (\function_exists('WCPOS\\Vendor\\wp_rand')) { - $randomOffset = wp_rand(0, $upperLimit); - } else { - //This constructor may be called before wp_rand() is available. - //phpcs:ignore WordPress.WP.AlternativeFunctions.rand_rand - $randomOffset = \rand(0, $upperLimit); - } - $firstCheckTime = \time() - $randomOffset; - $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')); - //phpcs:ignore Squiz.PHP.CommentedOutCode.Found -- Not actually code, just file names. - //"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, \WCPOS\Vendor\YahnisElsts\PluginUpdateChecker\v5p2\Theme\UpdateChecker::class)) { - 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, \WCPOS\Vendor\YahnisElsts\PluginUpdateChecker\v5p2\Plugin\UpdateChecker::class)) { - 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')); - } - } - } - } -} diff --git a/vendor_prefixed/yahnis-elsts/plugin-update-checker/Puc/v5p2/StateStore.php b/vendor_prefixed/yahnis-elsts/plugin-update-checker/Puc/v5p2/StateStore.php deleted file mode 100644 index a85f1d0..0000000 --- a/vendor_prefixed/yahnis-elsts/plugin-update-checker/Puc/v5p2/StateStore.php +++ /dev/null @@ -1,196 +0,0 @@ -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|Update - */ - public function getUpdate() - { - $this->lazyLoad(); - return $this->update; - } - /** - * @param Update|null $update - * @return $this - */ - public function setUpdate(\WCPOS\Vendor\YahnisElsts\PluginUpdateChecker\v5p2\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 (\WCPOS\Vendor\YahnisElsts\PluginUpdateChecker\v5p2\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(\WCPOS\Vendor\YahnisElsts\PluginUpdateChecker\v5p2\Utils::get($state, 'lastCheck', 0)); - $this->checkedVersion = \WCPOS\Vendor\YahnisElsts\PluginUpdateChecker\v5p2\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)) { - $updateClass = $state->updateClass; - } - } - $factory = array($updateClass, 'fromObject'); - if ($updateClass !== null && \is_callable($factory)) { - $this->update = \call_user_func($factory, $state->update); - } - } - } - public function delete() - { - delete_site_option($this->optionName); - $this->lastCheck = 0; - $this->checkedVersion = ''; - $this->update = null; - } - private function getLibPrefix() - { - //This assumes that the current class is at the top of the versioned namespace. - return __NAMESPACE__ . '\\'; - } - } -} diff --git a/vendor_prefixed/yahnis-elsts/plugin-update-checker/Puc/v5p2/Theme/Package.php b/vendor_prefixed/yahnis-elsts/plugin-update-checker/Puc/v5p2/Theme/Package.php deleted file mode 100644 index c82e3c2..0000000 --- a/vendor_prefixed/yahnis-elsts/plugin-update-checker/Puc/v5p2/Theme/Package.php +++ /dev/null @@ -1,55 +0,0 @@ -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'); - } - } -} diff --git a/vendor_prefixed/yahnis-elsts/plugin-update-checker/Puc/v5p2/Theme/Update.php b/vendor_prefixed/yahnis-elsts/plugin-update-checker/Puc/v5p2/Theme/Update.php deleted file mode 100644 index 92fef3c..0000000 --- a/vendor_prefixed/yahnis-elsts/plugin-update-checker/Puc/v5p2/Theme/Update.php +++ /dev/null @@ -1,76 +0,0 @@ - $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|self $object The source object. - * @return self 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 \WCPOS\Vendor\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'; - } - } -} diff --git a/vendor_prefixed/yahnis-elsts/plugin-update-checker/Puc/v5p2/Theme/UpdateChecker.php b/vendor_prefixed/yahnis-elsts/plugin-update-checker/Puc/v5p2/Theme/UpdateChecker.php deleted file mode 100644 index 0becaa4..0000000 --- a/vendor_prefixed/yahnis-elsts/plugin-update-checker/Puc/v5p2/Theme/UpdateChecker.php +++ /dev/null @@ -1,141 +0,0 @@ -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 Update|null An instance of Update, or NULL when no updates are available. - */ - public function requestUpdate() - { - list($themeUpdate, $result) = $this->requestMetadata(\WCPOS\Vendor\YahnisElsts\PluginUpdateChecker\v5p2\Theme\Update::class, 'request_update'); - if ($themeUpdate !== null) { - /** @var 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 Scheduler - */ - protected function createScheduler($checkPeriod) - { - return new \WCPOS\Vendor\YahnisElsts\PluginUpdateChecker\v5p2\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 \WCPOS\Vendor\YahnisElsts\PluginUpdateChecker\v5p2\DebugBar\Extension($this, \WCPOS\Vendor\YahnisElsts\PluginUpdateChecker\v5p2\DebugBar\ThemePanel::class); - } - /** - * 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 InstalledPackage - */ - protected function createInstalledPackage() - { - return new \WCPOS\Vendor\YahnisElsts\PluginUpdateChecker\v5p2\Theme\Package($this->stylesheet, $this); - } - } -} diff --git a/vendor_prefixed/yahnis-elsts/plugin-update-checker/Puc/v5p2/Update.php b/vendor_prefixed/yahnis-elsts/plugin-update-checker/Puc/v5p2/Update.php deleted file mode 100644 index 52e21d9..0000000 --- a/vendor_prefixed/yahnis-elsts/plugin-update-checker/Puc/v5p2/Update.php +++ /dev/null @@ -1,35 +0,0 @@ -slug = $this->slug; - $update->new_version = $this->version; - $update->package = $this->download_url; - return $update; - } - } -} diff --git a/vendor_prefixed/yahnis-elsts/plugin-update-checker/Puc/v5p2/UpdateChecker.php b/vendor_prefixed/yahnis-elsts/plugin-update-checker/Puc/v5p2/UpdateChecker.php deleted file mode 100644 index 5d9328a..0000000 --- a/vendor_prefixed/yahnis-elsts/plugin-update-checker/Puc/v5p2/UpdateChecker.php +++ /dev/null @@ -1,849 +0,0 @@ -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 \WCPOS\Vendor\YahnisElsts\PluginUpdateChecker\v5p2\UpgraderStatus(); - $this->updateState = new \WCPOS\Vendor\YahnisElsts\PluginUpdateChecker\v5p2\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('WCPOS\\Vendor\\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 - */ - public abstract 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 = wp_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 InstalledPackage - */ - protected abstract function createInstalledPackage(); - /** - * @return 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 Scheduler - */ - protected abstract function createScheduler($checkPeriod); - /** - * Check for updates. The results are stored in the DB option specified in $optionName. - * - * @return 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 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 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 Update An instance of Update, or NULL when no updates are available. - */ - public abstract function requestUpdate(); - /** - * Filter the result of a requestUpdate() call. - * - * @template T of Update - * @param T|null $update - * @param array|WP_Error|null $httpResult The value returned by wp_remote_get(), if any. - * @return T - */ - 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 Metadata|null $update - */ - protected function fixSupportedWordpressVersion(\WCPOS\Vendor\YahnisElsts\PluginUpdateChecker\v5p2\Metadata $update = null) - { - if (!isset($update->tested) || !\preg_match('/^\\d++\\.\\d++$/', $update->tested)) { - return; - } - $actualWpVersions = array(); - $wpVersion = $GLOBALS['wp_version']; - if (\function_exists('WCPOS\\Vendor\\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\\d++\\.\\d++)(?:\\.(?P\\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()) { - //phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_trigger_error -- Only happens in debug mode. - \trigger_error(esc_html($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 - */ - protected abstract 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 class-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 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' => wp_doing_cron() ? 10 : 3, '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 (\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 \WCPOS\Vendor\WP_Error($result->get_error_code(), 'WP HTTP Error: ' . $result->get_error_message()); - } - if (!isset($result['response']['code'])) { - return new \WCPOS\Vendor\WP_Error('puc_no_response_code', 'wp_remote_get() returned an unexpected result.'); - } - if ($result['response']['code'] !== 200) { - return new \WCPOS\Vendor\WP_Error('puc_unexpected_response_code', 'HTTP response code is ' . $result['response']['code'] . ' (expected: 200)'); - } - if (empty($result['body'])) { - return new \WCPOS\Vendor\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('WCPOS\\Vendor\\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 \WCPOS\Vendor\WP_Error('puc-incorrect-directory-structure', \sprintf('The directory structure of the update is incorrect. All files should be inside ' . 'a directory named %s, not at the root of the ZIP archive.', \htmlentities($this->slug))); - } - /** @var \WP_Upgrader_Skin $upgrader ->skin */ - $upgrader->skin->feedback(\sprintf('Renaming %s to %s…', '' . \basename($source) . '', '' . $this->directoryName . '')); - if ($wp_filesystem->move($source, $correctedSource, \true)) { - $upgrader->skin->feedback('Directory successfully renamed.'); - return $correctedSource; - } else { - return new \WCPOS\Vendor\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 - */ - public abstract 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('WCPOS\\Vendor\\Debug_Bar', \false) && \file_exists(\dirname(__FILE__) . '/DebugBar')) { - $this->debugBarExtension = $this->createDebugBarExtension(); - } - } - protected function createDebugBarExtension() - { - return new \WCPOS\Vendor\YahnisElsts\PluginUpdateChecker\v5p2\DebugBar\Extension($this); - } - /** - * Display additional configuration details in the Debug Bar panel. - * - * @param DebugBar\Panel $panel - */ - public function onDisplayConfiguration($panel) - { - //Do nothing. Subclasses can use this to add additional info to the panel. - } - } -} diff --git a/vendor_prefixed/yahnis-elsts/plugin-update-checker/Puc/v5p2/UpgraderStatus.php b/vendor_prefixed/yahnis-elsts/plugin-update-checker/Puc/v5p2/UpgraderStatus.php deleted file mode 100644 index 4de11c2..0000000 --- a/vendor_prefixed/yahnis-elsts/plugin-update-checker/Puc/v5p2/UpgraderStatus.php +++ /dev/null @@ -1,196 +0,0 @@ -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 \WCPOS\Vendor\WP_Theme) { - $themeDirectoryName = $skin->theme_info->get_stylesheet(); - } elseif ($skin instanceof \WCPOS\Vendor\Plugin_Upgrader_Skin) { - if (isset($skin->plugin) && \is_string($skin->plugin) && $skin->plugin !== '') { - $pluginFile = $skin->plugin; - } - } elseif ($skin instanceof \WCPOS\Vendor\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('WCPOS\\Vendor\\get_plugins')) { - 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; - } - } -} diff --git a/vendor_prefixed/yahnis-elsts/plugin-update-checker/Puc/v5p2/Utils.php b/vendor_prefixed/yahnis-elsts/plugin-update-checker/Puc/v5p2/Utils.php deleted file mode 100644 index de554bb..0000000 --- a/vendor_prefixed/yahnis-elsts/plugin-update-checker/Puc/v5p2/Utils.php +++ /dev/null @@ -1,69 +0,0 @@ -{$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; - } - } -} diff --git a/vendor_prefixed/yahnis-elsts/plugin-update-checker/Puc/v5p2/Vcs/Api.php b/vendor_prefixed/yahnis-elsts/plugin-update-checker/Puc/v5p2/Vcs/Api.php deleted file mode 100644 index a3da34b..0000000 --- a/vendor_prefixed/yahnis-elsts/plugin-update-checker/Puc/v5p2/Vcs/Api.php +++ /dev/null @@ -1,347 +0,0 @@ -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|Reference - */ - public function chooseReference($configBranch) - { - $strategies = $this->getUpdateDetectionStrategies($configBranch); - if (!empty($this->strategyFilterName)) { - $strategies = apply_filters($this->strategyFilterName, $strategies, $this->slug); - } - foreach ($strategies as $strategy) { - $reference = \call_user_func($strategy); - if (!empty($reference)) { - return $reference; - } - } - return null; - } - /** - * Get an ordered list of strategies that can be used to find the latest version. - * - * The update checker will try each strategy in order until one of them - * returns a valid reference. - * - * @param string $configBranch - * @return array Array of callables that return Vcs_Reference objects. - */ - protected abstract function getUpdateDetectionStrategies($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 \WCPOS\Vendor\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 Reference|null - */ - public abstract function getBranch($branchName); - /** - * Get a specific tag. - * - * @param string $tagName - * @return Reference|null - */ - public abstract function getTag($tagName); - /** - * Get the tag that looks like the highest version number. - * (Implementations should skip pre-release versions if possible.) - * - * @return Reference|null - */ - public abstract 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. - */ - public abstract 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 - */ - public abstract 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; - } - return \WCPOS\Vendor\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 $filterName - */ - public function setStrategyFilterName($filterName) - { - $this->strategyFilterName = $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; - } - } -} diff --git a/vendor_prefixed/yahnis-elsts/plugin-update-checker/Puc/v5p2/Vcs/BaseChecker.php b/vendor_prefixed/yahnis-elsts/plugin-update-checker/Puc/v5p2/Vcs/BaseChecker.php deleted file mode 100644 index 87a4524..0000000 --- a/vendor_prefixed/yahnis-elsts/plugin-update-checker/Puc/v5p2/Vcs/BaseChecker.php +++ /dev/null @@ -1,27 +0,0 @@ -[^/]+?)/(?P[^/#?&]+?)/?$@', $path, $matches)) { - $this->username = $matches['username']; - $this->repository = $matches['repository']; - } else { - throw new \InvalidArgumentException('Invalid BitBucket repository URL: "' . $repositoryUrl . '"'); - } - parent::__construct($repositoryUrl, $credentials); - } - protected function getUpdateDetectionStrategies($configBranch) - { - $strategies = array(self::STRATEGY_STABLE_TAG => function () use($configBranch) { - return $this->getStableTag($configBranch); - }); - if ($configBranch === 'master' || $configBranch === 'main') { - $strategies[self::STRATEGY_LATEST_TAG] = array($this, 'getLatestTag'); - } - $strategies[self::STRATEGY_BRANCH] = function () use($configBranch) { - return $this->getBranch($configBranch); - }; - return $strategies; - } - public function getBranch($branchName) - { - $branch = $this->api('/refs/branches/' . $branchName); - if (is_wp_error($branch) || empty($branch)) { - return null; - } - //The "/src/{stuff}/{path}" endpoint doesn't seem to handle branch names that contain slashes. - //If we don't encode the slash, we get a 404. If we encode it as "%2F", we get a 401. - //To avoid issues, if the branch name is not URL-safe, let's use the commit hash instead. - $ref = $branch->name; - if (\urlencode($ref) !== $ref && isset($branch->target->hash)) { - $ref = $branch->target->hash; - } - return new \WCPOS\Vendor\YahnisElsts\PluginUpdateChecker\v5p2\Vcs\Reference(array('name' => $ref, 'updated' => $branch->target->date, 'downloadUrl' => $this->getDownloadUrl($branch->name))); - } - /** - * Get a specific tag. - * - * @param string $tagName - * @return Reference|null - */ - public function getTag($tagName) - { - $tag = $this->api('/refs/tags/' . $tagName); - if (is_wp_error($tag) || empty($tag)) { - return null; - } - return new \WCPOS\Vendor\YahnisElsts\PluginUpdateChecker\v5p2\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 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 \WCPOS\Vendor\YahnisElsts\PluginUpdateChecker\v5p2\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|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 = \WCPOS\Vendor\YahnisElsts\PluginUpdateChecker\v5p2\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' => wp_doing_cron() ? 10 : 3); - 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 \WCPOS\Vendor\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 \WCPOS\Vendor\YahnisElsts\PluginUpdateChecker\v5p2\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; - } - } -} diff --git a/vendor_prefixed/yahnis-elsts/plugin-update-checker/Puc/v5p2/Vcs/GitHubApi.php b/vendor_prefixed/yahnis-elsts/plugin-update-checker/Puc/v5p2/Vcs/GitHubApi.php deleted file mode 100644 index 5155894..0000000 --- a/vendor_prefixed/yahnis-elsts/plugin-update-checker/Puc/v5p2/Vcs/GitHubApi.php +++ /dev/null @@ -1,387 +0,0 @@ -[^/]+?)/(?P[^/#?&]+?)/?$@', $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 Reference|null - */ - public function getLatestRelease() - { - //The "latest release" endpoint returns one release and always skips pre-releases, - //so we can only use it if that's compatible with the current filter settings. - if ($this->shouldSkipPreReleases() && ($this->releaseFilterMaxReleases === 1 || !$this->hasCustomReleaseFilter())) { - //Just get the latest release. - $release = $this->api('/repos/:user/:repo/releases/latest'); - if (is_wp_error($release) || !\is_object($release) || !isset($release->tag_name)) { - return null; - } - $foundReleases = array($release); - } else { - //Get a list of the most recent releases. - $foundReleases = $this->api('/repos/:user/:repo/releases', array('per_page' => $this->releaseFilterMaxReleases)); - if (is_wp_error($foundReleases) || !\is_array($foundReleases)) { - return null; - } - } - foreach ($foundReleases as $release) { - //Always skip drafts. - if (isset($release->draft) && !empty($release->draft)) { - continue; - } - //Skip pre-releases unless specifically included. - if ($this->shouldSkipPreReleases() && isset($release->prerelease) && !empty($release->prerelease)) { - continue; - } - $versionNumber = \ltrim($release->tag_name, 'v'); - //Remove the "v" prefix from "v1.2.3". - //Custom release filtering. - if (!$this->matchesCustomReleaseFilter($versionNumber, $release)) { - continue; - } - $reference = new \WCPOS\Vendor\YahnisElsts\PluginUpdateChecker\v5p2\Vcs\Reference(array('name' => $release->tag_name, 'version' => $versionNumber, '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) { - //Use the first release asset that matches the specified regular expression. - if (isset($release->assets, $release->assets[0])) { - $matchingAssets = \array_values(\array_filter($release->assets, array($this, 'matchesAssetFilter'))); - } else { - $matchingAssets = array(); - } - 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; - } else { - if ($this->releaseAssetPreference === \WCPOS\Vendor\YahnisElsts\PluginUpdateChecker\v5p2\Vcs\Api::REQUIRE_RELEASE_ASSETS) { - //None of the assets match the filter, and we're not allowed - //to fall back to the auto-generated source ZIP. - return null; - } - } - } - if (!empty($release->body)) { - $reference->changelog = \WCPOS\Vendor\Parsedown::instance()->text($release->body); - } - return $reference; - } - return null; - } - /** - * Get the tag that looks like the highest version number. - * - * @return 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 \WCPOS\Vendor\YahnisElsts\PluginUpdateChecker\v5p2\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|Reference - */ - public function getBranch($branchName) - { - $branch = $this->api('/repos/:user/:repo/branches/' . $branchName); - if (is_wp_error($branch) || empty($branch)) { - return null; - } - $reference = new \WCPOS\Vendor\YahnisElsts\PluginUpdateChecker\v5p2\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' => wp_doing_cron() ? 10 : 3); - if ($this->isAuthenticationEnabled()) { - $options['headers'] = array('Authorization' => $this->getAuthorizationHeader()); - } - if (!empty($this->httpFilterName)) { - $options = apply_filters($this->httpFilterName, $options); - } - $response = wp_remote_get($url, $options); - 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 \WCPOS\Vendor\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+ - } - protected function getUpdateDetectionStrategies($configBranch) - { - $strategies = array(); - if ($configBranch === 'master' || $configBranch === 'main') { - //Use the latest release. - $strategies[self::STRATEGY_LATEST_RELEASE] = array($this, 'getLatestRelease'); - //Failing that, use the tag with the highest version number. - $strategies[self::STRATEGY_LATEST_TAG] = array($this, 'getLatestTag'); - } - //Alternatively, just use the branch itself. - $strategies[self::STRATEGY_BRANCH] = function () use($configBranch) { - return $this->getBranch($configBranch); - }; - return $strategies; - } - /** - * Get the unchanging part of a release asset URL. Used to identify download attempts. - * - * @return string - */ - protected function getAssetApiBaseUrl() - { - return \sprintf('//api.github.com/repos/%1$s/%2$s/releases/assets/', $this->userName, $this->repositoryName); - } - protected function getFilterableAssetName($releaseAsset) - { - if (isset($releaseAsset->name)) { - return $releaseAsset->name; - } - return null; - } - /** - * @param bool $result - * @return bool - * @internal - */ - public function addHttpRequestFilter($result) - { - if (!$this->downloadFilterAdded && $this->isAuthenticationEnabled()) { - //phpcs:ignore WordPressVIPMinimum.Hooks.RestrictedHooks.http_request_args -- The callback doesn't change the timeout. - add_filter('http_request_args', array($this, 'setUpdateDownloadHeaders'), 10, 2); - add_action('requests-requests.before_redirect', array($this, 'removeAuthHeaderFromRedirects'), 10, 4); - $this->downloadFilterAdded = \true; - } - return $result; - } - /** - * Set the HTTP headers that are necessary to download updates from private repositories. - * - * See GitHub docs: - * - * @link https://developer.github.com/v3/repos/releases/#get-a-single-release-asset - * @link https://developer.github.com/v3/auth/#basic-authentication - * - * @internal - * @param array $requestArgs - * @param string $url - * @return array - */ - public function setUpdateDownloadHeaders($requestArgs, $url = '') - { - //Is WordPress trying to download one of our release assets? - if ($this->releaseAssetsEnabled && \strpos($url, $this->getAssetApiBaseUrl()) !== \false) { - $requestArgs['headers']['Accept'] = 'application/octet-stream'; - } - //Use Basic authentication, but only if the download is from our repository. - $repoApiBaseUrl = $this->buildApiUrl('/repos/:user/:repo/', array()); - if ($this->isAuthenticationEnabled() && \strpos($url, $repoApiBaseUrl) === 0) { - $requestArgs['headers']['Authorization'] = $this->getAuthorizationHeader(); - } - return $requestArgs; - } - /** - * When following a redirect, the Requests library will automatically forward - * the authorization header to other hosts. We don't want that because it breaks - * AWS downloads and can leak authorization information. - * - * @param string $location - * @param array $headers - * @internal - */ - public function removeAuthHeaderFromRedirects(&$location, &$headers) - { - $repoApiBaseUrl = $this->buildApiUrl('/repos/:user/:repo/', array()); - if (\strpos($location, $repoApiBaseUrl) === 0) { - return; - //This request is going to GitHub, so it's fine. - } - //Remove the header. - if (isset($headers['Authorization'])) { - unset($headers['Authorization']); - } - } - /** - * Generate the value of the "Authorization" header. - * - * @return string - */ - protected function getAuthorizationHeader() - { - return 'Basic ' . \base64_encode($this->userName . ':' . $this->accessToken); - } - } -} diff --git a/vendor_prefixed/yahnis-elsts/plugin-update-checker/Puc/v5p2/Vcs/GitLabApi.php b/vendor_prefixed/yahnis-elsts/plugin-update-checker/Puc/v5p2/Vcs/GitLabApi.php deleted file mode 100644 index 7f2b353..0000000 --- a/vendor_prefixed/yahnis-elsts/plugin-update-checker/Puc/v5p2/Vcs/GitLabApi.php +++ /dev/null @@ -1,333 +0,0 @@ -repositoryHost = wp_parse_url($repositoryUrl, \PHP_URL_HOST) . $port; - if ($this->repositoryHost !== 'gitlab.com') { - $this->repositoryProtocol = wp_parse_url($repositoryUrl, \PHP_URL_SCHEME); - } - //Find the repository information - $path = wp_parse_url($repositoryUrl, \PHP_URL_PATH); - if (\preg_match('@^/?(?P[^/]+?)/(?P[^/#?&]+?)/?$@', $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 Reference|null - */ - public function getLatestRelease() - { - $releases = $this->api('/:id/releases', array('per_page' => $this->releaseFilterMaxReleases)); - if (is_wp_error($releases) || empty($releases) || !\is_array($releases)) { - return null; - } - foreach ($releases as $release) { - if (!\is_object($release) || !isset($release->tag_name) || !empty($release->upcoming_release) && $this->shouldSkipPreReleases()) { - continue; - } - $versionNumber = \ltrim($release->tag_name, 'v'); - //Remove the "v" prefix from "v1.2.3". - //Apply custom filters. - if (!$this->matchesCustomReleaseFilter($versionNumber, $release)) { - continue; - } - $downloadUrl = $this->findReleaseDownloadUrl($release); - if (empty($downloadUrl)) { - //The latest release doesn't have valid download URL. - return null; - } - if (!empty($this->accessToken)) { - $downloadUrl = add_query_arg('private_token', $this->accessToken, $downloadUrl); - } - return new \WCPOS\Vendor\YahnisElsts\PluginUpdateChecker\v5p2\Vcs\Reference(array('name' => $release->tag_name, 'version' => $versionNumber, 'downloadUrl' => $downloadUrl, 'updated' => $release->released_at, 'apiResponse' => $release)); - } - return null; - } - /** - * @param object $release - * @return string|null - */ - protected function findReleaseDownloadUrl($release) - { - if ($this->releaseAssetsEnabled) { - if (isset($release->assets, $release->assets->links)) { - //Use the first asset link where the URL matches the filter. - foreach ($release->assets->links as $link) { - if ($this->matchesAssetFilter($link)) { - return $link->url; - } - } - } - if ($this->releaseAssetPreference === \WCPOS\Vendor\YahnisElsts\PluginUpdateChecker\v5p2\Vcs\Api::REQUIRE_RELEASE_ASSETS) { - //Falling back to source archives is not allowed, so give up. - return null; - } - } - //Use the first source code archive that's in ZIP format. - foreach ($release->assets->sources as $source) { - if (isset($source->format) && $source->format === 'zip') { - return $source->url; - } - } - return null; - } - /** - * Get the tag that looks like the highest version number. - * - * @return 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 \WCPOS\Vendor\YahnisElsts\PluginUpdateChecker\v5p2\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|Reference - */ - public function getBranch($branchName) - { - $branch = $this->api('/:id/repository/branches/' . $branchName); - if (is_wp_error($branch) || empty($branch)) { - return null; - } - $reference = new \WCPOS\Vendor\YahnisElsts\PluginUpdateChecker\v5p2\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' => wp_doing_cron() ? 10 : 3); - 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 \WCPOS\Vendor\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.'); - } - protected function getUpdateDetectionStrategies($configBranch) - { - $strategies = array(); - if ($configBranch === 'main' || $configBranch === 'master') { - $strategies[self::STRATEGY_LATEST_RELEASE] = array($this, 'getLatestRelease'); - $strategies[self::STRATEGY_LATEST_TAG] = array($this, 'getLatestTag'); - } - $strategies[self::STRATEGY_BRANCH] = function () use($configBranch) { - return $this->getBranch($configBranch); - }; - return $strategies; - } - public function setAuthentication($credentials) - { - parent::setAuthentication($credentials); - $this->accessToken = \is_string($credentials) ? $credentials : null; - } - /** - * Use release assets that link to GitLab generic packages (e.g. .zip files) - * instead of automatically generated source archives. - * - * This is included for backwards compatibility with older versions of PUC. - * - * @return void - * @deprecated Use enableReleaseAssets() instead. - * @noinspection PhpUnused -- Public API - */ - public function enableReleasePackages() - { - $this->enableReleaseAssets( - /** @lang RegExp */ - '/\\.zip($|[?&#])/i', - \WCPOS\Vendor\YahnisElsts\PluginUpdateChecker\v5p2\Vcs\Api::REQUIRE_RELEASE_ASSETS - ); - } - protected function getFilterableAssetName($releaseAsset) - { - if (isset($releaseAsset->url)) { - return $releaseAsset->url; - } - return null; - } - } -} diff --git a/vendor_prefixed/yahnis-elsts/plugin-update-checker/Puc/v5p2/Vcs/PluginUpdateChecker.php b/vendor_prefixed/yahnis-elsts/plugin-update-checker/Puc/v5p2/Vcs/PluginUpdateChecker.php deleted file mode 100644 index 7231f8b..0000000 --- a/vendor_prefixed/yahnis-elsts/plugin-update-checker/Puc/v5p2/Vcs/PluginUpdateChecker.php +++ /dev/null @@ -1,204 +0,0 @@ -api = $api; - parent::__construct($api->getRepositoryUrl(), $pluginFile, $slug, $checkPeriod, $optionName, $muPluginFile); - $this->api->setHttpFilterName($this->getUniqueName('request_info_options')); - $this->api->setStrategyFilterName($this->getUniqueName('vcs_update_detection_strategies')); - $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 \WCPOS\Vendor\YahnisElsts\PluginUpdateChecker\v5p2\Plugin\PluginInfo(); - $info->filename = $this->pluginFile; - $info->slug = $this->slug; - $this->setInfoFromHeader($this->package->getPluginHeader(), $info); - $this->setIconsFromLocalAssets($info); - $this->setBannersFromLocalAssets($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 \WCPOS\Vendor\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 Plugin\PluginInfo $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 Plugin\PluginInfo $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]; - } - } - /** - * Add icons from the currently installed version to a Plugin Info object. - * - * The icons should be in a subdirectory named "assets". Supported image formats - * and file names are described here: - * @link https://developer.wordpress.org/plugins/wordpress-org/plugin-assets/#plugin-icons - * - * @param Plugin\PluginInfo $pluginInfo - */ - protected function setIconsFromLocalAssets($pluginInfo) - { - $icons = $this->getLocalAssetUrls(array('icon.svg' => 'svg', 'icon-256x256.png' => '2x', 'icon-256x256.jpg' => '2x', 'icon-128x128.png' => '1x', 'icon-128x128.jpg' => '1x')); - if (!empty($icons)) { - //The "default" key seems to be used only as last-resort fallback in WP core (5.8/5.9), - //but we'll set it anyway in case some code somewhere needs it. - \reset($icons); - $firstKey = \key($icons); - $icons['default'] = $icons[$firstKey]; - $pluginInfo->icons = $icons; - } - } - /** - * Add banners from the currently installed version to a Plugin Info object. - * - * The banners should be in a subdirectory named "assets". Supported image formats - * and file names are described here: - * @link https://developer.wordpress.org/plugins/wordpress-org/plugin-assets/#plugin-headers - * - * @param Plugin\PluginInfo $pluginInfo - */ - protected function setBannersFromLocalAssets($pluginInfo) - { - $banners = $this->getLocalAssetUrls(array('banner-772x250.png' => 'high', 'banner-772x250.jpg' => 'high', 'banner-1544x500.png' => 'low', 'banner-1544x500.jpg' => 'low')); - if (!empty($banners)) { - $pluginInfo->banners = $banners; - } - } - /** - * @param array $filesToKeys - * @return array - */ - protected function getLocalAssetUrls($filesToKeys) - { - $assetDirectory = $this->package->getAbsoluteDirectoryPath() . \DIRECTORY_SEPARATOR . 'assets'; - if (!\is_dir($assetDirectory)) { - return array(); - } - $assetBaseUrl = trailingslashit(plugins_url('', $assetDirectory . '/imaginary.file')); - $foundAssets = array(); - foreach ($filesToKeys as $fileName => $key) { - $fullBannerPath = $assetDirectory . \DIRECTORY_SEPARATOR . $fileName; - if (!isset($icons[$key]) && \is_file($fullBannerPath)) { - $foundAssets[$key] = $assetBaseUrl . $fileName; - } - } - return $foundAssets; - } - } -} diff --git a/vendor_prefixed/yahnis-elsts/plugin-update-checker/Puc/v5p2/Vcs/Reference.php b/vendor_prefixed/yahnis-elsts/plugin-update-checker/Puc/v5p2/Vcs/Reference.php deleted file mode 100644 index 8f580e4..0000000 --- a/vendor_prefixed/yahnis-elsts/plugin-update-checker/Puc/v5p2/Vcs/Reference.php +++ /dev/null @@ -1,50 +0,0 @@ -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]); - } - } -} diff --git a/vendor_prefixed/yahnis-elsts/plugin-update-checker/Puc/v5p2/Vcs/ReleaseAssetSupport.php b/vendor_prefixed/yahnis-elsts/plugin-update-checker/Puc/v5p2/Vcs/ReleaseAssetSupport.php deleted file mode 100644 index 96087e1..0000000 --- a/vendor_prefixed/yahnis-elsts/plugin-update-checker/Puc/v5p2/Vcs/ReleaseAssetSupport.php +++ /dev/null @@ -1,78 +0,0 @@ -releaseAssetsEnabled = \true; - $this->assetFilterRegex = $nameRegex; - $this->releaseAssetPreference = $preference; - } - /** - * Disable release assets. - * - * @return void - * @noinspection PhpUnused -- Public API - */ - public function disableReleaseAssets() - { - $this->releaseAssetsEnabled = \false; - $this->assetFilterRegex = null; - } - /** - * Does the specified asset match the name regex? - * - * @param mixed $releaseAsset Data type and structure depend on the host/API. - * @return bool - */ - protected function matchesAssetFilter($releaseAsset) - { - if ($this->assetFilterRegex === null) { - //The default is to accept all assets. - return \true; - } - $name = $this->getFilterableAssetName($releaseAsset); - if (!\is_string($name)) { - return \false; - } - return (bool) \preg_match($this->assetFilterRegex, $releaseAsset->name); - } - /** - * Get the part of asset data that will be checked against the filter regex. - * - * @param mixed $releaseAsset - * @return string|null - */ - protected abstract function getFilterableAssetName($releaseAsset); - } -} diff --git a/vendor_prefixed/yahnis-elsts/plugin-update-checker/Puc/v5p2/Vcs/ReleaseFilteringFeature.php b/vendor_prefixed/yahnis-elsts/plugin-update-checker/Puc/v5p2/Vcs/ReleaseFilteringFeature.php deleted file mode 100644 index e13546c..0000000 --- a/vendor_prefixed/yahnis-elsts/plugin-update-checker/Puc/v5p2/Vcs/ReleaseFilteringFeature.php +++ /dev/null @@ -1,91 +0,0 @@ - 100) { - throw new \InvalidArgumentException(\sprintf('The max number of releases is too high (%d). It must be 100 or less.', $maxReleases)); - } else { - if ($maxReleases < 1) { - throw new \InvalidArgumentException(\sprintf('The max number of releases is too low (%d). It must be at least 1.', $maxReleases)); - } - } - $this->releaseFilterCallback = $callback; - $this->releaseFilterByType = $releaseTypes; - $this->releaseFilterMaxReleases = $maxReleases; - return $this; - } - /** - * Filter releases by their version number. - * - * @param string $regex A regular expression. The release version number must match this regex. - * @param int $releaseTypes - * @param int $maxReleasesToExamine - * @return $this - * @noinspection PhpUnused -- Public API - */ - public function setReleaseVersionFilter($regex, $releaseTypes = \WCPOS\Vendor\YahnisElsts\PluginUpdateChecker\v5p2\Vcs\Api::RELEASE_FILTER_SKIP_PRERELEASE, $maxReleasesToExamine = 20) - { - return $this->setReleaseFilter(function ($versionNumber) use($regex) { - return \preg_match($regex, $versionNumber) === 1; - }, $releaseTypes, $maxReleasesToExamine); - } - /** - * @param string $versionNumber The detected release version number. - * @param object $releaseObject Varies depending on the host/API. - * @return bool - */ - protected function matchesCustomReleaseFilter($versionNumber, $releaseObject) - { - if (!\is_callable($this->releaseFilterCallback)) { - return \true; - //No custom filter. - } - return \call_user_func($this->releaseFilterCallback, $versionNumber, $releaseObject); - } - /** - * @return bool - */ - protected function shouldSkipPreReleases() - { - //Maybe this could be a bitfield in the future, if we need to support - //more release types. - return $this->releaseFilterByType !== \WCPOS\Vendor\YahnisElsts\PluginUpdateChecker\v5p2\Vcs\Api::RELEASE_FILTER_ALL; - } - /** - * @return bool - */ - protected function hasCustomReleaseFilter() - { - return isset($this->releaseFilterCallback) && \is_callable($this->releaseFilterCallback); - } - } -} diff --git a/vendor_prefixed/yahnis-elsts/plugin-update-checker/Puc/v5p2/Vcs/ThemeUpdateChecker.php b/vendor_prefixed/yahnis-elsts/plugin-update-checker/Puc/v5p2/Vcs/ThemeUpdateChecker.php deleted file mode 100644 index 8358488..0000000 --- a/vendor_prefixed/yahnis-elsts/plugin-update-checker/Puc/v5p2/Vcs/ThemeUpdateChecker.php +++ /dev/null @@ -1,57 +0,0 @@ -api = $api; - parent::__construct($api->getRepositoryUrl(), $stylesheet, $customSlug, $checkPeriod, $optionName); - $this->api->setHttpFilterName($this->getUniqueName('request_update_options')); - $this->api->setStrategyFilterName($this->getUniqueName('vcs_update_detection_strategies')); - $this->api->setSlug($this->slug); - } - public function requestUpdate() - { - $api = $this->api; - $api->setLocalDirectory($this->package->getAbsoluteDirectoryPath()); - $update = new \WCPOS\Vendor\YahnisElsts\PluginUpdateChecker\v5p2\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 \WCPOS\Vendor\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 = \WCPOS\Vendor\YahnisElsts\PluginUpdateChecker\v5p2\Utils::findNotEmpty(array($remoteHeader['Version'], \WCPOS\Vendor\YahnisElsts\PluginUpdateChecker\v5p2\Utils::get($updateSource, 'version'))); - //The details URL defaults to the Theme URI header or the repository URL. - $update->details_url = \WCPOS\Vendor\YahnisElsts\PluginUpdateChecker\v5p2\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; - } - } -} diff --git a/vendor_prefixed/yahnis-elsts/plugin-update-checker/Puc/v5p2/Vcs/VcsCheckerMethods.php b/vendor_prefixed/yahnis-elsts/plugin-update-checker/Puc/v5p2/Vcs/VcsCheckerMethods.php deleted file mode 100644 index 643db00..0000000 --- a/vendor_prefixed/yahnis-elsts/plugin-update-checker/Puc/v5p2/Vcs/VcsCheckerMethods.php +++ /dev/null @@ -1,55 +0,0 @@ -branch = $branch; - return $this; - } - /** - * Set authentication credentials. - * - * @param array|string $credentials - * @return $this - */ - public function setAuthentication($credentials) - { - $this->api->setAuthentication($credentials); - return $this; - } - /** - * @return Api - */ - 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)); - } - } -} diff --git a/vendor_prefixed/yahnis-elsts/plugin-update-checker/load-v5p2.php b/vendor_prefixed/yahnis-elsts/plugin-update-checker/load-v5p2.php deleted file mode 100644 index 3299523..0000000 --- a/vendor_prefixed/yahnis-elsts/plugin-update-checker/load-v5p2.php +++ /dev/null @@ -1,17 +0,0 @@ - \WCPOS\Vendor\YahnisElsts\PluginUpdateChecker\v5p2\Plugin\UpdateChecker::class, 'WCPOS\\Vendor\\Theme\\UpdateChecker' => \WCPOS\Vendor\YahnisElsts\PluginUpdateChecker\v5p2\Theme\UpdateChecker::class, 'WCPOS\\Vendor\\Vcs\\PluginUpdateChecker' => \WCPOS\Vendor\YahnisElsts\PluginUpdateChecker\v5p2\Vcs\PluginUpdateChecker::class, 'WCPOS\\Vendor\\Vcs\\ThemeUpdateChecker' => \WCPOS\Vendor\YahnisElsts\PluginUpdateChecker\v5p2\Vcs\ThemeUpdateChecker::class, 'GitHubApi' => \WCPOS\Vendor\YahnisElsts\PluginUpdateChecker\v5p2\Vcs\GitHubApi::class, 'BitBucketApi' => \WCPOS\Vendor\YahnisElsts\PluginUpdateChecker\v5p2\Vcs\BitBucketApi::class, 'GitLabApi' => \WCPOS\Vendor\YahnisElsts\PluginUpdateChecker\v5p2\Vcs\GitLabApi::class) as $pucGeneralClass => $pucVersionedClass) { - \WCPOS\Vendor\YahnisElsts\PluginUpdateChecker\v5\PucFactory::addVersion($pucGeneralClass, $pucVersionedClass, '5.2'); - //Also add it to the minor-version factory in case the major-version factory - //was already defined by another, older version of the update checker. - \WCPOS\Vendor\YahnisElsts\PluginUpdateChecker\v5p2\PucFactory::addVersion($pucGeneralClass, $pucVersionedClass, '5.2'); -} diff --git a/vendor_prefixed/yahnis-elsts/plugin-update-checker/plugin-update-checker.php b/vendor_prefixed/yahnis-elsts/plugin-update-checker/plugin-update-checker.php deleted file mode 100644 index d3f8af4..0000000 --- a/vendor_prefixed/yahnis-elsts/plugin-update-checker/plugin-update-checker.php +++ /dev/null @@ -1,12 +0,0 @@ -