Skip to content

Commit

Permalink
Merge pull request #39 from studiopress/nc/LOC-6166
Browse files Browse the repository at this point in the history
[LOC-6166] Fetch plugin updates from WPE servers
  • Loading branch information
nickcernis authored Oct 10, 2024
2 parents 2aee08b + 656333c commit 44a25a3
Show file tree
Hide file tree
Showing 13 changed files with 450 additions and 28 deletions.
28 changes: 14 additions & 14 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,23 +2,23 @@ version: 2.1

orbs:
php: circleci/[email protected]
wp-svn: studiopress/[email protected]
# wp-svn: studiopress/[email protected]

workflows:
test-deploy:
jobs:
- php/test:
test-command: phpcs
- approval-for-deploy-tested-up-to-bump:
requires:
- php/test
type: approval
filters:
tags:
ignore: /.*/
branches:
only: /^bump-tested-up-to.*/
- wp-svn/deploy-tested-up-to-bump:
context: genesis-svn
requires:
- approval-for-deploy-tested-up-to-bump
# - approval-for-deploy-tested-up-to-bump:
# requires:
# - php/test
# type: approval
# filters:
# tags:
# ignore: /.*/
# branches:
# only: /^bump-tested-up-to.*/
# - wp-svn/deploy-tested-up-to-bump:
# context: genesis-svn
# requires:
# - approval-for-deploy-tested-up-to-bump
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@ Thumbs.db
.svn
node_modules/
vendor/
*.zip
1 change: 1 addition & 0 deletions .nvmrc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
20
11 changes: 11 additions & 0 deletions .svnignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,16 @@
.DS_Store
.git
.gitignore
.gitattributes
.svnignore
.nvmrc
.circleci/
composer.json
composer.lock
Gruntfile.js
package-lock.json
package.json
node_modules
scripts/
vendor/
*.zip
16 changes: 15 additions & 1 deletion genesis-simple-hooks.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

define( 'GENESIS_SIMPLE_HOOKS_DIR', plugin_dir_path( __FILE__ ) );
define( 'GENESIS_SIMPLE_HOOKS_URL', plugins_url( '', __FILE__ ) );
define( 'GENESIS_SIMPLE_HOOKS_VERSION', '2.3.0' );
define( 'GENESIS_SIMPLE_HOOKS_VERSION', '2.3.1' );

require_once GENESIS_SIMPLE_HOOKS_DIR . '/includes/class-genesis-simple-hooks.php';

Expand All @@ -30,3 +30,17 @@ function genesis_simple_hooks() {
* Initialize the object on `plugins_loaded`.
*/
add_action( 'plugins_loaded', array( Genesis_Simple_Hooks(), 'init' ) );

/**
* Initialize checking of plugin updates from WP Engine.
*/
function genesis_simple_hooks_check_for_upgrades() {
$properties = array(
'plugin_slug' => 'genesis-simple-hooks',
'plugin_basename' => plugin_basename( dirname( __FILE__ ) . '/plugin.php' ),
);

require_once __DIR__ . '/includes/class-genesis-simple-hooks-plugin-updater.php';
new Genesis_Simple_Hooks_Plugin_Updater( $properties );
}
add_action( 'admin_init', 'genesis_simple_hooks_check_for_upgrades' );
252 changes: 252 additions & 0 deletions includes/class-genesis-simple-hooks-plugin-updater.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,252 @@
<?php
/**
* The GenesisSimpleHooksPluginUpdater class which can be used to pull plugin updates from a new location.
*
* @package genesis-simple-hooks
*/

// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
exit;
}

