244 lines
6.3 KiB
PHP
244 lines
6.3 KiB
PHP
<?php
|
|
/**
|
|
* GitHub-based plugin updater.
|
|
*
|
|
* Hooks into WordPress's native update system so the plugin receives update
|
|
* notifications sourced from GitHub releases rather than the wp.org directory.
|
|
*
|
|
* @package LogoSoup\Support
|
|
*/
|
|
|
|
namespace LogoSoup\Support;
|
|
|
|
/**
|
|
* Enables automatic plugin updates sourced from GitHub releases.
|
|
*/
|
|
class GitHub_Updater {
|
|
|
|
const GITHUB_USER = 'Vincent-Design-Inc';
|
|
const GITHUB_REPO = 'Logo-Soup';
|
|
const TRANSIENT = 'logo_soup_github_release';
|
|
const TRANSIENT_TTL = 43200; // 12 hours.
|
|
|
|
/**
|
|
* Plugin slug (relative path from plugins dir, e.g. logo-soup/logo-soup.php).
|
|
*
|
|
* @var string
|
|
*/
|
|
private $plugin_slug;
|
|
|
|
/**
|
|
* Absolute path to the main plugin file.
|
|
*
|
|
* @var string
|
|
*/
|
|
private $plugin_file;
|
|
|
|
/**
|
|
* Currently installed version, read from plugin headers.
|
|
*
|
|
* @var string
|
|
*/
|
|
private $current_version;
|
|
|
|
/**
|
|
* Constructor.
|
|
*
|
|
* @param string $plugin_file Absolute path to the main plugin file.
|
|
*/
|
|
public function __construct( $plugin_file ) {
|
|
$this->plugin_file = $plugin_file;
|
|
$this->plugin_slug = plugin_basename( $plugin_file );
|
|
|
|
$headers = get_file_data( $plugin_file, array( 'Version' => 'Version' ) );
|
|
$this->current_version = ! empty( $headers['Version'] ) ? $headers['Version'] : '0.0.0';
|
|
}
|
|
|
|
/**
|
|
* Register WordPress hooks.
|
|
*
|
|
* @return void
|
|
*/
|
|
public function register() {
|
|
add_filter( 'pre_set_site_transient_update_plugins', array( $this, 'inject_update' ) );
|
|
add_filter( 'plugins_api', array( $this, 'plugin_info' ), 20, 3 );
|
|
add_filter( 'upgrader_process_complete', array( $this, 'purge_transient' ), 10, 2 );
|
|
}
|
|
|
|
/**
|
|
* Inject update data into WordPress's update_plugins transient.
|
|
*
|
|
* @param object $transient The update_plugins transient value.
|
|
* @return object
|
|
*/
|
|
public function inject_update( $transient ) {
|
|
if ( empty( $transient->checked ) ) {
|
|
return $transient;
|
|
}
|
|
|
|
$release = $this->fetch_latest_release();
|
|
|
|
if ( ! $release ) {
|
|
return $transient;
|
|
}
|
|
|
|
$remote_version = ltrim( $release['tag_name'], 'v' );
|
|
|
|
if ( version_compare( $this->current_version, $remote_version, '>=' ) ) {
|
|
return $transient;
|
|
}
|
|
|
|
$download_url = $this->get_download_url( $release );
|
|
|
|
if ( ! $download_url ) {
|
|
return $transient;
|
|
}
|
|
|
|
$transient->response[ $this->plugin_slug ] = (object) array(
|
|
'slug' => dirname( $this->plugin_slug ),
|
|
'plugin' => $this->plugin_slug,
|
|
'new_version' => $remote_version,
|
|
'url' => 'https://github.com/' . self::GITHUB_USER . '/' . self::GITHUB_REPO,
|
|
'package' => $download_url,
|
|
);
|
|
|
|
return $transient;
|
|
}
|
|
|
|
/**
|
|
* Supply plugin info for the "View version details" modal in the admin.
|
|
*
|
|
* @param false|object|array $result The result object or array, or false.
|
|
* @param string $action The type of info being requested.
|
|
* @param object $args Plugin API arguments.
|
|
* @return false|object
|
|
*/
|
|
public function plugin_info( $result, $action, $args ) {
|
|
if ( 'plugin_information' !== $action ) {
|
|
return $result;
|
|
}
|
|
|
|
if ( ! isset( $args->slug ) || dirname( $this->plugin_slug ) !== $args->slug ) {
|
|
return $result;
|
|
}
|
|
|
|
$release = $this->fetch_latest_release();
|
|
|
|
if ( ! $release ) {
|
|
return $result;
|
|
}
|
|
|
|
$remote_version = ltrim( $release['tag_name'], 'v' );
|
|
$download_url = $this->get_download_url( $release );
|
|
$changelog = ! empty( $release['body'] ) ? nl2br( esc_html( $release['body'] ) ) : '';
|
|
|
|
return (object) array(
|
|
'name' => 'Logo Soup',
|
|
'slug' => dirname( $this->plugin_slug ),
|
|
'version' => $remote_version,
|
|
'author' => 'Vincent Design Inc',
|
|
'homepage' => 'https://github.com/' . self::GITHUB_USER . '/' . self::GITHUB_REPO,
|
|
'download_link' => $download_url,
|
|
'trunk' => $download_url,
|
|
'requires' => '6.1',
|
|
'requires_php' => '7.4',
|
|
'sections' => array(
|
|
'description' => esc_html__( 'Build balanced logo grids and marquees directly inside the WordPress block editor.', 'logo-soup' ),
|
|
'changelog' => $changelog,
|
|
),
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Delete the cached release transient after a successful plugin update.
|
|
*
|
|
* @param \WP_Upgrader $upgrader Upgrader instance.
|
|
* @param array $hook_extra Extra data about the upgrade.
|
|
* @return void
|
|
*/
|
|
public function purge_transient( $upgrader, $hook_extra ) {
|
|
if (
|
|
! empty( $hook_extra['action'] ) &&
|
|
'update' === $hook_extra['action'] &&
|
|
! empty( $hook_extra['type'] ) &&
|
|
'plugin' === $hook_extra['type'] &&
|
|
! empty( $hook_extra['plugins'] ) &&
|
|
in_array( $this->plugin_slug, (array) $hook_extra['plugins'], true )
|
|
) {
|
|
delete_transient( self::TRANSIENT );
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Fetch the latest GitHub release, with a 12-hour transient cache.
|
|
*
|
|
* @return array|null Decoded release data or null on failure.
|
|
*/
|
|
private function fetch_latest_release() {
|
|
$cached = get_transient( self::TRANSIENT );
|
|
|
|
if ( is_array( $cached ) ) {
|
|
return $cached;
|
|
}
|
|
|
|
$url = sprintf(
|
|
'https://api.github.com/repos/%s/%s/releases/latest',
|
|
self::GITHUB_USER,
|
|
self::GITHUB_REPO
|
|
);
|
|
|
|
$response = wp_remote_get(
|
|
$url,
|
|
array(
|
|
'timeout' => 10,
|
|
'user-agent' => 'WordPress/' . get_bloginfo( 'version' ) . '; ' . get_bloginfo( 'url' ),
|
|
'headers' => array(
|
|
'Accept' => 'application/vnd.github+json',
|
|
),
|
|
)
|
|
);
|
|
|
|
if ( is_wp_error( $response ) ) {
|
|
return null;
|
|
}
|
|
|
|
if ( 200 !== wp_remote_retrieve_response_code( $response ) ) {
|
|
return null;
|
|
}
|
|
|
|
$data = json_decode( wp_remote_retrieve_body( $response ), true );
|
|
|
|
if ( ! is_array( $data ) || empty( $data['tag_name'] ) ) {
|
|
return null;
|
|
}
|
|
|
|
set_transient( self::TRANSIENT, $data, self::TRANSIENT_TTL );
|
|
|
|
return $data;
|
|
}
|
|
|
|
/**
|
|
* Extract the zip download URL from a release.
|
|
*
|
|
* Prefers an attached release asset (.zip); falls back to the auto-generated
|
|
* GitHub source zipball.
|
|
*
|
|
* @param array $release Decoded GitHub release data.
|
|
* @return string|null
|
|
*/
|
|
private function get_download_url( array $release ) {
|
|
if ( ! empty( $release['assets'] ) && is_array( $release['assets'] ) ) {
|
|
foreach ( $release['assets'] as $asset ) {
|
|
if (
|
|
isset( $asset['browser_download_url'] ) &&
|
|
'.zip' === substr( $asset['browser_download_url'], -4 )
|
|
) {
|
|
return $asset['browser_download_url'];
|
|
}
|
|
}
|
|
}
|
|
|
|
return isset( $release['zipball_url'] ) ? $release['zipball_url'] : null;
|
|
}
|
|
}
|