Skip to content

Commit

Permalink
Add possibility to force sync the WPCOM license (#2812)
Browse files Browse the repository at this point in the history
Co-authored-by: Peter Kiss <[email protected]>
Co-authored-by: gikaragia <[email protected]>
  • Loading branch information
3 people authored Apr 26, 2024
1 parent ad9a9dc commit 84006d9
Show file tree
Hide file tree
Showing 5 changed files with 281 additions and 2 deletions.
19 changes: 18 additions & 1 deletion includes/3rd-party/wpcom.php
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,24 @@ function wpjm_display_managed_by_wpcom_notice_for_addon( $product_slug ) {
return;
}

esc_html_e( 'The license for this add-on is automatically managed by WordPress.com.', 'wp-job-manager' );
$helper = WP_Job_Manager_Helper::instance();
$has_license = $helper->has_plugin_license( $product_slug );

?>
<form method="post" class="plugin-license-form">
<?php wp_nonce_field( 'wpjm-manage-license' ); ?>
<input type="hidden" id="<?php echo esc_attr( sanitize_title( $product_slug ) ); ?>_action" name="action" value="flush-wpcom-license"/>
<input type="hidden" id="<?php echo esc_attr( sanitize_title( $product_slug ) ); ?>_plugin" name="product_slug" value="<?php echo esc_attr( $product_slug ); ?>"/>

<?php if ( $has_license ) : ?>
<img src="<?php echo esc_url( JOB_MANAGER_PLUGIN_URL . '/assets/images/icons/checkmark-icon.svg' ); ?>" class='plugin-license-checkmark' aria-hidden='true' alt='<?php esc_attr_e( 'Plugin is activated', 'wp-job-manager' ); ?>'/>
<label class="plugin-license-label"><?php esc_html_e( 'The license for this add-on is automatically managed by WordPress.com.', 'wp-job-manager' ); ?></label>
<?php else : ?>
<label class="plugin-license-label"><?php esc_html_e( 'The license is not activated. Click on the following button to activate it now.', 'wp-job-manager' ); ?></label>
<input type="submit" class="button plugin-license-button" name="submit" value="<?php esc_attr_e( 'Activate License', 'wp-job-manager' ); ?>" />
<?php endif; ?>
</form>
<?php
}

add_action( 'wpjm_manage_license_page_after_license_form', 'wpjm_display_managed_by_wpcom_notice_for_addon' );
31 changes: 31 additions & 0 deletions includes/helper/class-wp-job-manager-helper-api.php
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,37 @@ public function deactivate( $args ) {
return $response;
}

/**
* Flush the WPCOM license for a given plugin.
*
* @param string $plugin_slug The plugin slug to flush the WPCOM license for.
* @param string $activation_url The activation URL for WPJMCOM to request from this blog with the license information.
* @return array|bool Pass through the API response from the licensing server, or false on error.
*/
public function flush_wpcom_license( string $plugin_slug, string $activation_url ) {
// Get domain.
$domain = wp_parse_url( $activation_url, PHP_URL_HOST );
// Call activation service.
$response = $this->request_endpoint(
'/wp-json/wpjmcom-licensing/v1/flush-wpcom-activation',
[
'method' => 'POST',
'body' => wp_json_encode(
[
'plugin_slug' => $plugin_slug,
'domain' => $domain,
'activation_url' => $activation_url,
]
),
'timeout' => 60,
]
);
if ( ! is_array( $response ) || ! array_key_exists( 'success', $response ) ) {
return false;
}
return $response;
}

/**
* Make a license helper API request.
*
Expand Down
54 changes: 54 additions & 0 deletions includes/helper/class-wp-job-manager-helper-nonce.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
<?php
/**
* File containing the class WP_Job_Manager_Helper_Nonce.
*
* @package wp-job-manager
*/

if ( ! defined( 'ABSPATH' ) ) {
exit;
}