/**
* The GenesisBlocksPluginUpdater class which can be used to pull plugin updates from a new location.
*/
class Genesis_Simple_Hooks_Plugin_Updater {
/**
* The URL where the api is located.
*
* @var ApiUrl
*/
private $api_url;

/**
* The amount of time to wait before checking for new updates.
*
* @var CacheTime
*/
private $cache_time;

/**
* These properties are passed in when instantiating to identify the plugin and it's update location.
*
* @var Properties
*/
private $properties;

/**
* Get the class constructed.
*
* @param Properties $properties These properties are passed in when instantiating to identify the plugin and it's update location.
*/
public function __construct( $properties ) {
if (
// This must match the key in "https://wpe-plugin-updates.wpengine.com/plugins.json".
empty( $properties['plugin_slug'] ) ||

// This must be the result of calling plugin_basename( __FILE__ ); in the main plugin root file.
empty( $properties['plugin_basename'] )
) {
// If any of the values we require were not passed, throw a fatal.
// phpcs:ignore
error_log( 'WPE Secure Plugin Updater received a malformed request.' );
return;
}

$this->api_url = 'https://wpe-plugin-updates.wpengine.com/';

$this->cache_time = time() + HOUR_IN_SECONDS * 5;

$this->properties = $this->get_full_plugin_properties( $properties, $this->api_url );

if ( ! $this->properties ) {
return;
}

$this->register();
}

/**
* Get the full plugin properties, including the directory name, version, basename, and add a transient name.
*
* @param Properties $properties These properties are passed in when instantiating to identify the plugin and it's update location.
* @param ApiUrl $api_url The URL where the api is located.
*/
public function get_full_plugin_properties( $properties, $api_url ) {
$plugins = \get_plugins();

// Scan through all plugins installed and find the one which matches this one in question.
foreach ( $plugins as $plugin_basename => $plugin_data ) {
// Match using the passed-in plugin's basename.
if ( $plugin_basename === $properties['plugin_basename'] ) {
// Add the values we need to the properties.
$properties['plugin_dirname'] = dirname( $plugin_basename );
$properties['plugin_version'] = $plugin_data['Version'];
$properties['plugin_update_transient_name'] = 'wpesu-plugin-' . sanitize_title( $properties['plugin_dirname'] );
$properties['plugin_update_transient_exp_name'] = 'wpesu-plugin-' . sanitize_title( $properties['plugin_dirname'] ) . '-expiry';
$properties['plugin_manifest_url'] = trailingslashit( $api_url ) . trailingslashit( $properties['plugin_slug'] ) . 'info.json';

return $properties;
}
}

// No matching plugin was found installed.
return null;
}

/**
* Register hooks.
*
* @return void
*/
public function register() {
add_filter( 'plugins_api', array( $this, 'filter_plugin_update_info' ), 20, 3 );
add_filter( 'pre_set_site_transient_update_plugins', array( $this, 'filter_plugin_update_transient' ) );
}

/**
* Filter the plugin update transient to take over update notifications.
*
* @param object $transient The site_transient_update_plugins transient.
*
* @handles site_transient_update_plugins
* @return object
*/
public function filter_plugin_update_transient( $transient ) {
// No update object exists. Return early.
if ( empty( $transient ) ) {
return $transient;
}

$result = $this->fetch_plugin_info();

if ( false === $result ) {
return $transient;
}

$res = $this->parse_plugin_info( $result );

if ( version_compare( $this->properties['plugin_version'], $result->version, '<' ) ) {
$transient->response[ $res->plugin ] = $res;
$transient->checked[ $res->plugin ] = $result->version;
} else {
$transient->no_update[ $res->plugin ] = $res;
}

return $transient;
}

/**
* Filters the plugin update information.
*
* @param object $res The response to be modified for the plugin in question.
* @param string $action The action in question.
* @param object $args The arguments for the plugin in question.
*
* @handles plugins_api
* @return object
*/
public function filter_plugin_update_info( $res, $action, $args ) {
// Do nothing if this is not about getting plugin information.
if ( 'plugin_information' !== $action ) {
return $res;
}

// Do nothing if it is not our plugin.
if ( $this->properties['plugin_dirname'] !== $args->slug ) {
return $res;
}

$result = $this->fetch_plugin_info();

// Do nothing if we don't get the correct response from the server.
if ( false === $result ) {
return $res;
}

return $this->parse_plugin_info( $result );
}

/**
* Fetches the plugin update object from the WP Product Info API.
*
* @return object|false
*/
private function fetch_plugin_info() {
// Fetch cache first.
$expiry = get_option( $this->properties['plugin_update_transient_exp_name'], 0 );
$response = get_option( $this->properties['plugin_update_transient_name'] );

if ( empty( $expiry ) || time() > $expiry || empty( $response ) ) {
$response = wp_remote_get(
$this->properties['plugin_manifest_url'],
array(
'timeout' => 10,
'headers' => array(
'Accept' => 'application/json',
),
)
);

if (
is_wp_error( $response ) ||
200 !== wp_remote_retrieve_response_code( $response ) ||
empty( wp_remote_retrieve_body( $response ) )
) {
return false;
}

$response = wp_remote_retrieve_body( $response );

// Cache the response.
update_option( $this->properties['plugin_update_transient_exp_name'], $this->cache_time, false );
update_option( $this->properties['plugin_update_transient_name'], $response, false );
}

$decoded_response = json_decode( $response );

if ( json_last_error() !== JSON_ERROR_NONE ) {
return false;
}

return $decoded_response;
}

/**
* Parses the product info response into an object that WordPress would be able to understand.
*
* @param object $response The response object.
*
* @return stdClass
*/
private function parse_plugin_info( $response ) {

global $wp_version;

$res = new stdClass();
$res->name = $response->name;
$res->slug = $response->slug;
$res->version = $response->version;
$res->requires = $response->requires;
$res->download_link = $response->download_link;
$res->trunk = $response->download_link;
$res->new_version = $response->version;
$res->plugin = $this->properties['plugin_basename'];
$res->package = $response->download_link;

// Plugin information modal and core update table use a strict version comparison, which is weird.
// If we're genuinely not compatible with the point release, use our WP tested up to version.
// otherwise use exact same version as WP to avoid false positive.
$res->tested = 1 === version_compare( substr( $wp_version, 0, 3 ), $response->tested )
? $response->tested
: $wp_version;

$res->sections = array(
'description' => $response->sections->description,
'changelog' => $response->sections->changelog,
);

return $res;
}
}
2 changes: 1 addition & 1 deletion includes/class-genesis-simple-hooks.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
* Author: StudioPress
* Author URI: http://www.studiopress.com/
*
* Version: 2.3.0
* Version: 2.3.1
*
* Text Domain: genesis-simple-hooks
* Domain Path: /languages
Expand Down
Loading

0 comments on commit 44a25a3

Please sign in to comment.