Implement API for Premium plugin installation and activation
[MAILPOET-2431]
This commit is contained in:
committed by
Jack Kitterhing
parent
757d09ac19
commit
4e75bcd296
@ -7,15 +7,6 @@ import MssMessages from 'settings/premium_tab/messages/mss_messages.jsx';
|
||||
import { PremiumStatus, PremiumMessages } from 'settings/premium_tab/messages/premium_messages.jsx';
|
||||
import { PremiumInstallationStatus } from 'settings/premium_tab/messages/premium_installation_messages.jsx';
|
||||
|
||||
const request = async (url) => {
|
||||
try {
|
||||
const response = await fetch(url);
|
||||
return response.ok;
|
||||
} catch (error) {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
const requestServicesApi = async (key, action) => MailPoet.Ajax.post({
|
||||
api_version: window.mailpoet_api_version,
|
||||
endpoint: 'services',
|
||||
@ -23,6 +14,12 @@ const requestServicesApi = async (key, action) => MailPoet.Ajax.post({
|
||||
data: { key },
|
||||
});
|
||||
|
||||
const requestPremiumApi = async (action) => MailPoet.Ajax.post({
|
||||
api_version: window.mailpoet_api_version,
|
||||
endpoint: 'premium',
|
||||
action,
|
||||
});
|
||||
|
||||
const activateMss = async (key) => MailPoet.Ajax.post({
|
||||
api_version: window.mailpoet_api_version,
|
||||
endpoint: 'settings',
|
||||
@ -47,8 +44,6 @@ const PremiumTab = (props) => {
|
||||
const [mssKeyValid, setMssKeyValid] = useState(key ? props.mssKeyValid : null);
|
||||
const [mssKeyMessage, setMssKeyMessage] = useState(null);
|
||||
|
||||
let premiumActivateUrl = props.premiumActivateUrl;
|
||||
|
||||
// key is considered valid if either Premium or MSS check passes
|
||||
const keyValid = useMemo(() => {
|
||||
if (premiumStatus > PremiumStatus.KEY_INVALID || mssKeyValid) {
|
||||
@ -64,7 +59,9 @@ const PremiumTab = (props) => {
|
||||
const errorStatus = isAfterInstall ? status.INSTALL_ACTIVATING_ERROR : status.ACTIVATE_ERROR;
|
||||
|
||||
setPremiumInstallationStatus(activateStatus);
|
||||
if (!await request(premiumActivateUrl)) {
|
||||
try {
|
||||
await requestPremiumApi('activatePlugin');
|
||||
} catch (error) {
|
||||
setPremiumInstallationStatus(errorStatus);
|
||||
return;
|
||||
}
|
||||
@ -73,15 +70,8 @@ const PremiumTab = (props) => {
|
||||
|
||||
const installPremiumPlugin = async () => {
|
||||
setPremiumInstallationStatus(PremiumInstallationStatus.INSTALL_INSTALLING);
|
||||
if (!await request(props.premiumInstallUrl)) {
|
||||
setPremiumInstallationStatus(PremiumInstallationStatus.INSTALL_INSTALLING_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
// refetch 'plugin_activate_url' since it's only set after installation
|
||||
try {
|
||||
const response = await requestServicesApi(key, 'checkPremiumKey');
|
||||
premiumActivateUrl = response.meta.premium_activate_url;
|
||||
await requestPremiumApi('installPlugin');
|
||||
} catch (error) {
|
||||
setPremiumInstallationStatus(PremiumInstallationStatus.INSTALL_INSTALLING_ERROR);
|
||||
return;
|
||||
@ -228,13 +218,10 @@ PremiumTab.propTypes = {
|
||||
premiumStatus: PropTypes.number.isRequired,
|
||||
mssKeyValid: PropTypes.bool.isRequired,
|
||||
premiumPluginActive: PropTypes.bool.isRequired,
|
||||
premiumInstallUrl: PropTypes.string.isRequired,
|
||||
premiumActivateUrl: PropTypes.string,
|
||||
};
|
||||
|
||||
PremiumTab.defaultProps = {
|
||||
activationKey: null,
|
||||
premiumActivateUrl: null,
|
||||
};
|
||||
|
||||
const container = document.getElementById('settings-premium-tab');
|
||||
@ -260,8 +247,6 @@ if (container) {
|
||||
premiumStatus={getPremiumStatus()}
|
||||
mssKeyValid={window.mailpoet_mss_key_valid}
|
||||
premiumPluginActive={!!window.mailpoet_premium_version}
|
||||
premiumInstallUrl={window.mailpoet_premium_install_url}
|
||||
premiumActivateUrl={window.mailpoet_premium_activate_url || null}
|
||||
/>,
|
||||
container
|
||||
);
|
||||
|
74
lib/API/JSON/v1/Premium.php
Normal file
74
lib/API/JSON/v1/Premium.php
Normal file
@ -0,0 +1,74 @@
|
||||
<?php
|
||||
|
||||
namespace MailPoet\API\JSON\v1;
|
||||
|
||||
use MailPoet\API\JSON\Endpoint as APIEndpoint;
|
||||
use MailPoet\API\JSON\Error as APIError;
|
||||
use MailPoet\Config\AccessControl;
|
||||
use MailPoet\Config\ServicesChecker;
|
||||
use MailPoet\WP\Functions as WPFunctions;
|
||||
use WP_Error;
|
||||
|
||||
class Premium extends APIEndpoint {
|
||||
const PREMIUM_PLUGIN_SLUG = 'mailpoet-premium';
|
||||
const PREMIUM_PLUGIN_PATH = 'mailpoet-premium/mailpoet-premium.php';
|
||||
|
||||
public $permissions = [
|
||||
'global' => AccessControl::PERMISSION_MANAGE_SETTINGS,
|
||||
];
|
||||
|
||||
/** @var ServicesChecker */
|
||||
private $services_checker;
|
||||
|
||||
/** @var WPFunctions */
|
||||
private $wp;
|
||||
|
||||
public function __construct(
|
||||
ServicesChecker $services_checker,
|
||||
WPFunctions $wp
|
||||
) {
|
||||
$this->services_checker = $services_checker;
|
||||
$this->wp = $wp;
|
||||
}
|
||||
|
||||
public function installPlugin() {
|
||||
$premium_key_valid = $this->services_checker->isPremiumKeyValid(false);
|
||||
if (!$premium_key_valid) {
|
||||
return $this->error($this->wp->__('Premium key is not valid.', 'mailpoet'));
|
||||
}
|
||||
|
||||
$plugin_info = $this->wp->pluginsApi('plugin_information', [
|
||||
'slug' => self::PREMIUM_PLUGIN_SLUG,
|
||||
]);
|
||||
|
||||
if (!$plugin_info || $plugin_info instanceof WP_Error) {
|
||||
return $this->error($this->wp->__('Error when installing MailPoet Premium plugin.', 'mailpoet'));
|
||||
}
|
||||
|
||||
$plugin_info = (array)$plugin_info;
|
||||
$result = $this->wp->installPlugin($plugin_info['download_link']);
|
||||
if ($result !== true) {
|
||||
return $this->error($this->wp->__('Error when installing MailPoet Premium plugin.', 'mailpoet'));
|
||||
}
|
||||
return $this->successResponse();
|
||||
}
|
||||
|
||||
public function activatePlugin() {
|
||||
$premium_key_valid = $this->services_checker->isPremiumKeyValid(false);
|
||||
if (!$premium_key_valid) {
|
||||
return $this->error($this->wp->__('Premium key is not valid.', 'mailpoet'));
|
||||
}
|
||||
|
||||
$result = $this->wp->activatePlugin(self::PREMIUM_PLUGIN_PATH);
|
||||
if ($result !== null) {
|
||||
return $this->error($this->wp->__('Error when activating MailPoet Premium plugin.', 'mailpoet'));
|
||||
}
|
||||
return $this->successResponse();
|
||||
}
|
||||
|
||||
private function error($message) {
|
||||
return $this->badRequest([
|
||||
APIError::BAD_REQUEST => $message,
|
||||
]);
|
||||
}
|
||||
}
|
@ -76,6 +76,7 @@ class ContainerConfigurator implements IContainerConfigurator {
|
||||
$container->autowire(\MailPoet\API\JSON\v1\Newsletters::class)->setPublic(true);
|
||||
$container->autowire(\MailPoet\API\JSON\v1\NewsletterLinks::class)->setPublic(true);
|
||||
$container->autowire(\MailPoet\API\JSON\v1\NewsletterTemplates::class)->setPublic(true);
|
||||
$container->autowire(\MailPoet\API\JSON\v1\Premium::class)->setPublic(true);
|
||||
$container->autowire(\MailPoet\API\JSON\v1\Segments::class)->setPublic(true);
|
||||
$container->autowire(\MailPoet\API\JSON\v1\SendingQueue::class)->setPublic(true);
|
||||
$container->autowire(\MailPoet\API\JSON\v1\Services::class)->setPublic(true);
|
||||
|
@ -2,6 +2,8 @@
|
||||
|
||||
namespace MailPoet\WP;
|
||||
|
||||
use Plugin_Upgrader;
|
||||
use WP_Ajax_Upgrader_Skin;
|
||||
use WP_Error;
|
||||
|
||||
class Functions {
|
||||
@ -562,6 +564,40 @@ class Functions {
|
||||
return wpautop($pee, $br);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $action
|
||||
* @param array|object $args
|
||||
* @return object|array|WP_Error
|
||||
*/
|
||||
public function pluginsApi($action, $args = []) {
|
||||
require_once ABSPATH . 'wp-admin/includes/plugin-install.php';
|
||||
return plugins_api($action, $args);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $package
|
||||
* @param array $args
|
||||
* @return bool|WP_Error
|
||||
*/
|
||||
public function installPlugin($package, $args = []) {
|
||||
require_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php';
|
||||
require_once ABSPATH . 'wp-admin/includes/class-plugin-upgrader.php';
|
||||
require_once ABSPATH . 'wp-admin/includes/class-wp-ajax-upgrader-skin.php';
|
||||
$upgrader = new Plugin_Upgrader(new WP_Ajax_Upgrader_Skin());
|
||||
return $upgrader->install($package, $args);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $plugin
|
||||
* @param string $redirect
|
||||
* @param bool $network_wide
|
||||
* @param bool $silent
|
||||
* @return WP_Error|null
|
||||
*/
|
||||
public function activatePlugin($plugin, $redirect = '', $network_wide = false, $silent = false) {
|
||||
return activate_plugin($plugin, $redirect, $network_wide, $silent);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $host
|
||||
* @return array|bool
|
||||
|
@ -16,3 +16,38 @@ if (!class_exists(WooCommerce::class)) {
|
||||
}
|
||||
|
||||
require_once __DIR__ . '/function-stubs.php';
|
||||
|
||||
// methods & classes for Premium plugin installation are required
|
||||
// only when needed so we need to let PHPStan know about them
|
||||
if (!function_exists('plugins_api')) {
|
||||
/**
|
||||
* @param string $action
|
||||
* @param array|object $args
|
||||
* @return object|array|WP_Error
|
||||
*/
|
||||
function plugins_api($action, $args) {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
if (!class_exists(WP_Ajax_Upgrader_Skin::class)) {
|
||||
// phpcs:ignore
|
||||
class WP_Ajax_Upgrader_Skin {}
|
||||
}
|
||||
|
||||
if (!class_exists(Plugin_Upgrader::class)) {
|
||||
// phpcs:ignore
|
||||
class Plugin_Upgrader {
|
||||
public function __construct($skin = null) {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $package
|
||||
* @param array $args
|
||||
* @return bool|WP_Error
|
||||
*/
|
||||
public function install($package, $args = []) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
134
tests/unit/API/JSON/PremiumTest.php
Normal file
134
tests/unit/API/JSON/PremiumTest.php
Normal file
@ -0,0 +1,134 @@
|
||||
<?php
|
||||
|
||||
namespace MailPoet\Test\API\JSON;
|
||||
|
||||
use Codeception\Stub\Expected;
|
||||
use MailPoet\API\JSON\ErrorResponse;
|
||||
use MailPoet\API\JSON\SuccessResponse;
|
||||
use MailPoet\API\JSON\v1\Premium;
|
||||
use MailPoet\Config\ServicesChecker;
|
||||
use MailPoet\WP\Functions as WPFunctions;
|
||||
|
||||
class PremiumTest extends \MailPoetUnitTest {
|
||||
public function testItInstallsPlugin() {
|
||||
$services_checker = $this->makeEmpty(ServicesChecker::class, [
|
||||
'isPremiumKeyValid' => Expected::once(true),
|
||||
]);
|
||||
|
||||
$wp = $this->make(WPFunctions::class, [
|
||||
'pluginsApi' => Expected::once([
|
||||
'download_link' => 'https://some-download-link',
|
||||
]),
|
||||
'installPlugin' => Expected::once(true),
|
||||
]);
|
||||
|
||||
$premium = new Premium($services_checker, $wp);
|
||||
$response = $premium->installPlugin();
|
||||
expect($response)->isInstanceOf(SuccessResponse::class);
|
||||
}
|
||||
|
||||
public function testInstallationFailsWhenKeyInvalid() {
|
||||
$services_checker = $this->makeEmpty(ServicesChecker::class, [
|
||||
'isPremiumKeyValid' => Expected::once(false),
|
||||
]);
|
||||
|
||||
$wp = $this->make(WPFunctions::class, [
|
||||
'pluginsApi' => Expected::never(),
|
||||
'installPlugin' => Expected::never(),
|
||||
]);
|
||||
|
||||
$premium = new Premium($services_checker, $wp);
|
||||
$response = $premium->installPlugin();
|
||||
expect($response)->isInstanceOf(ErrorResponse::class);
|
||||
expect($response->getData()['errors'][0])->same([
|
||||
'error' => 'bad_request',
|
||||
'message' => 'Premium key is not valid.',
|
||||
]);
|
||||
}
|
||||
|
||||
public function testInstallationFailsWhenNoPluginInfo() {
|
||||
$services_checker = $this->makeEmpty(ServicesChecker::class, [
|
||||
'isPremiumKeyValid' => Expected::once(true),
|
||||
]);
|
||||
|
||||
$wp = $this->make(WPFunctions::class, [
|
||||
'pluginsApi' => Expected::once(null),
|
||||
'installPlugin' => Expected::never(),
|
||||
]);
|
||||
|
||||
$premium = new Premium($services_checker, $wp);
|
||||
$response = $premium->installPlugin();
|
||||
expect($response)->isInstanceOf(ErrorResponse::class);
|
||||
expect($response->getData()['errors'][0])->same([
|
||||
'error' => 'bad_request',
|
||||
'message' => 'Error when installing MailPoet Premium plugin.',
|
||||
]);
|
||||
}
|
||||
|
||||
public function testInstallationFailsOnError() {
|
||||
$services_checker = $this->makeEmpty(ServicesChecker::class, [
|
||||
'isPremiumKeyValid' => Expected::once(true),
|
||||
]);
|
||||
|
||||
$wp = $this->make(WPFunctions::class, [
|
||||
'pluginsApi' => Expected::once([
|
||||
'download_link' => 'https://some-download-link',
|
||||
]),
|
||||
'installPlugin' => Expected::once(false),
|
||||
]);
|
||||
|
||||
$premium = new Premium($services_checker, $wp);
|
||||
$response = $premium->installPlugin();
|
||||
expect($response)->isInstanceOf(ErrorResponse::class);
|
||||
expect($response->getData()['errors'][0])->same([
|
||||
'error' => 'bad_request',
|
||||
'message' => 'Error when installing MailPoet Premium plugin.',
|
||||
]);
|
||||
}
|
||||
|
||||
public function testItActivatesPlugin() {
|
||||
$services_checker = $this->makeEmpty(ServicesChecker::class, [
|
||||
'isPremiumKeyValid' => Expected::once(true),
|
||||
]);
|
||||
|
||||
$wp = $this->make(WPFunctions::class, [
|
||||
'activatePlugin' => Expected::once(null),
|
||||
]);
|
||||
|
||||
$premium = new Premium($services_checker, $wp);
|
||||
$response = $premium->activatePlugin();
|
||||
expect($response)->isInstanceOf(SuccessResponse::class);
|
||||
}
|
||||
|
||||
public function testActivationFailsWhenKeyInvalid() {
|
||||
$services_checker = $this->makeEmpty(ServicesChecker::class, [
|
||||
'isPremiumKeyValid' => Expected::once(false),
|
||||
]);
|
||||
|
||||
$premium = new Premium($services_checker, new WPFunctions());
|
||||
$response = $premium->activatePlugin();
|
||||
expect($response)->isInstanceOf(ErrorResponse::class);
|
||||
expect($response->getData()['errors'][0])->same([
|
||||
'error' => 'bad_request',
|
||||
'message' => 'Premium key is not valid.',
|
||||
]);
|
||||
}
|
||||
|
||||
public function testActivationFailsOnError() {
|
||||
$services_checker = $this->makeEmpty(ServicesChecker::class, [
|
||||
'isPremiumKeyValid' => Expected::once(true),
|
||||
]);
|
||||
|
||||
$wp = $this->make(WPFunctions::class, [
|
||||
'activatePlugin' => Expected::once('error'),
|
||||
]);
|
||||
|
||||
$premium = new Premium($services_checker, $wp);
|
||||
$response = $premium->activatePlugin();
|
||||
expect($response)->isInstanceOf(ErrorResponse::class);
|
||||
expect($response->getData()['errors'][0])->same([
|
||||
'error' => 'bad_request',
|
||||
'message' => 'Error when activating MailPoet Premium plugin.',
|
||||
]);
|
||||
}
|
||||
}
|
@ -4,8 +4,6 @@
|
||||
var mailpoet_premium_key_valid = <%= json_encode(premium_key_valid) %>;
|
||||
var mailpoet_premium_plugin_installed = <%= json_encode(premium_plugin_installed) %>;
|
||||
var mailpoet_mss_key_valid = <%= json_encode(mss_key_valid) %>;
|
||||
var mailpoet_premium_install_url = <%= json_encode(premium_install_url) %>;
|
||||
var mailpoet_premium_activate_url = <%= json_encode(premium_activate_url) %>;
|
||||
<% endautoescape %>
|
||||
</script>
|
||||
|
||||
|
Reference in New Issue
Block a user