/**
* WP_Job_Manager_Helper_Nonce
*/
class WP_Job_Manager_Helper_Nonce {

/**
* The prefix for the custom nonce.
*/
private const PREFIX_NONCE = 'wpjm-custom-nonce-';

/**
* The default expiration time for the custom nonce.
*/
private const DEFAULT_EXPIRE = MINUTE_IN_SECONDS;

/**
* Creates a custom nonce for the given plugin slug.
*
* @param string $action The action name.
*
* @return string The custom nonce.
*/
public function create_custom_nonce( $action ) {
$custom_nonce = wp_generate_password( 15, false );
set_transient( self::PREFIX_NONCE . $action, $custom_nonce, self::DEFAULT_EXPIRE );

return $custom_nonce;
}

/**
* Checks if the given nonce is valid for the given action.
*
* @param string $nonce The nonce to check.
* @param string $action The action name.
*
* @return bool True if the nonce is valid, false otherwise.
*/
public function check_custom_nonce( $nonce, $action ) {
$saved_nonce = get_transient( self::PREFIX_NONCE . $action );

return ! empty( $saved_nonce ) && hash_equals( $saved_nonce, $nonce );
}
}
139 changes: 139 additions & 0 deletions includes/helper/class-wp-job-manager-helper-rest-api.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
<?php
/**
* File containing the class WP_Job_Manager_Helper_REST_API.
*
* @package wp-job-manager
*/

if ( ! defined( 'ABSPATH' ) ) {
exit;
}

