mirror of
https://ghproxy.net/https://github.com/fairpm/fair-plugin.git
synced 2025-09-04 10:30:44 +08:00
688 lines
18 KiB
PHP
688 lines
18 KiB
PHP
<?php
|
|
/**
|
|
* Install FAIR packages.
|
|
*
|
|
* @package FAIR
|
|
*/
|
|
|
|
namespace FAIR\Packages;
|
|
|
|
use FAIR\Packages\DID\PLC;
|
|
use FAIR\Packages\DID\Web;
|
|
use FAIR\Updater;
|
|
use WP_Error;
|
|
use WP_Upgrader;
|
|
|
|
const SERVICE_ID = 'FairPackageManagementRepo';
|
|
const CONTENT_TYPE = 'application/json+fair';
|
|
const CACHE_KEY = 'fair-packages-';
|
|
const CACHE_LIFETIME = 12 * HOUR_IN_SECONDS;
|
|
const RELEASE_PACKAGES_CACHE_KEY = 'fair-release-packages';
|
|
|
|
// phpcs:disable WordPress.NamingConventions.ValidVariableName
|
|
|
|
/**
|
|
* Bootstrap.
|
|
*
|
|
* @return void
|
|
*/
|
|
function bootstrap() {
|
|
Admin\bootstrap();
|
|
}
|
|
|
|
/**
|
|
* Parse DID.
|
|
*
|
|
* @param string $id DID.
|
|
* @return DID|WP_Error
|
|
*/
|
|
function parse_did( string $id ) {
|
|
if ( ! str_starts_with( $id, 'did:' ) ) {
|
|
return new WP_Error( 'fair.packages.validate_did.not_did', __( 'ID is not a valid DID.', 'fair' ) );
|
|
}
|
|
|
|
$parts = explode( ':', $id, 3 );
|
|
if ( count( $parts ) !== 3 ) {
|
|
return new WP_Error( 'fair.packages.validate_did.not_uri', __( 'DID could not be parsed as a URI.', 'fair' ) );
|
|
}
|
|
|
|
switch ( $parts[1] ) {
|
|
case PLC::METHOD:
|
|
return new PLC( $id );
|
|
|
|
case Web::METHOD:
|
|
return new Web( $id );
|
|
|
|
default:
|
|
return new WP_Error( 'fair.packages.validate_did.invalid_method', __( 'Unsupported DID method.', 'fair' ) );
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Return hash of DID.
|
|
*
|
|
* This ensures a consistent representation of a DID
|
|
* regardless of its method, method-specific-id format, or length.
|
|
*
|
|
* @param string $id DID
|
|
*
|
|
* @return string|WP_Error
|
|
*/
|
|
function get_did_hash( string $id ) {
|
|
$did = parse_did( $id );
|
|
if ( is_wp_error( $did ) ) {
|
|
return $did;
|
|
}
|
|
|
|
return substr( hash( 'sha256', $did->get_id() ), 0, 6 );
|
|
}
|
|
|
|
/**
|
|
* Get DID document.
|
|
*
|
|
* @param string $id DID.
|
|
* @return DIDDocument|WP_Error
|
|
*/
|
|
function get_did_document( string $id ) {
|
|
$cached = wp_cache_get( $id, 'did-docs' );
|
|
if ( $cached ) {
|
|
return $cached;
|
|
}
|
|
|
|
// Parse the DID, then fetch the details.
|
|
$did = parse_did( $id );
|
|
if ( is_wp_error( $did ) ) {
|
|
return $did;
|
|
}
|
|
|
|
$document = $did->fetch_document();
|
|
if ( is_wp_error( $document ) ) {
|
|
return $document;
|
|
}
|
|
wp_cache_set( $id, $document, 'did-docs', CACHE_LIFETIME );
|
|
|
|
return $document;
|
|
}
|
|
|
|
/**
|
|
* Fetch metadata for a package.
|
|
*
|
|
* @param string $id DID of the package to fetch metadata for.
|
|
* @return MetadataDocument|WP_Error Metadata document on success, WP_Error on failure.
|
|
*/
|
|
function fetch_package_metadata( string $id ) {
|
|
$document = get_did_document( $id );
|
|
if ( is_wp_error( $document ) ) {
|
|
return $document;
|
|
}
|
|
|
|
// Fetch data from the repository.
|
|
$service = $document->get_service( SERVICE_ID );
|
|
if ( empty( $service ) ) {
|
|
return new WP_Error( 'fair.packages.fetch_metadata.no_service', __( 'DID is not a valid package to fetch metadata for.', 'fair' ) );
|
|
}
|
|
$repo_url = $service->serviceEndpoint;
|
|
|
|
$metadata = fetch_metadata_doc( $repo_url );
|
|
|
|
if ( is_wp_error( $metadata ) ) {
|
|
return $metadata;
|
|
}
|
|
|
|
if ( $metadata->id !== $id ) {
|
|
return new WP_Error( 'fair.packages.fetch_metadata.mismatch', __( 'Fetched metadata does not match the requested DID.', 'fair' ) );
|
|
}
|
|
|
|
return $metadata;
|
|
}
|
|
|
|
/**
|
|
* Fetch the metadata document for a package.
|
|
*
|
|
* @param string $url URL for the metadata document.
|
|
* @return MetadataDocument|WP_Error
|
|
*/
|
|
function fetch_metadata_doc( string $url ) {
|
|
$cache_key = CACHE_KEY . md5( $url );
|
|
$response = wp_cache_get( $cache_key, 'metadata-docs' );
|
|
|
|
if ( ! $response ) {
|
|
$response = wp_remote_get( $url, [
|
|
'headers' => [
|
|
'Accept' => sprintf( '%s;q=1.0, application/json;q=0.8', CONTENT_TYPE ),
|
|
],
|
|
'timeout' => 7,
|
|
] );
|
|
$code = wp_remote_retrieve_response_code( $response );
|
|
if ( is_wp_error( $response ) ) {
|
|
return $response;
|
|
} elseif ( $code !== 200 ) {
|
|
return new WP_Error( 'fair.packages.metadata.failure', __( 'HTTP error code received', 'fair' ) );
|
|
}
|
|
wp_cache_set( $cache_key, $response, 'metadata-docs', CACHE_LIFETIME );
|
|
}
|
|
|
|
return MetadataDocument::from_response( $response );
|
|
}
|
|
|
|
/**
|
|
* Select the best release from a list of releases.
|
|
*
|
|
* @param array $releases List of releases to choose from.
|
|
* @param string|null $version Version to select. If null, the latest release is returned.
|
|
* @return ReleaseDocument|null The selected release or null if not found.
|
|
*/
|
|
function pick_release( array $releases, ?string $version = null ) : ?ReleaseDocument {
|
|
// Sort releases by version, descending.
|
|
usort( $releases, fn ( $a, $b ) => version_compare( $b->version, $a->version ) );
|
|
|
|
// If no version is specified, return the latest release.
|
|
if ( empty( $version ) ) {
|
|
return reset( $releases );
|
|
}
|
|
|
|
return array_find( $releases, fn ( $release ) => $release->version === $version );
|
|
}
|
|
|
|
/**
|
|
* Get the latest release for a DID.
|
|
*
|
|
* @param string $id DID.
|
|
*
|
|
* @return ReleaseDocument|WP_Error The latest release, or a WP_Error object on failure.
|
|
*/
|
|
function get_latest_release_from_did( $id ) {
|
|
$document = get_did_document( $id );
|
|
if ( is_wp_error( $document ) ) {
|
|
return $document;
|
|
}
|
|
|
|
$valid_keys = $document->get_fair_signing_keys();
|
|
if ( empty( $valid_keys ) ) {
|
|
return new WP_Error( 'fair.packages.install.no_signing_keys', __( 'DID does not contain valid signing keys.', 'fair' ) );
|
|
}
|
|
|
|
$metadata = fetch_package_metadata( $id );
|
|
if ( is_wp_error( $metadata ) ) {
|
|
return $metadata;
|
|
}
|
|
|
|
$release = pick_release( $metadata->releases );
|
|
if ( empty( $release ) ) {
|
|
return new WP_Error( 'fair.packages.install.no_releases', __( 'No releases found in the repository.', 'fair' ) );
|
|
}
|
|
|
|
return $release;
|
|
}
|
|
|
|
/**
|
|
* Get viable languages for a given locale.
|
|
*
|
|
* Based on the RFC4647 language matching algorithm, with slight modifications.
|
|
* In particular, the base language code (e.g. "de") is treated as equivalent
|
|
* to language-plus-country/region with the same name (e.g. "de-DE").
|
|
*
|
|
* Additionally, for WordPress-compatibility, underscores are treated as
|
|
* separators equivalent to hyphens. The default language is "en-US" or "en".
|
|
*
|
|
* The priority list can be filtered using the
|
|
* `fair.packages.language_priority_list` filter.
|
|
*
|
|
* @see https://datatracker.ietf.org/doc/html/rfc4647
|
|
* @see https://datatracker.ietf.org/doc/html/rfc5646
|
|
*
|
|
* @param string|null $locale Locale to match against. Defaults to the current locale.
|
|
* @return string[]|null Prioritized list of language codes.
|
|
*/
|
|
function get_language_priority_list( ?string $locale = null ) {
|
|
$locale = $locale ?: get_locale();
|
|
$locale = strtolower( str_replace( '_', '-', $locale ) );
|
|
$langs = [];
|
|
$langs[] = $locale;
|
|
|
|
if ( strpos( $locale, '-' ) !== false ) {
|
|
// Add all possible prefixes.
|
|
$i = strlen( $locale );
|
|
do {
|
|
$i = strrpos( substr( $locale, 0, $i ), '-' );
|
|
if ( $i === false ) {
|
|
break;
|
|
}
|
|
|
|
// If this is just "x", skip it.
|
|
if ( substr( $locale, $i - 1, 1 ) === 'x' ) {
|
|
continue;
|
|
}
|
|
|
|
$langs[] = substr( $locale, 0, $i );
|
|
} while ( $i > 0 );
|
|
}
|
|
|
|
/*
|
|
* Double the primary language code, to catch cases where the
|
|
* locale matches the country code. (e.g. de becomes de-DE.)
|
|
*/
|
|
$primary = substr( $locale, 0, strpos( $locale, '-' ) );
|
|
$langs[] = $primary . '-' . $primary;
|
|
|
|
// Defaults.
|
|
$langs[] = 'en-us';
|
|
$langs[] = 'en';
|
|
|
|
/**
|
|
* Filter the list of languages to prioritize.
|
|
*/
|
|
return apply_filters( 'fair.packages.language_priority_list', $langs, $locale );
|
|
}
|
|
|
|
/**
|
|
* Pick the best matching artifact based on the current locale.
|
|
*
|
|
* Uses the language priority list to pick the best scoring artifact. The
|
|
* algorithm can be overridden by the
|
|
* `fair.packages.pick_artifact_by_lang` filter.
|
|
*
|
|
* @see get_language_priority_list()
|
|
*
|
|
* @param array $artifacts List of artifacts to choose from.
|
|
* @param string|null $locale Locale to match against. Defaults to the current locale.
|
|
* @return stdClass|null The best matching artifact or null if none found.
|
|
*/
|
|
function pick_artifact_by_lang( array $artifacts, ?string $locale = null ) {
|
|
$langs = get_language_priority_list( $locale );
|
|
|
|
// Score artifacts based on match.
|
|
$score_artifact = function ( $artifact ) use ( $langs ) {
|
|
$score = 0;
|
|
|
|
// Check for lang match.
|
|
$idx = array_search( strtolower( $artifact->lang ), $langs, true );
|
|
if ( $idx !== false ) {
|
|
$score += ( count( $langs ) - $idx ) * 100;
|
|
}
|
|
|
|
return $score;
|
|
};
|
|
usort( $artifacts, function ( $a, $b ) use ( $score_artifact ) {
|
|
$a_score = $score_artifact( $a );
|
|
$b_score = $score_artifact( $b );
|
|
|
|
return $b_score <=> $a_score;
|
|
} );
|
|
|
|
// Return the best match.
|
|
$selected = reset( $artifacts );
|
|
|
|
/**
|
|
* Filter the selected artifact.
|
|
*/
|
|
return apply_filters( 'fair.packages.pick_artifact_by_lang', $selected, $artifacts, $locale, $langs );
|
|
}
|
|
|
|
/**
|
|
* Get version requirements.
|
|
*
|
|
* @param ReleaseDocument $release Release document.
|
|
*
|
|
* @return array
|
|
*/
|
|
function version_requirements( ReleaseDocument $release ) {
|
|
$required_versions = [];
|
|
foreach ( $release->requires as $pkg => $vers ) {
|
|
$vers = preg_replace( '/^[^0-9]+/', '', $vers );
|
|
if ( $pkg === 'env:php' ) {
|
|
$required_versions['requires_php'] = $vers;
|
|
}
|
|
if ( $pkg === 'env:wp' ) {
|
|
$required_versions['requires_wp'] = $vers;
|
|
}
|
|
}
|
|
foreach ( $release->suggests as $pkg => $vers ) {
|
|
$vers = preg_replace( '/^[^0-9]+/', '', $vers );
|
|
if ( $pkg === 'env:wp' ) {
|
|
$required_versions['tested_to'] = $vers;
|
|
}
|
|
}
|
|
|
|
return $required_versions;
|
|
}
|
|
|
|
/**
|
|
* Get unmet requirements.
|
|
*
|
|
* @param array $requirements Requirements to check. Map of package names to requirement strings.
|
|
* @return array Map of package names to unmet requirements.
|
|
*/
|
|
function get_unmet_requirements( array $requirements ) : array {
|
|
$unmet = [];
|
|
foreach ( $requirements as $pkg => $req_list ) {
|
|
$req_parts = explode( ',', $req_list );
|
|
$req_unmet = [];
|
|
foreach ( $req_parts as $req ) {
|
|
$req = trim( $req );
|
|
$comp_spn = strspn( $req, '<>=!' );
|
|
if ( $comp_spn === 0 ) {
|
|
// Invalid requirement, for now.
|
|
continue;
|
|
}
|
|
|
|
$comp = trim( substr( $req, 0, $comp_spn ) );
|
|
$ver = trim( substr( $req, $comp_spn ) );
|
|
|
|
switch ( true ) {
|
|
case $pkg === 'env:wp':
|
|
// From is_wp_version_compatible()
|
|
// We use our own copy to allow passing $comp.
|
|
if (
|
|
defined( 'WP_RUN_CORE_TESTS' )
|
|
&& WP_RUN_CORE_TESTS
|
|
&& isset( $GLOBALS['_wp_tests_wp_version'] )
|
|
) {
|
|
$wp_version = $GLOBALS['_wp_tests_wp_version'];
|
|
} else {
|
|
$wp_version = wp_get_wp_version();
|
|
}
|
|
|
|
$valid = version_compare( $wp_version, $ver, $comp );
|
|
if ( ! $valid ) {
|
|
$req_unmet[] = $req;
|
|
}
|
|
break;
|
|
|
|
case $pkg === 'env:php':
|
|
$valid = version_compare( PHP_VERSION, $ver, $comp );
|
|
if ( ! $valid ) {
|
|
$req_unmet[] = $req;
|
|
}
|
|
break;
|
|
|
|
case str_starts_with( $pkg, 'env:php-' ):
|
|
// todo: check extensions.
|
|
break;
|
|
|
|
case str_starts_with( $pkg, 'env:' ):
|
|
// todo: check other env, or fail.
|
|
break;
|
|
|
|
default:
|
|
// todo: check packages.
|
|
break;
|
|
}
|
|
}
|
|
if ( ! empty( $req_unmet ) ) {
|
|
$unmet[ $pkg ] = implode( ', ', $req_unmet );
|
|
}
|
|
}
|
|
|
|
return $unmet;
|
|
}
|
|
|
|
/**
|
|
* Check if a release meets the requirements.
|
|
*
|
|
* @param ReleaseDocument $release Release document.
|
|
*
|
|
* @return bool True if the release meets the requirements, false otherwise.
|
|
*/
|
|
function check_requirements( ReleaseDocument $release ) {
|
|
$requires = get_unmet_requirements( (array) $release->requires );
|
|
return empty( $requires );
|
|
}
|
|
|
|
/**
|
|
* Get the installed version of a package.
|
|
*
|
|
* @param string $id DID of the package to check.
|
|
* @param string $type Type of the package (e.g. 'plugin', 'theme').
|
|
*
|
|
* @return string|null The installed version, or null if not installed.
|
|
*/
|
|
function get_installed_version( string $id, string $type ) {
|
|
$type .= 's';
|
|
$packages = Updater\get_packages();
|
|
|
|
if ( empty( $packages[ $type ][ $id ] ) ) {
|
|
// Not installed.
|
|
return null;
|
|
}
|
|
|
|
return get_file_data( $packages[ $type ][ $id ], [ 'Version' => 'Version' ] )['Version'];
|
|
}
|
|
|
|
/**
|
|
* Get icons.
|
|
*
|
|
* @param array $icons Array of icon data.
|
|
*
|
|
* @return array
|
|
*/
|
|
function get_icons( $icons ) : array {
|
|
if ( empty( $icons ) ) {
|
|
return [];
|
|
}
|
|
|
|
$icons_arr = [];
|
|
$regular = array_find( $icons, fn ( $icon ) => $icon->width === 772 && $icon->height === 250 );
|
|
$high_res = array_find( $icons, fn ( $icon ) => $icon->width === 1544 && $icon->height === 500 );
|
|
$svg = array_find( $icons, fn ( $icon ) => str_contains( $icon->{'content-type'}, 'svg+xml' ) );
|
|
|
|
if ( empty( $regular ) && empty( $high_res ) && empty( $svg ) ) {
|
|
return [];
|
|
}
|
|
|
|
$icons_arr['1x'] = $regular->url ?? '';
|
|
$icons_arr['2x'] = $high_res->url ?? '';
|
|
if ( str_contains( $svg->url, 's.w.org/plugins' ) ) {
|
|
$icons_arr['default'] = $svg->url;
|
|
} else {
|
|
$icons_arr['svg'] = $svg->url ?? '';
|
|
}
|
|
|
|
return $icons_arr;
|
|
}
|
|
|
|
/**
|
|
* Get banners.
|
|
*
|
|
* @param array $banners Array of banner data.
|
|
*
|
|
* @return array
|
|
*/
|
|
function get_banners( $banners ) : array {
|
|
if ( empty( $banners ) ) {
|
|
return [];
|
|
}
|
|
|
|
$banners_arr = [];
|
|
$regular = array_find( $banners, fn ( $banner ) => $banner->width === 772 && $banner->height === 250 );
|
|
$high_res = array_find( $banners, fn ( $banner ) => $banner->width === 1544 && $banner->height === 500 );
|
|
|
|
if ( empty( $regular ) && empty( $high_res ) ) {
|
|
return [];
|
|
}
|
|
|
|
$banners_arr['low'] = $regular->url;
|
|
$banners_arr['high'] = $high_res->url;
|
|
|
|
return $banners_arr;
|
|
}
|
|
|
|
/**
|
|
* Get hashed file name from MetadataDocument.
|
|
*
|
|
* @param MetadataDocument $metadata MetadataDocument.
|
|
*
|
|
* @return string
|
|
*/
|
|
function get_hashed_filename( $metadata ) : string {
|
|
$filename = $metadata->filename;
|
|
$type = str_replace( 'wp-', '', $metadata->type );
|
|
$did_hash = '-' . get_did_hash( $metadata->id );
|
|
|
|
list( $slug, $file ) = explode( '/', $filename, 2 );
|
|
if ( 'plugin' === $type ) {
|
|
if ( ! str_contains( $slug, $did_hash ) ) {
|
|
$slug .= $did_hash;
|
|
}
|
|
$filename = $slug . '/' . $file;
|
|
} else {
|
|
$filename = $slug . $did_hash;
|
|
}
|
|
|
|
return $filename;
|
|
}
|
|
|
|
/**
|
|
* Get update data for use with transient and API responses.
|
|
*
|
|
* @param string $did DID.
|
|
* @return array|WP_Error
|
|
*/
|
|
function get_update_data( $did ) {
|
|
$metadata = fetch_package_metadata( $did );
|
|
if ( is_wp_error( $metadata ) ) {
|
|
return $metadata;
|
|
}
|
|
|
|
$release = get_latest_release_from_did( $did );
|
|
if ( is_wp_error( $release ) ) {
|
|
return $release;
|
|
}
|
|
|
|
$required_versions = version_requirements( $release );
|
|
$filename = get_hashed_filename( $metadata );
|
|
$type = str_replace( 'wp-', '', $metadata->type );
|
|
|
|
$response = [
|
|
'name' => $metadata->name,
|
|
'author' => $metadata->authors[0]->name,
|
|
'author_uri' => $metadata->authors[0]->url,
|
|
'slug' => $metadata->slug . '-' . get_did_hash( $did ),
|
|
$type => $filename,
|
|
'file' => $filename,
|
|
'url' => $metadata->url ?? $metadata->slug,
|
|
'sections' => (array) $metadata->sections,
|
|
'icons' => isset( $release->artifacts->icon ) ? get_icons( $release->artifacts->icon ) : [],
|
|
'banners' => isset( $release->artifacts->banner ) ? get_banners( $release->artifacts->banner ) : [],
|
|
'update-supported' => true,
|
|
'requires' => $required_versions['requires_wp'] ?? '',
|
|
'requires_php' => $required_versions['requires_php'] ?? '',
|
|
'new_version' => $release->version,
|
|
'version' => $release->version,
|
|
'remote_version' => $release->version,
|
|
'package' => $release->artifacts->package[0]->url,
|
|
'download_link' => $release->artifacts->package[0]->url,
|
|
'tested' => $required_versions['tested_to'] ?? '',
|
|
'external' => 'xxx',
|
|
];
|
|
if ( 'theme' === $type ) {
|
|
$response['theme_uri'] = $response['url'];
|
|
}
|
|
|
|
return $response;
|
|
}
|
|
|
|
/**
|
|
* Send upgrader_pre_download filter to hook `upgrader_source_selection` during AJAX
|
|
* and send to `maybe_add_accept_header()`.
|
|
*
|
|
* @param bool $false Whether to bail without returning the package.
|
|
* Default false.
|
|
* @return bool
|
|
*/
|
|
function upgrader_pre_download( $false ) : bool {
|
|
add_filter( 'http_request_args', 'FAIR\\Packages\\maybe_add_accept_header', 20, 2 );
|
|
add_filter( 'upgrader_source_selection', __NAMESPACE__ . '\\rename_source_selection', 10, 3 );
|
|
return $false;
|
|
}
|
|
|
|
/**
|
|
* Renames a package's directory when it doesn't match the slug.
|
|
*
|
|
* This is commonly required for packages from Git hosts.
|
|
*
|
|
* @param string $source Path of $source.
|
|
* @param string $remote_source Path of $remote_source.
|
|
* @param WP_Upgrader $upgrader An Upgrader object.
|
|
*
|
|
* @return string|WP_Error
|
|
*/
|
|
function rename_source_selection( string $source, string $remote_source, WP_Upgrader $upgrader ) {
|
|
global $wp_filesystem;
|
|
|
|
$did = wp_cache_get( Admin\ACTION_INSTALL_DID );
|
|
|
|
if ( ! $did ) {
|
|
return $source;
|
|
}
|
|
|
|
$metadata = fetch_package_metadata( $did );
|
|
if ( is_wp_error( $metadata ) ) {
|
|
return $metadata;
|
|
}
|
|
|
|
// Sanity check.
|
|
if ( $upgrader->new_plugin_data['Name'] !== $metadata->name ) {
|
|
return $source;
|
|
}
|
|
|
|
if ( str_contains( $source, get_did_hash( $did ) ) && basename( $source ) === $metadata->slug ) {
|
|
return $source;
|
|
}
|
|
|
|
$new_source = trailingslashit( $remote_source ) . $metadata->slug . '-' . get_did_hash( $did );
|
|
|
|
if ( trailingslashit( strtolower( $source ) ) !== trailingslashit( strtolower( $new_source ) ) ) {
|
|
$wp_filesystem->move( $source, $new_source, true );
|
|
}
|
|
|
|
return trailingslashit( $new_source );
|
|
}
|
|
|
|
/**
|
|
* Add FAIR ReleaseDocument data to cache.
|
|
*
|
|
* @param string $did DID.
|
|
* @return void
|
|
*/
|
|
function add_package_to_release_cache( string $did ) : void {
|
|
if ( empty( $did ) ) {
|
|
return;
|
|
}
|
|
$releases = wp_cache_get( RELEASE_PACKAGES_CACHE_KEY ) ?: [];
|
|
$releases[ $did ] = get_latest_release_from_did( $did );
|
|
wp_cache_set( RELEASE_PACKAGES_CACHE_KEY, $releases );
|
|
}
|
|
|
|
/**
|
|
* Maybe add accept header for release asset package binary.
|
|
*
|
|
* ReleaseDocument artifact package content-type will be application/octet-stream.
|
|
* Only for GitHub release assets.
|
|
*
|
|
* @param array $args Array of http args.
|
|
* @param string $url Download URL.
|
|
*
|
|
* @return array
|
|
*/
|
|
function maybe_add_accept_header( $args, $url ) : array {
|
|
$releases = wp_cache_get( RELEASE_PACKAGES_CACHE_KEY ) ?: [];
|
|
|
|
if ( ! str_contains( $url, 'api.github.com' ) ) {
|
|
return $args;
|
|
}
|
|
|
|
foreach ( $releases as $release ) {
|
|
if ( $url === $release->artifacts->package[0]->url ) {
|
|
$content_type = $release->artifacts->package[0]->{'content-type'};
|
|
if ( $content_type === 'application/octet-stream' ) {
|
|
$args = array_merge( $args, [ 'headers' => [ 'Accept' => $content_type ] ] );
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return $args;
|
|
}
|
|
|
|
// phpcs:enable
|