From fda83ead1ae31928bec6670743af85ff73796a15 Mon Sep 17 00:00:00 2001 From: Andy Fragen Date: Tue, 26 Aug 2025 10:44:34 -0700 Subject: [PATCH 1/3] remove Git Updater Lite dependency Signed-off-by: Andy Fragen --- inc/namespace.php | 5 - inc/updater/class-lite.php | 437 ------------------------------------- 2 files changed, 442 deletions(-) delete mode 100644 inc/updater/class-lite.php diff --git a/inc/namespace.php b/inc/namespace.php index 49d898ea..1be91d91 100644 --- a/inc/namespace.php +++ b/inc/namespace.php @@ -7,8 +7,6 @@ namespace FAIR; -use Fragen\Git_Updater; - const NS_SEPARATOR = '\\'; /** @@ -41,9 +39,6 @@ function bootstrap() { Upgrades\bootstrap(); User_Notification\bootstrap(); Version_Check\bootstrap(); - - // Self-update check. - ( new Git_Updater\Lite( PLUGIN_FILE ) )->run(); } /** diff --git a/inc/updater/class-lite.php b/inc/updater/class-lite.php deleted file mode 100644 index e7a0d5b9..00000000 --- a/inc/updater/class-lite.php +++ /dev/null @@ -1,437 +0,0 @@ -slug = basename( dirname( $file_path ) ); - - if ( str_ends_with( $file_path, 'functions.php' ) ) { - $this->file = $this->slug . '/style.css'; - $file_path = dirname( $file_path ) . '/style.css'; - } else { - $this->file = $this->slug . '/' . basename( $file_path ); - } - - $file_data = get_file_data( - $file_path, - array( - 'Version' => 'Version', - 'UpdateURI' => 'Update URI', - ) - ); - $this->local_version = $file_data['Version']; - $this->update_server = $this->check_update_uri( $file_data['UpdateURI'] ); - } - - /** - * Ensure properly formatted Update URI. - * - * @param string $updateUri Data from Update URI header. - * - * @return string|WP_Error - */ - private function check_update_uri( $updateUri ) { - if ( filter_var( $updateUri, FILTER_VALIDATE_URL ) - && null === parse_url( $updateUri, PHP_URL_PATH ) // null means no path is present. - ) { - $updateUri = untrailingslashit( trim( $updateUri ) ); - } else { - return new WP_Error( 'invalid_header_data', 'Invalid data from Update URI header', $updateUri ); - } - - return $updateUri; - } - - /** - * Get API data. - * - * @global string $pagenow Current page. - * @return void|WP_Error - */ - public function run() { - global $pagenow; - - // Needed for mu-plugin. - if ( ! isset( $pagenow ) ) { - $php_self = isset( $_SERVER['PHP_SELF'] ) ? sanitize_url( wp_unslash( $_SERVER['PHP_SELF'] ) ) : null; - if ( null !== $php_self ) { - // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited - $pagenow = basename( $php_self ); - } - } - - // Only run on the following pages. - $pages = array( 'update-core.php', 'update.php', 'plugins.php', 'themes.php' ); - $view_details = array( 'plugin-install.php', 'theme-install.php' ); - $autoupdate_pages = array( 'admin-ajax.php', 'index.php', 'wp-cron.php' ); - if ( ! in_array( $pagenow, array_merge( $pages, $view_details, $autoupdate_pages ), true ) ) { - return; - } - - if ( empty( $this->update_server ) || is_wp_error( $this->update_server ) ) { - return new WP_Error( 'invalid_domain', 'Invalid update server domain', $this->update_server ); - } - $url = add_query_arg( - array( 'slug' => $this->slug ), - sprintf( '%s/wp-json/git-updater/v1/update-api/', $this->update_server ) - ); - $response = get_site_transient( "git-updater-lite_{$this->file}" ); - if ( ! $response ) { - $response = wp_remote_post( $url ); - if ( is_wp_error( $response ) || wp_remote_retrieve_response_code( $response ) === 404 ) { - return $response; - } - - $this->api_data = (object) json_decode( wp_remote_retrieve_body( $response ), true ); - if ( null === $this->api_data || empty( (array) $this->api_data ) || property_exists( $this->api_data, 'error' ) ) { - return new WP_Error( 'non_json_api_response', 'Poorly formed JSON', $response ); - } - $this->api_data->file = $this->file; - - /* - * Set transient for 5 minutes as AWS sets 5 minute timeout - * for release asset redirect. - * - * Set limited timeout so wp_remote_post() not hit as frequently. - * wp_remote_post() for plugin/theme check can run on every pageload - * for certain pages. - */ - set_site_transient( "git-updater-lite_{$this->file}", $this->api_data, 5 * \MINUTE_IN_SECONDS ); - } else { - if ( property_exists( $response, 'error' ) ) { - return new WP_Error( 'repo-no-exist', 'Specified repo does not exist' ); - } - $this->api_data = $response; - } - - $this->load_hooks(); - } - - /** - * Load hooks. - * - * @return void - */ - public function load_hooks() { - $type = $this->api_data->type; - add_filter( 'upgrader_source_selection', array( $this, 'upgrader_source_selection' ), 10, 4 ); - add_filter( "{$type}s_api", array( $this, 'repo_api_details' ), 99, 3 ); - add_filter( "site_transient_update_{$type}s", array( $this, 'update_site_transient' ), 20, 1 ); - if ( ! is_multisite() ) { - add_filter( 'wp_prepare_themes_for_js', array( $this, 'customize_theme_update_html' ) ); - } - - // Load hook for adding authentication headers for download packages. - add_filter( - 'upgrader_pre_download', - function () { - add_filter( 'http_request_args', array( $this, 'add_auth_header' ), 20, 2 ); - return false; // upgrader_pre_download filter default return value. - } - ); - } - - /** - * Correctly rename dependency for activation. - * - * @param string $source Path of $source. - * @param string $remote_source Path of $remote_source. - * @param WP_Upgrader $upgrader An Upgrader object. - * @param array $hook_extra Array of hook data. - * - * @throws TypeError If the type of $upgrader is not correct. - * - * @return string|WP_Error - */ - public function upgrader_source_selection( string $source, string $remote_source, WP_Upgrader $upgrader, $hook_extra = null ) { - global $wp_filesystem; - - $new_source = $source; - - // Exit if installing. - if ( isset( $hook_extra['action'] ) && 'install' === $hook_extra['action'] ) { - return $source; - } - - if ( ! $upgrader instanceof Plugin_Upgrader && ! $upgrader instanceof Theme_Upgrader ) { - throw new TypeError( __METHOD__ . '(): Argument #3 ($upgrader) must be of type Plugin_Upgrader|Theme_Upgrader, ' . esc_attr( gettype( $upgrader ) ) . ' given.' ); - } - - // Rename plugins. - if ( $upgrader instanceof Plugin_Upgrader ) { - if ( isset( $hook_extra['plugin'] ) ) { - $slug = dirname( $hook_extra['plugin'] ); - $new_source = trailingslashit( $remote_source ) . $slug; - } - } - - // Rename themes. - if ( $upgrader instanceof Theme_Upgrader ) { - if ( isset( $hook_extra['theme'] ) ) { - $slug = $hook_extra['theme']; - $new_source = trailingslashit( $remote_source ) . $slug; - } - } - - if ( basename( $source ) === $slug ) { - return $source; - } - - if ( trailingslashit( strtolower( $source ) ) !== trailingslashit( strtolower( $new_source ) ) ) { - $wp_filesystem->move( $source, $new_source, true ); - } - - return trailingslashit( $new_source ); - } - - /** - * Put changelog in plugins_api, return WP.org data as appropriate - * - * @param bool $result Default false. - * @param string $action The type of information being requested from the Plugin Installation API. - * @param stdClass $response Repo API arguments. - * - * @return stdClass|bool - */ - public function repo_api_details( $result, string $action, stdClass $response ) { - if ( "{$this->api_data->type}_information" !== $action ) { - return $result; - } - - // Exit if not our repo. - if ( $response->slug !== $this->api_data->slug ) { - return $result; - } - - return $this->api_data; - } - - /** - * Hook into site_transient_update_{plugins|themes} to update from GitHub. - * - * @param stdClass $transient Plugin|Theme update transient. - * - * @return stdClass - */ - public function update_site_transient( $transient ) { - // needed to fix PHP 7.4 warning. - if ( ! is_object( $transient ) ) { - $transient = new stdClass(); - } - - $response = array( - 'slug' => $this->api_data->slug, - $this->api_data->type => 'theme' === $this->api_data->type ? $this->api_data->slug : $this->api_data->file, - 'url' => isset( $this->api_data->url ) ? $this->api_data->url : $this->api_data->slug, - 'icons' => (array) $this->api_data->icons, - 'banners' => $this->api_data->banners, - 'branch' => $this->api_data->branch, - 'type' => "{$this->api_data->git}-{$this->api_data->type}", - 'update-supported' => true, - 'requires' => $this->api_data->requires, - 'requires_php' => $this->api_data->requires_php, - 'new_version' => $this->api_data->version, - 'package' => $this->api_data->download_link, - 'tested' => $this->api_data->tested, - ); - if ( 'theme' === $this->api_data->type ) { - $response['theme_uri'] = $response['url']; - } - - if ( version_compare( $this->api_data->version, $this->local_version, '>' ) ) { - $response = 'plugin' === $this->api_data->type ? (object) $response : $response; - $key = 'plugin' === $this->api_data->type ? $this->api_data->file : $this->api_data->slug; - $transient->response[ $key ] = $response; - } else { - $response = 'plugin' === $this->api_data->type ? (object) $response : $response; - - // Add repo without update to $transient->no_update for 'View details' link. - $transient->no_update[ $this->api_data->file ] = $response; - } - - return $transient; - } - - /** - * Add auth header for download package. - * - * @param array $args Array of http args. - * @param string $url Download URL. - * - * @return array - */ - public function add_auth_header( $args, $url ) { - if ( property_exists( $this->api_data, 'auth_header' ) - && str_contains( $url, $this->api_data->slug ) - ) { - $args = array_merge( $args, $this->api_data->auth_header ); - } - return $args; - } - - - /** - * Call theme messaging for single site installation. - * - * @author Seth Carstens - * - * @param array $prepared_themes Array of prepared themes. - * - * @return array - */ - public function customize_theme_update_html( $prepared_themes ) { - $theme = $this->api_data; - - if ( 'theme' !== $theme->type ) { - return $prepared_themes; - } - - if ( ! empty( $prepared_themes[ $theme->slug ]['hasUpdate'] ) ) { - $prepared_themes[ $theme->slug ]['update'] = $this->append_theme_actions_content( $theme ); - } else { - $prepared_themes[ $theme->slug ]['description'] .= $this->append_theme_actions_content( $theme ); - } - - return $prepared_themes; - } - - /** - * Create theme update messaging for single site installation. - * - * @author Seth Carstens - * - * @access protected - * @codeCoverageIgnore - * - * @param stdClass $theme Theme object. - * - * @return string (content buffer) - */ - protected function append_theme_actions_content( $theme ) { - $details_url = esc_attr( - add_query_arg( - array( - 'tab' => 'theme-information', - 'theme' => $theme->slug, - 'TB_iframe' => 'true', - 'width' => 270, - 'height' => 400, - ), - self_admin_url( 'theme-install.php' ) - ) - ); - $nonced_update_url = wp_nonce_url( - esc_attr( - add_query_arg( - array( - 'action' => 'upgrade-theme', - 'theme' => rawurlencode( $theme->slug ), - ), - self_admin_url( 'update.php' ) - ) - ), - 'upgrade-theme_' . $theme->slug - ); - - $current = get_site_transient( 'update_themes' ); - - /** - * Display theme update links. - */ - ob_start(); - if ( isset( $current->response[ $theme->slug ] ) ) { - ?> -

- - name ) - ); - printf( - ' ', - esc_url( $details_url ), - esc_attr( $theme->name ) - ); - if ( ! empty( $current->response[ $theme->slug ]['package'] ) ) { - printf( - /* translators: 1: opening anchor with version number, 2: closing anchor tag, 3: opening anchor with update URL */ - esc_html__( 'View version %1$s details%2$s or %3$supdate now%2$s.', 'fair' ), - $theme->remote_version = isset( $theme->remote_version ) ? esc_attr( $theme->remote_version ) : null, - '', - sprintf( - /* translators: %s: theme name */ - '', - esc_attr( $theme->name ) - ) - ); - } else { - printf( - /* translators: 1: opening anchor with version number, 2: closing anchor tag, 3: opening anchor with update URL */ - esc_html__( 'View version %1$s details%2$s.', 'fair' ), - $theme->remote_version = isset( $theme->remote_version ) ? esc_attr( $theme->remote_version ) : null, - '' - ); - echo( - '

' . esc_html__( 'Automatic update is unavailable for this theme.', 'fair' ) . '

' - ); - } - ?> - -

- Date: Wed, 27 Aug 2025 11:24:18 -0700 Subject: [PATCH 2/3] missed one, thanks Colin Signed-off-by: Andy Fragen --- plugin.php | 1 - 1 file changed, 1 deletion(-) diff --git a/plugin.php b/plugin.php index ca092781..6aeaa377 100644 --- a/plugin.php +++ b/plugin.php @@ -44,7 +44,6 @@ // External dependencies. require_once __DIR__ . '/inc/compatibility/php-polyfill.php'; require_once __DIR__ . '/inc/compatibility/wp-polyfill.php'; -require_once __DIR__ . '/inc/updater/class-lite.php'; /** * Load translations. From 00519ba489155038c49caa63298755b7751282da Mon Sep 17 00:00:00 2001 From: Andy Fragen Date: Mon, 27 Oct 2025 12:59:47 -0700 Subject: [PATCH 3/3] Fix merge fix Signed-off-by: Andy Fragen --- .github/workflows/generate-pot-pr.yml | 2 +- .github/workflows/playground-to-pr.yml | 25 ++ composer.json | 13 +- inc/dashboard-widgets/namespace.php | 2 +- inc/default-repo/namespace.php | 6 + inc/icons/namespace.php | 2 +- inc/namespace.php | 11 + inc/packages/admin/class-list-table.php | 43 +++ inc/packages/admin/info.php | 137 +++++++++- inc/packages/admin/namespace.php | 216 ++++++++++++++- inc/packages/class-metadatadocument.php | 8 + inc/packages/did/class-document.php | 12 +- inc/packages/namespace.php | 325 ++++++++++++++++++++--- inc/packages/wp-cli/compat/namespace.php | 292 ++++++++++++++++++++ inc/packages/wp-cli/namespace.php | 23 ++ inc/pings/namespace.php | 25 +- inc/updater/class-base58btc.php | 103 +++++++ inc/updater/class-updater.php | 29 +- inc/updater/namespace.php | 133 ++++++++++ inc/version-check/namespace.php | 9 +- languages/fair.pot | 235 +++++++++++----- phpcs.xml.dist | 1 + plugin.php | 6 +- 23 files changed, 1496 insertions(+), 162 deletions(-) create mode 100644 .github/workflows/playground-to-pr.yml create mode 100644 inc/packages/admin/class-list-table.php create mode 100644 inc/packages/wp-cli/compat/namespace.php create mode 100644 inc/packages/wp-cli/namespace.php create mode 100644 inc/updater/class-base58btc.php diff --git a/.github/workflows/generate-pot-pr.yml b/.github/workflows/generate-pot-pr.yml index 7d6d9013..c3c32546 100644 --- a/.github/workflows/generate-pot-pr.yml +++ b/.github/workflows/generate-pot-pr.yml @@ -51,7 +51,7 @@ jobs: wp i18n make-pot . "./languages/fair.pot" --headers='{"Report-Msgid-Bugs-To":"https://github.com/${{ github.event.repository.full_name }}/issues"}' - name: Check if there are changes - run: echo "CHANGES_DETECTED=$([[ -z $(git status --porcelain) ]] && echo "0" || echo "1")" >> $GITHUB_ENV + run: echo "CHANGES_DETECTED=$([[ -z $(git diff --ignore-matching-lines='^"POT-Creation-Date') ]] && echo "0" || echo "1")" >> $GITHUB_ENV - name: Commit changes if: env.CHANGES_DETECTED == 1 diff --git a/.github/workflows/playground-to-pr.yml b/.github/workflows/playground-to-pr.yml new file mode 100644 index 00000000..fb07062d --- /dev/null +++ b/.github/workflows/playground-to-pr.yml @@ -0,0 +1,25 @@ +name: Add Playground link to PR + +on: + pull_request_target: + types: [opened] + +jobs: + add-comment-with-link: + runs-on: ubuntu-latest + steps: + - name: add-comment + uses: actions/github-script@v7 + with: + github-token: ${{ github.token }} + script: | + const prNumber = context.issue.number + const repoName = context.repo.owner + '/' + context.repo.repo + const playgroundUrl = `https://playground.wordpress.net/?mode=seamless#{%22siteOptions%22:{%22blogname%22:%22FAIR%20Plugin%20Test%20Site:%20PR%20#${prNumber}%22},%22features%22:{%22networking%22:true},%22login%22:true,%22landingPage%22:%22/wp-admin/%22,%22plugins%22:[%22https://github-proxy.com/proxy/?repo=${repoName}&pr=${prNumber}%22],%22preferredVersions%22:{%22php%22:%228.3%22,%22wp%22:%22latest%22}}` + + github.rest.issues.createComment({ + issue_number: prNumber, + owner: context.repo.owner, + repo: context.repo.repo, + body: `[🧪 Test this PR on Playground](${playgroundUrl})` + }) diff --git a/composer.json b/composer.json index d2f9e76d..99c2add4 100644 --- a/composer.json +++ b/composer.json @@ -1,11 +1,13 @@ { - "name": "fair/fair-plugin", + "name": "fairpm/fair-plugin", "description": "Make your site more FAIR.", "type": "wordpress-plugin", "license": "gpl-2.0-only", "authors": [ { - "name": "FAIR Contributors" + "name": "FAIR Contributors", + "email": "operations@fair.pm", + "homepage": "https://fair.pm" } ], "require": { @@ -62,5 +64,10 @@ "@coverage:multisite", "@coverage:merge" ] - } + }, + "support": { + "issues": "https://github.com/fairpm/fair-plugin/issues", + "source": "https://github.com/fairpm/fair-plugin", + "docs": "https://github.com/fairpm/fair-plugin/blob/main/README.md" + } } diff --git a/inc/dashboard-widgets/namespace.php b/inc/dashboard-widgets/namespace.php index df0acfa4..78a71165 100644 --- a/inc/dashboard-widgets/namespace.php +++ b/inc/dashboard-widgets/namespace.php @@ -7,7 +7,7 @@ namespace FAIR\Dashboard_Widgets; -use const FAIR\Packages\CACHE_LIFETIME; +use const FAIR\CACHE_LIFETIME; use WP_Error; diff --git a/inc/default-repo/namespace.php b/inc/default-repo/namespace.php index b98e3aaf..6719ede4 100644 --- a/inc/default-repo/namespace.php +++ b/inc/default-repo/namespace.php @@ -7,6 +7,8 @@ namespace FAIR\Default_Repo; +use FAIR; + /** * Bootstrap. */ @@ -63,6 +65,10 @@ function replace_repo_api_urls( $status, $args, $url ) { // Alter the URL, then reissue the request (with a lock to prevent loops). $url = str_replace( '//api.wordpress.org/', '//' . get_default_repo_domain() . '/', $url ); + + // Indicate this is a FAIR install. + $url = add_query_arg( '_fair', FAIR\VERSION, $url ); + $is_replacing = true; $response = wp_remote_request( $url, $args ); $is_replacing = false; diff --git a/inc/icons/namespace.php b/inc/icons/namespace.php index 65b54e76..e91d34ea 100644 --- a/inc/icons/namespace.php +++ b/inc/icons/namespace.php @@ -31,7 +31,7 @@ function set_default_icon( $transient ) { $transient = new stdClass(); } - if ( ! property_exists( $transient, 'response' ) ) { + if ( ! property_exists( $transient, 'response' ) || ! is_array( $transient->response ) ) { return $transient; } diff --git a/inc/namespace.php b/inc/namespace.php index 1be91d91..2133e19b 100644 --- a/inc/namespace.php +++ b/inc/namespace.php @@ -7,6 +7,8 @@ namespace FAIR; +const CACHE_BASE = 'fair-'; +const CACHE_LIFETIME = 12 * HOUR_IN_SECONDS; const NS_SEPARATOR = '\\'; /** @@ -78,3 +80,12 @@ function register_class_path( string $prefix, string $path ) : void { } ); Version_Check\bootstrap(); } + +/** + * Check if WP-CLI is running. + * + * @return bool True if running in WP-CLI, false otherwise. + */ +function is_wp_cli(): bool { + return defined( 'WP_CLI' ) && WP_CLI; +} diff --git a/inc/packages/admin/class-list-table.php b/inc/packages/admin/class-list-table.php new file mode 100644 index 00000000..0d179b70 --- /dev/null +++ b/inc/packages/admin/class-list-table.php @@ -0,0 +1,43 @@ + $content ) { $prepared = sanitize_html( $content ); $prepared = links_add_target( $prepared, '_blank' ); @@ -271,15 +274,20 @@ function name_requirement( string $requirement ) : string { * @return void */ function render_fyi( MetadataDocument $doc, ReleaseDocument $release ) : void { + $did = Packages\get_did_document( $doc->id ); ?>
    +
  • version ); ?>
  • slug ) ) : ?>
  • slug ); ?>
  • + id ) ) : ?> +
  • id ); ?>
  • + requires ) ) : ?>
  • @@ -322,10 +330,45 @@ function render_fyi( MetadataDocument $doc, ReleaseDocument $release ) : void { ?>
+ id ); + if ( $repo_host ) : + ?> +