/**
* WP_Job_Manager_Helper_REST_API
*/
class WP_Job_Manager_Helper_REST_API {

/**
* The nonce helper to validate the request.
*
* @var WP_Job_Manager_Helper_Nonce
*/
private WP_Job_Manager_Helper_Nonce $nonce;

/**
* The namespace.
*
* @var string
*/
private const NAMESPACE = 'wpjm-internal/v1';

/**
* Rest base for the current object.
*
* @var string
*/
private const REST_BASE = '/licensing';

/**
* Construct the REST API class.
*
* @param WP_Job_Manager_Helper_Nonce $nonce
*/
public function __construct( WP_Job_Manager_Helper_Nonce $nonce ) {
$this->nonce = $nonce;
}

/**
* Initialize the hooks for the REST API.
*
* @return void
*/
public function init() {
add_action( 'rest_api_init', [ $this, 'register_rest_routes' ] );
}

/**
* Register the REST routes related to licensing.
*
* @return void
*/
public function register_rest_routes() {
register_rest_route(
self::NAMESPACE,
self::REST_BASE . '/receive-wpcom-license-key',
[
[
'methods' => \WP_REST_Server::CREATABLE,
'callback' => [ $this, 'receive_wpcom_license_key' ],
'permission_callback' => '__return_true',
'args' => [
'plugin_slug' => [
'type' => 'string',
'required' => true,
],
'license_key' => [
'type' => 'string',
'required' => true,
],
'custom_nonce' => [
'type' => 'string',
'required' => true,
],
],
],
]
);
}


/**
* Receives the license key for the given plugin slug from the WPCOM website.
* This endpoint is expected to be called as the `activation_url` from flush WPCOM license.
*
* @param \WP_REST_Request $request The current request.
*
* @return \WP_REST_Response|\WP_Error
*/
public function receive_wpcom_license_key( $request ) {
$license_key = sanitize_text_field( $request->get_param( 'license_key' ) );
$plugin_slug = sanitize_text_field( $request->get_param( 'plugin_slug' ) );
$custom_nonce = $request->get_param( 'custom_nonce' );
$response = [
'success' => false,
'messages' => [],
];

if ( $this->nonce->check_custom_nonce( $custom_nonce, 'receive-license-' . $plugin_slug ) ) {
$instance = WP_Job_Manager_Helper::instance();
$instance->activate_license( $plugin_slug, $license_key );
$messages = $instance->get_messages( $plugin_slug );
$success = ! empty( $messages );
if ( ! $success ) {
$messages[] = [
'type' => 'error',
'message' => __( 'An error occurred while activating the license.', 'wp-job-manager' ),
];
}
foreach ( $messages as $message ) {
if ( 'error' === $message['type'] ) {
$success = false;
break;
}
}

$response['success'] = $success;
$response['messages'] = $messages;
} else {
$response['messages'][] = [
'type' => 'error',
'message' => __( 'Invalid nonce.', 'wp-job-manager' ),
];
}

// Pass through the API response from the license server.
return rest_ensure_response( $response );
}


}
40 changes: 39 additions & 1 deletion includes/helper/class-wp-job-manager-helper.php
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,13 @@ class WP_Job_Manager_Helper {
*/
private $language_pack_helper;

/**
* Nonce generator.
*
* @var WP_Job_Manager_Helper_Nonce
*/
private WP_Job_Manager_Helper_Nonce $nonce;

/**
* The single instance of the class.
*
Expand Down Expand Up @@ -75,10 +82,15 @@ public function init() {
include_once JOB_MANAGER_PLUGIN_DIR . '/includes/helper/class-wp-job-manager-helper-options.php';
include_once JOB_MANAGER_PLUGIN_DIR . '/includes/helper/class-wp-job-manager-helper-api.php';
include_once JOB_MANAGER_PLUGIN_DIR . '/includes/helper/class-wp-job-manager-helper-language-packs.php';
include_once JOB_MANAGER_PLUGIN_DIR . '/includes/helper/class-wp-job-manager-helper-nonce.php';
include_once JOB_MANAGER_PLUGIN_DIR . '/includes/helper/class-wp-job-manager-helper-renewals.php';
include_once JOB_MANAGER_PLUGIN_DIR . '/includes/helper/class-wp-job-manager-helper-rest-api.php';
include_once JOB_MANAGER_PLUGIN_DIR . '/includes/helper/class-wp-job-manager-site-trust-token.php';

$this->api = WP_Job_Manager_Helper_API::instance();
$this->api = WP_Job_Manager_Helper_API::instance();
$this->nonce = new WP_Job_Manager_Helper_Nonce();

( new WP_Job_Manager_Helper_REST_API( $this->nonce ) )->init();

add_action( 'job_manager_helper_output', [ $this, 'license_output' ] );

Expand Down Expand Up @@ -810,6 +822,9 @@ private function handle_single_request() {
case 'deactivate':
$this->deactivate_license( $product_slug, true );
break;
case 'flush-wpcom-license':
$this->flush_wpcom_license( $product_slug );
break;
}
}

Expand Down Expand Up @@ -954,6 +969,29 @@ private function deactivate_license( $product_slug, $silently = false ) {
);
}

/**
* Flush the WPCOM license for a given product slug, pinging WPJMCOM to sync the license.
*
* @param string $product_slug The slug of the product being processed.
*/
public function flush_wpcom_license( $product_slug ) {
$custom_nonce = $this->nonce->create_custom_nonce( 'receive-license-' . $product_slug );
$activation_url = rest_url( '/wpjm-internal/v1/licensing/receive-wpcom-license-key' );
$activation_url = add_query_arg( 'custom_nonce', $custom_nonce, $activation_url );

$remote = $this->api->flush_wpcom_license( $product_slug, $activation_url );
if (
false !== $remote &&
true === $remote['success']
) {
$this->add_success( $product_slug, __( 'The license has been synced properly.', 'wp-job-manager' ) );
// Clear the options cache to make the product appear in the correct place.
wp_cache_delete( 'alloptions', 'options' );
return;
}
$this->add_error( $product_slug, __( 'There was an error while syncing the license.', 'wp-job-manager' ) );
}

/**
* Add an error message.
*
Expand Down

0 comments on commit 84006d9

Please sign in to comment.