Files
wordpress-preseed/wp-content/plugins/github-updater/src/GitHub_Updater/Rest_Update.php
2019-08-31 00:48:20 +02:00

315 lines
8.1 KiB
PHP

<?php
/**
* GitHub Updater
*
* @author Andy Fragen, Mikael Lindqvist
* @license GPL-2.0+
* @link https://github.com/afragen/github-updater
* @package github-updater
*/
namespace Fragen\GitHub_Updater;
use Fragen\Singleton;
/*
* Exit if called directly.
*/
if ( ! defined( 'WPINC' ) ) {
die;
}
require_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php';
/**
* Class Rest_Update
*
* Updates a single plugin or theme, in a way suitable for rest requests.
* This class inherits from Base in order to be able to call the
* set_defaults function.
*/
class Rest_Update extends Base {
/**
* Holds REST Upgrader Skin.
*
* @var Rest_Upgrader_Skin $upgrader_skin
*/
protected $upgrader_skin;
/**
* Constructor.
*/
public function __construct() {
parent::__construct();
$this->load_options();
$this->upgrader_skin = new Rest_Upgrader_Skin();
}
/**
* Update plugin.
*
* @param string $plugin_slug
* @param string $tag
*
* @throws \UnexpectedValueException Plugin not found or not updatable.
*/
public function update_plugin( $plugin_slug, $tag = 'master' ) {
$plugin = null;
$is_plugin_active = false;
foreach ( (array) Singleton::get_instance( 'Plugin', $this )->get_plugin_configs() as $config_entry ) {
if ( $config_entry->slug === $plugin_slug ) {
$plugin = $config_entry;
break;
}
}
if ( ! $plugin ) {
throw new \UnexpectedValueException( 'Plugin not found or not updatable with GitHub Updater: ' . $plugin_slug );
}
if ( is_plugin_active( $plugin->file ) ) {
$is_plugin_active = true;
}
$this->get_remote_repo_meta( $plugin );
$repo_api = Singleton::get_instance( 'API', $this )->get_repo_api( $plugin->git, $plugin );
$update = [
'slug' => $plugin->slug,
'plugin' => $plugin->file,
'new_version' => null,
'url' => $plugin->uri,
'package' => $repo_api->construct_download_link( $tag ),
];
add_filter(
'site_transient_update_plugins',
function ( $current ) use ( $plugin, $update ) {
$current->response[ $plugin->file ] = (object) $update;
return $current;
}
);
$upgrader = new \Plugin_Upgrader( $this->upgrader_skin );
$upgrader->upgrade( $plugin->file );
if ( $is_plugin_active ) {
$activate = is_multisite() ? activate_plugin( $plugin->file, null, true ) : activate_plugin( $plugin->file );
if ( ! $activate ) {
$this->upgrader_skin->messages[] = 'Plugin reactivated successfully.';
}
}
}
/**
* Update a single theme.
*
* @param string $theme_slug
* @param string $tag
*
* @throws \UnexpectedValueException Theme not found or not updatable.
*/
public function update_theme( $theme_slug, $tag = 'master' ) {
$theme = null;
foreach ( (array) Singleton::get_instance( 'Theme', $this )->get_theme_configs() as $config_entry ) {
if ( $config_entry->slug === $theme_slug ) {
$theme = $config_entry;
break;
}
}
if ( ! $theme ) {
throw new \UnexpectedValueException( 'Theme not found or not updatable with GitHub Updater: ' . $theme_slug );
}
$this->get_remote_repo_meta( $theme );
$repo_api = Singleton::get_instance( 'API', $this )->get_repo_api( $theme->git, $theme );
$update = [
'theme' => $theme->slug,
'new_version' => null,
'url' => $theme->uri,
'package' => $repo_api->construct_download_link( $tag ),
];
add_filter(
'site_transient_update_themes',
function ( $current ) use ( $theme, $update ) {
$current->response[ $theme->slug ] = $update;
return $current;
}
);
$upgrader = new \Theme_Upgrader( $this->upgrader_skin );
$upgrader->upgrade( $theme->slug );
}
/**
* Is there an error?
*/
public function is_error() {
return $this->upgrader_skin->error;
}
/**
* Get messages during update.
*/
public function get_messages() {
return $this->upgrader_skin->messages;
}
/**
* Process request.
*
* Relies on data in $_REQUEST, prints out json and exits.
* If the request came through a webhook, and if the branch in the
* webhook matches the branch specified by the url, use the latest
* update available as specified in the webhook payload.
*
* @throws \UnexpectedValueException Under multiple bad or missing params.
*/
public function process_request() {
$start = microtime( true );
try {
if ( ! isset( $_REQUEST['key'] ) ||
get_site_option( 'github_updater_api_key' ) !== $_REQUEST['key']
) {
throw new \UnexpectedValueException( 'Bad API key.' );
}
/**
* Allow access into the REST Update process.
*
* @since 7.6.0
* @access public
*/
do_action( 'github_updater_pre_rest_process_request' );
$tag = 'master';
if ( isset( $_REQUEST['tag'] ) ) {
$tag = $_REQUEST['tag'];
} elseif ( isset( $_REQUEST['committish'] ) ) {
$tag = $_REQUEST['committish'];
}
$this->get_webhook_source();
$current_branch = $this->get_local_branch();
$override = isset( $_REQUEST['override'] );
if ( $tag !== $current_branch && ! $override ) {
throw new \UnexpectedValueException( 'Webhook tag and current branch are not matching. Consider using `override` query arg.' );
}
if ( isset( $_REQUEST['plugin'] ) ) {
$this->update_plugin( $_REQUEST['plugin'], $tag );
} elseif ( isset( $_REQUEST['theme'] ) ) {
$this->update_theme( $_REQUEST['theme'], $tag );
} else {
throw new \UnexpectedValueException( 'No plugin or theme specified for update.' );
}
} catch ( \Exception $e ) {
$http_response = [
'success' => false,
'messages' => $e->getMessage(),
'webhook' => $_GET,
'elapsed_time' => round( ( microtime( true ) - $start ) * 1000, 2 ) . ' ms',
];
$this->log_exit( $http_response, 417 );
}
$response = [
'success' => true,
'messages' => $this->get_messages(),
'webhook' => $_GET,
'elapsed_time' => round( ( microtime( true ) - $start ) * 1000, 2 ) . ' ms',
];
if ( $this->is_error() ) {
$response['success'] = false;
$this->log_exit( $response, 417 );
}
$this->log_exit( $response, 200 );
}
/**
* Returns the current branch of the local repository referenced in the webhook.
*
* @return string $current_branch Default return is 'master'.
*/
private function get_local_branch() {
$repo = false;
if ( isset( $_REQUEST['plugin'] ) ) {
$repos = Singleton::get_instance( 'Plugin', $this )->get_plugin_configs();
$repo = isset( $repos[ $_REQUEST['plugin'] ] ) ? $repos[ $_REQUEST['plugin'] ] : false;
}
if ( isset( $_REQUEST['theme'] ) ) {
$repos = Singleton::get_instance( 'Theme', $this )->get_theme_configs();
$repo = isset( $repos[ $_REQUEST['theme'] ] ) ? $repos[ $_REQUEST['theme'] ] : false;
}
$current_branch = $repo ?
Singleton::get_instance( 'Branch', $this )->get_current_branch( $repo ) :
'master';
return $current_branch;
}
/**
* Sets the source of the webhook to $_GET variable.
*/
private function get_webhook_source() {
switch ( $_SERVER ) {
case isset( $_SERVER['HTTP_X_GITHUB_EVENT'] ):
$webhook_source = 'GitHub webhook';
break;
case isset( $_SERVER['HTTP_X_EVENT_KEY'] ):
$webhook_source = 'Bitbucket webhook';
break;
case isset( $_SERVER['HTTP_X_GITLAB_EVENT'] ):
$webhook_source = 'GitLab webhook';
break;
case isset( $_SERVER['HTTP_X_GITEA_EVENT'] ):
$webhook_source = 'Gitea webhook';
break;
default:
$webhook_source = 'browser';
break;
}
$_GET['webhook_source'] = $webhook_source;
}
/**
* Append $response to debug.log and wp_die().
*
* @param array $response
* @param int $code
*
* 128 == JSON_PRETTY_PRINT
* 64 == JSON_UNESCAPED_SLASHES
*/
private function log_exit( $response, $code ) {
$json_encode_flags = 128 | 64;
error_log( json_encode( $response, $json_encode_flags ) );
/**
* Action hook after processing REST process.
*
* @since 8.6.0
*
* @param array $response
* @param int $code HTTP response.
*/
do_action( 'github_updater_post_rest_process_request', $response, $code );
unset( $response['success'] );
if ( 200 === $code ) {
wp_die( wp_send_json_success( $response, $code ) );
} else {
wp_die( wp_send_json_error( $response, $code ) );
}
}
}