Plugin available via FAIR repository hosted at

+ +

Plugin available via FAIR repository

+
get_service( Packages\SERVICE_ID ); + if ( empty( $repo ) ) { + return null; + } + + // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase + $host = parse_url( $repo->serviceEndpoint, PHP_URL_HOST ); + if ( empty( $host ) ) { + // Invalid URL. + return null; + } + + return $host; +} + /** * Check requirements, and add notices if not met. * @@ -392,6 +435,76 @@ function add_requirement_notices( ReleaseDocument $release ) : void { } } +/** + * Render the validation notice. + * + * Renders the validation status for the package's alias. Also returns a bool + * indicating whether the package is "safe" to install - packages which fail + * validation are not safe, while those without an alias or with a valid alias + * are safe. + * + * @param DIDDocument $did DID to validate. + * @return bool True if the package is "safe" to install, false if install should be blocked. + */ +function render_alias_notice( DIDDocument $did ) : bool { + $validation = Packages\validate_package_alias( $did ); + $title = __( 'Domain Alias:', 'fair' ); + $result = false; + switch ( gettype( $validation ) ) { + case 'string': + $message = sprintf( + /* translators: %1$s: full URL for validated domain, %2$s: raw domain */ + __( 'Validated as %2$s', 'fair' ), + esc_url( 'https://' . $validation . '/' ), + esc_html( $validation ) + ); + $result = true; + break; + + case 'NULL': + $message = __( 'Not validated: No domain alias is set', 'fair' ); + $result = true; + break; + + default: + if ( ! is_wp_error( $validation ) ) { + // Invalid type, assume failure. + $validation = new WP_Error( + 'fair.packages.admin.info.validation_notice.invalid_result', + __( 'An unknown error occurred', 'fair' ) + ); + } + $message = sprintf( + '%s', + esc_html__( 'Validation failed', 'fair' ) + ); + add_action( 'minifair.render.notices', function () use ( $validation ) { + wp_admin_notice( + sprintf( + /* translators: %s: validation error message */ + __( '

