* Update translation text domain * Escape everything that should be escaped. * Add nonce checks where needed. * Sanitize all inputs. * Apply Code style changes across the codebase. * Correct many deprecation notices. * Optimize load order of many filters. * Add Proper Build script * Use emojii flags * Fix i18n deprecation notice for translating too early * Put all scripts in footer and load async
434 lines
9.2 KiB
PHP
434 lines
9.2 KiB
PHP
<?php
|
|
/**
|
|
* Membership Manager
|
|
*
|
|
* Handles processes related to memberships.
|
|
*
|
|
* @package WP_Ultimo
|
|
* @subpackage Managers/Membership_Manager
|
|
* @since 2.0.0
|
|
*/
|
|
|
|
namespace WP_Ultimo\Managers;
|
|
|
|
use Psr\Log\LogLevel;
|
|
use WP_Ultimo\Managers\Base_Manager;
|
|
use WP_Ultimo\Database\Memberships\Membership_Status;
|
|
|
|
// Exit if accessed directly
|
|
defined('ABSPATH') || exit;
|
|
|
|
/**
|
|
* Handles processes related to memberships.
|
|
*
|
|
* @since 2.0.0
|
|
*/
|
|
class Membership_Manager extends Base_Manager {
|
|
|
|
use \WP_Ultimo\Apis\Rest_Api;
|
|
use \WP_Ultimo\Apis\WP_CLI;
|
|
use \WP_Ultimo\Traits\Singleton;
|
|
|
|
/**
|
|
* The manager slug.
|
|
*
|
|
* @since 2.0.0
|
|
* @var string
|
|
*/
|
|
protected $slug = 'membership';
|
|
|
|
/**
|
|
* The model class associated to this manager.
|
|
*
|
|
* @since 2.0.0
|
|
* @var string
|
|
*/
|
|
protected $model_class = \WP_Ultimo\Models\Membership::class;
|
|
|
|
/**
|
|
* Instantiate the necessary hooks.
|
|
*
|
|
* @since 2.0.0
|
|
* @return void
|
|
*/
|
|
public function init(): void {
|
|
|
|
$this->enable_rest_api();
|
|
|
|
$this->enable_wp_cli();
|
|
|
|
add_action(
|
|
'init',
|
|
function () {
|
|
Event_Manager::register_model_events('membership', __('Membership', 'wp-multisite-waas'), ['created', 'updated']);
|
|
}
|
|
);
|
|
|
|
add_action('wu_async_transfer_membership', [$this, 'async_transfer_membership'], 10, 2);
|
|
|
|
add_action('wu_async_delete_membership', [$this, 'async_delete_membership'], 10);
|
|
|
|
/*
|
|
* Transitions
|
|
*/
|
|
add_action('wu_transition_membership_status', [$this, 'mark_cancelled_date'], 10, 3);
|
|
|
|
add_action('wu_transition_membership_status', [$this, 'transition_membership_status'], 10, 3);
|
|
|
|
/*
|
|
* Deal with delayed/schedule swaps
|
|
*/
|
|
add_action('wu_async_membership_swap', [$this, 'async_membership_swap'], 10);
|
|
|
|
/*
|
|
* Deal with pending sites creation
|
|
*/
|
|
add_action('wp_ajax_wu_publish_pending_site', [$this, 'publish_pending_site']);
|
|
|
|
add_action('wp_ajax_wu_check_pending_site_created', [$this, 'check_pending_site_created']);
|
|
|
|
add_action('wu_async_publish_pending_site', [$this, 'async_publish_pending_site'], 10);
|
|
}
|
|
|
|
/**
|
|
* Processes a delayed site publish action.
|
|
*
|
|
* @since 2.0.11
|
|
*/
|
|
public function publish_pending_site(): void {
|
|
|
|
check_ajax_referer('wu_publish_pending_site');
|
|
|
|
ignore_user_abort(true);
|
|
|
|
// Don't make the request block till we finish, if possible.
|
|
if ( function_exists('fastcgi_finish_request') && version_compare(phpversion(), '7.0.16', '>=') ) {
|
|
wp_send_json(['status' => 'creating-site']);
|
|
|
|
fastcgi_finish_request();
|
|
}
|
|
|
|
$membership_id = wu_request('membership_id');
|
|
|
|
$this->async_publish_pending_site($membership_id);
|
|
|
|
exit; // Just exit the request
|
|
}
|
|
|
|
/**
|
|
* Processes a delayed site publish action.
|
|
*
|
|
* @since 2.0.0
|
|
*
|
|
* @param int $membership_id The membership id.
|
|
* @return bool|\WP_Error
|
|
*/
|
|
public function async_publish_pending_site($membership_id) {
|
|
|
|
$membership = wu_get_membership($membership_id);
|
|
|
|
if ( ! $membership) {
|
|
return new \WP_Error('error', __('An unexpected error happened.', 'wp-multisite-waas'));
|
|
}
|
|
|
|
$status = $membership->publish_pending_site();
|
|
|
|
if (is_wp_error($status)) {
|
|
wu_log_add('site-errors', $status, LogLevel::ERROR);
|
|
}
|
|
|
|
return $status;
|
|
}
|
|
|
|
/**
|
|
* Processes a delayed site publish action.
|
|
*
|
|
* @since 2.0.11
|
|
*/
|
|
public function check_pending_site_created() {
|
|
|
|
$membership_id = wu_request('membership_hash');
|
|
|
|
$membership = wu_get_membership_by_hash($membership_id);
|
|
|
|
if ( ! $membership) {
|
|
return new \WP_Error('error', __('An unexpected error happened.', 'wp-multisite-waas'));
|
|
}
|
|
|
|
$pending_site = $membership->get_pending_site();
|
|
|
|
if ( ! $pending_site) {
|
|
/**
|
|
* We do not have a pending site, so we can assume the site was created.
|
|
*/
|
|
wp_send_json(['publish_status' => 'completed']);
|
|
|
|
exit;
|
|
}
|
|
|
|
wp_send_json(['publish_status' => $pending_site->is_publishing() ? 'running' : 'stopped']);
|
|
|
|
exit;
|
|
}
|
|
|
|
/**
|
|
* Processes a membership swap.
|
|
*
|
|
* @since 2.0.0
|
|
*
|
|
* @param int $membership_id The membership id.
|
|
* @return bool|\WP_Error
|
|
*/
|
|
public function async_membership_swap($membership_id) {
|
|
|
|
global $wpdb;
|
|
|
|
$membership = wu_get_membership($membership_id);
|
|
|
|
if ( ! $membership) {
|
|
return new \WP_Error('error', __('An unexpected error happened.', 'wp-multisite-waas'));
|
|
}
|
|
|
|
$scheduled_swap = $membership->get_scheduled_swap();
|
|
|
|
if (empty($scheduled_swap)) {
|
|
return new \WP_Error('error', __('An unexpected error happened.', 'wp-multisite-waas'));
|
|
}
|
|
|
|
$order = $scheduled_swap->order;
|
|
|
|
$wpdb->query('START TRANSACTION');
|
|
|
|
try {
|
|
$membership->swap($order);
|
|
|
|
$status = $membership->save();
|
|
|
|
if (is_wp_error($status)) {
|
|
$wpdb->query('ROLLBACK');
|
|
|
|
return new \WP_Error('error', __('An unexpected error happened.', 'wp-multisite-waas'));
|
|
}
|
|
} catch (\Throwable $exception) {
|
|
$wpdb->query('ROLLBACK');
|
|
|
|
return new \WP_Error('error', __('An unexpected error happened.', 'wp-multisite-waas'));
|
|
}
|
|
|
|
/*
|
|
* Clean up the membership swap order.
|
|
*/
|
|
$membership->delete_scheduled_swap();
|
|
|
|
$wpdb->query('COMMIT');
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Watches the change in payment status to take action when needed.
|
|
*
|
|
* @todo Publishing sites should be done in async.
|
|
*
|
|
* @since 2.0.0
|
|
*
|
|
* @param string $old_status The old status of the membership.
|
|
* @param string $new_status The new status of the membership.
|
|
* @param integer $membership_id Payment ID.
|
|
* @return void
|
|
*/
|
|
public function transition_membership_status($old_status, $new_status, $membership_id): void {
|
|
|
|
$allowed_previous_status = [
|
|
Membership_Status::PENDING,
|
|
Membership_Status::ON_HOLD,
|
|
];
|
|
|
|
if ( ! in_array($old_status, $allowed_previous_status, true)) {
|
|
return;
|
|
}
|
|
|
|
$allowed_status = [
|
|
Membership_Status::ACTIVE,
|
|
Membership_Status::TRIALING,
|
|
];
|
|
|
|
if ( ! in_array($new_status, $allowed_status, true)) {
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Create pending sites.
|
|
*/
|
|
$membership = wu_get_membership($membership_id);
|
|
|
|
$status = $membership->publish_pending_site_async();
|
|
|
|
if (is_wp_error($status)) {
|
|
wu_log_add('site-errors', $status, LogLevel::ERROR);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Mark the membership date of cancellation.
|
|
*
|
|
* @since 2.0.0
|
|
*
|
|
* @param string $old_value Old status value.
|
|
* @param string $new_value New status value.
|
|
* @param int $item_id The membership id.
|
|
* @return void
|
|
*/
|
|
public function mark_cancelled_date($old_value, $new_value, $item_id): void {
|
|
|
|
if ('cancelled' === $new_value && $new_value !== $old_value) {
|
|
$membership = wu_get_membership($item_id);
|
|
|
|
$membership->set_date_cancellation(wu_get_current_time('mysql', true));
|
|
|
|
$membership->save();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Transfer a membership from a user to another.
|
|
*
|
|
* @since 2.0.0
|
|
*
|
|
* @param int $membership_id The ID of the membership being transferred.
|
|
* @param int $target_customer_id The new owner.
|
|
* @return mixed
|
|
*/
|
|
public function async_transfer_membership($membership_id, $target_customer_id) {
|
|
|
|
global $wpdb;
|
|
|
|
$membership = wu_get_membership($membership_id);
|
|
|
|
$target_customer = wu_get_customer($target_customer_id);
|
|
|
|
if ( ! $membership || ! $target_customer || absint($membership->get_customer_id()) === absint($target_customer->get_id())) {
|
|
return new \WP_Error('error', __('An unexpected error happened.', 'wp-multisite-waas'));
|
|
}
|
|
|
|
$wpdb->query('START TRANSACTION');
|
|
|
|
try {
|
|
/*
|
|
* Get Sites and move them over.
|
|
*/
|
|
$sites = wu_get_sites(
|
|
[
|
|
'meta_query' => [
|
|
'membership_id' => [
|
|
'key' => 'wu_membership_id',
|
|
'value' => $membership->get_id(),
|
|
],
|
|
],
|
|
]
|
|
);
|
|
|
|
foreach ($sites as $site) {
|
|
$site->set_customer_id($target_customer_id);
|
|
|
|
$saved = $site->save();
|
|
|
|
if (is_wp_error($saved)) {
|
|
$wpdb->query('ROLLBACK');
|
|
|
|
return $saved;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Change the membership
|
|
*/
|
|
$membership->set_customer_id($target_customer_id);
|
|
|
|
$saved = $membership->save();
|
|
|
|
if (is_wp_error($saved)) {
|
|
$wpdb->query('ROLLBACK');
|
|
|
|
return $saved;
|
|
}
|
|
} catch (\Throwable $e) {
|
|
$wpdb->query('ROLLBACK');
|
|
|
|
return new \WP_Error('exception', $e->getMessage());
|
|
}
|
|
|
|
$wpdb->query('COMMIT');
|
|
|
|
$membership->unlock();
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Delete a membership.
|
|
*
|
|
* @since 2.0.0
|
|
*
|
|
* @param int $membership_id The ID of the membership being deleted.
|
|
* @return mixed
|
|
*/
|
|
public function async_delete_membership($membership_id) {
|
|
|
|
global $wpdb;
|
|
|
|
$membership = wu_get_membership($membership_id);
|
|
|
|
if ( ! $membership) {
|
|
return new \WP_Error('error', __('An unexpected error happened.', 'wp-multisite-waas'));
|
|
}
|
|
|
|
$wpdb->query('START TRANSACTION');
|
|
|
|
try {
|
|
/*
|
|
* Get Sites and delete them.
|
|
*/
|
|
$sites = wu_get_sites(
|
|
[
|
|
'meta_query' => [
|
|
'membership_id' => [
|
|
'key' => 'wu_membership_id',
|
|
'value' => $membership->get_id(),
|
|
],
|
|
],
|
|
]
|
|
);
|
|
|
|
foreach ($sites as $site) {
|
|
$saved = $site->delete();
|
|
|
|
if (is_wp_error($saved)) {
|
|
$wpdb->query('ROLLBACK');
|
|
|
|
return $saved;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Delete the membership
|
|
*/
|
|
$saved = $membership->delete();
|
|
|
|
if (is_wp_error($saved)) {
|
|
$wpdb->query('ROLLBACK');
|
|
|
|
return $saved;
|
|
}
|
|
} catch (\Throwable $e) {
|
|
$wpdb->query('ROLLBACK');
|
|
|
|
return new \WP_Error('exception', $e->getMessage());
|
|
}
|
|
|
|
$wpdb->query('COMMIT');
|
|
|
|
return true;
|
|
}
|
|
}
|