Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
91 changes: 91 additions & 0 deletions inc/updater/namespace.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
*/
function bootstrap() {
add_action( 'init', __NAMESPACE__ . '\\run' );
add_filter( 'upgrader_source_selection', __NAMESPACE__ . '\\move_package_during_install', 10, 4 );
}

/**
Expand Down Expand Up @@ -195,3 +196,93 @@ function get_trusted_keys(): array {

return $recoded_keys;
}

/**
* Move a package to the correctly named directory during installation.
*
* @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.
* @return string The correct directory path for installation.
*/
function move_package_during_install( $source, $remote_source, $upgrader, $hook_extra ): string {
global $wp_filesystem;

if ( isset( $hook_extra['action'] ) && $hook_extra['action'] !== 'install' ) {
// Other actions are handled elsewhere.
return $source;
}

if ( ! in_array( $hook_extra['type'], [ 'plugin', 'theme' ], true ) ) {
// This package type is not supported.
return $source;
}

$did = get_did_by_path( $source, $hook_extra['type'] );
if ( is_wp_error( $did ) ) {
// This isn't a valid FAIR package.
return $source;
}

$did_hash = Packages\get_did_hash( $did->get_id() );
if ( str_ends_with( $source, "{$did_hash}/" ) ) {
// The directory name is already correct.
return $source;
}

$new_source = untrailingslashit( $source ) . "-{$did_hash}/";
// Core must be able to find the new source directory.
$wp_filesystem->move( $source, $new_source, true );

return $new_source;
}

/**
* Get a package's DID by its path.
*
* @param string $path The absolute path to the package's directory or main file.
* @param string $type The type of package. Allowed types are 'plugin' or 'theme'.
* @return DID|WP_Error The DID object on success, WP_Error on failure.
*/
function get_did_by_path( $path, $type ) {
global $wp_filesystem;

if ( $type === 'theme' ) {
if ( ! str_ends_with( $path, 'style.css' ) ) {
$path = trailingslashit( $path ) . 'style.css';
}

$id = get_file_data( $path, [ 'id' => 'Theme ID' ] )['id'];
if ( $id ) {
return Packages\parse_did( $id );
}
}

if ( $type === 'plugin' ) {
if ( str_ends_with( $path, '.php' ) ) {
$id = get_file_data( $path, [ 'id' => 'Plugin ID' ] )['id'];
return Packages\parse_did( $id );
}

$files = $wp_filesystem->dirlist( $path ) ?: false;
if ( ! $files ) {
// Finding a DID is impossible.
return new WP_Error( 'fair.packages.dirlist_failed', __( "The package's file list could not be retrieved.", 'fair' ) );
}

foreach ( $files as $filename => $data ) {
if ( $data['type'] !== 'f' || ! str_ends_with( $filename, '.php' ) ) {
continue;
}

$filepath = trailingslashit( $path ) . $filename;
$id = get_file_data( $filepath, [ 'id' => 'Plugin ID' ] )['id'];
if ( $id ) {
return Packages\parse_did( $id );
}
}
}

return new WP_Error( 'fair.packages.none_found', __( 'No FAIR packages were found.', 'fair' ) );
}