Error: Failed domain alias validation, this package may be unsafe: %s

', 'fair' ), + esc_html( $validation->get_error_message() ) + ), + [ + 'type' => 'error', + 'additional_classes' => [ 'notice-alt' ], + 'paragraph_wrap' => false, + ] + ); + } ); + $result = false; + break; + } + + printf( + '%s %s', + esc_html( $title ), + // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Sanitized. + sanitize_html( $message ) + ); + return $result; +} + /** * Gets the markup for the plugin install action button. * @@ -416,6 +529,15 @@ function get_action_button( MetadataDocument $doc, ReleaseDocument $release ) { $status = 'installed'; } + if ( $status === 'install' ) { + $file = null; + $slug = null; + } else { + $file = Updater\get_packages()[ "{$type}s" ][ $doc->id ]; + $file = $type === 'plugin' ? plugin_basename( $file ) : basename( dirname( $file ) ); + $slug = $type === 'plugin' ? dirname( $file ) : $file; + } + // Do we actually meet the requirements? $compatible = Packages\check_requirements( $release ); switch ( $status ) { @@ -447,10 +569,6 @@ function get_action_button( MetadataDocument $doc, ReleaseDocument $release ) { ); } - $file = Updater\get_packages()[ "{$type}s" ][ $doc->id ]; - $file = $type === 'plugin' ? plugin_basename( $file ) : basename( dirname( $file ) ); - $slug = $type === 'plugin' ? dirname( $file ) : $file; - return sprintf( '%s', esc_attr( $doc->id ), @@ -465,6 +583,17 @@ function get_action_button( MetadataDocument $doc, ReleaseDocument $release ) { ); case 'installed': + if ( current_user_can( 'activate_plugin', $file ) && is_plugin_inactive( $file ) ) { + return sprintf( + '%s', + esc_url( wp_nonce_url( self_admin_url( 'plugins.php?action=activate&plugin=' . rawurlencode( $file ) ), 'activate-plugin_' . $file ) ), + /* translators: %s: The package's name. */ + esc_attr( sprintf( __( 'Activate %s now', 'fair' ), $doc->name ) ), + esc_attr( $doc->name ), + esc_html__( 'Activate', 'fair' ) + ); + } + return sprintf( '', esc_html__( 'Installed', 'fair' ) diff --git a/inc/packages/admin/namespace.php b/inc/packages/admin/namespace.php index 910f0f05..3c8ad833 100644 --- a/inc/packages/admin/namespace.php +++ b/inc/packages/admin/namespace.php @@ -16,7 +16,6 @@ const TAB_DIRECT = 'fair_direct'; const ACTION_INSTALL = 'fair-install-plugin'; const ACTION_INSTALL_NONCE = 'fair-install-plugin'; -const ACTION_INSTALL_DID = 'fair-install-did'; /** * Bootstrap. @@ -28,11 +27,35 @@ function bootstrap() { add_filter( 'install_plugins_tabs', __NAMESPACE__ . '\\add_direct_tab' ); add_filter( 'plugins_api', __NAMESPACE__ . '\\handle_did_during_ajax', 10, 3 ); + add_filter( 'plugins_api', 'FAIR\\Packages\\search_by_did', 10, 3 ); + add_filter( 'upgrader_package_options', 'FAIR\\Packages\\cache_did_for_install', 10, 1 ); + add_action( 'upgrader_post_install', 'FAIR\\Packages\\delete_cached_did_for_install', 10, 3 ); add_filter( 'upgrader_pre_download', 'FAIR\\Packages\\upgrader_pre_download', 10, 1 ); add_action( 'install_plugins_' . TAB_DIRECT, __NAMESPACE__ . '\\render_tab_direct' ); add_action( 'load-plugin-install.php', __NAMESPACE__ . '\\load_plugin_install' ); add_action( 'install_plugins_pre_plugin-information', __NAMESPACE__ . '\\maybe_hijack_plugin_info', 0 ); + add_filter( 'plugins_api_result', __NAMESPACE__ . '\\alter_slugs', 10, 3 ); + add_filter( 'plugin_install_action_links', __NAMESPACE__ . '\\maybe_hijack_plugin_install_button', 10, 2 ); + add_filter( 'plugin_install_description', __NAMESPACE__ . '\\maybe_add_data_to_description', 10, 2 ); add_action( 'wp_ajax_check_plugin_dependencies', __NAMESPACE__ . '\\set_slug_to_hashed' ); + add_filter( 'wp_list_table_class_name', __NAMESPACE__ . '\\maybe_override_list_table' ); +} + +/** + * Override the install list table with our own. + * + * @param string $class_name List table class name to use. + * @return string Overridden class name. + */ +function maybe_override_list_table( $class_name ) { + if ( $class_name !== 'WP_Plugin_Install_List_Table' ) { + return $class_name; + } + + // Load list table class. We must do this here, as WP_List_Table isn't loaded by default. + require_once ABSPATH . 'wp-admin/includes/class-wp-plugin-install-list-table.php'; + require_once __DIR__ . '/class-list-table.php'; + return List_Table::class; } /** @@ -65,15 +88,16 @@ function handle_did_during_ajax( $result, $action, $args ) { } $did = 'did:' . explode( '-did:', str_replace( '--', ':', $slug ), 2 )[1]; - if ( ! preg_match( '/^did:(web|plc):.+$/', $did ) ) { + if ( ! preg_match( '/^did:plc:.+$/', $did ) ) { return $result; } - wp_cache_set( ACTION_INSTALL_DID, $did ); + ( new Updater\Updater( $did ) )->run(); + Packages\add_package_to_release_cache( $did ); add_filter( 'http_request_args', 'FAIR\\Packages\\maybe_add_accept_header', 20, 2 ); - return (object) Packages\get_update_data( $did ); + return (object) Packages\get_package_data( $did ); } /** @@ -129,15 +153,15 @@ class="screen-reader-text" type="text" id="plugin_id" name="plugin_id" - pattern="did:(web|plc):.+" - placeholder="did:..." + pattern="did:plc:.+" + placeholder="did:plc:..." required aria-describedby="fair-direct-install__note" />

- did:web:... or did:plc:...', 'fair' ); ?> + did:plc:...', 'fair' ); ?>