* 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
885 lines
22 KiB
PHP
885 lines
22 KiB
PHP
<?php
|
|
/**
|
|
* Site Manager
|
|
*
|
|
* Handles processes related to sites.
|
|
*
|
|
* @package WP_Ultimo
|
|
* @subpackage Managers/Site_Manager
|
|
* @since 2.0.0
|
|
*/
|
|
|
|
namespace WP_Ultimo\Managers;
|
|
|
|
use WP_Ultimo\Helpers\Screenshot;
|
|
use WP_Ultimo\Database\Sites\Site_Type;
|
|
use WP_Ultimo\Database\Memberships\Membership_Status;
|
|
|
|
// Exit if accessed directly
|
|
defined('ABSPATH') || exit;
|
|
|
|
/**
|
|
* Handles processes related to sites.
|
|
*
|
|
* @since 2.0.0
|
|
*/
|
|
class Site_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 = 'site';
|
|
|
|
/**
|
|
* The model class associated to this manager.
|
|
*
|
|
* @since 2.0.0
|
|
* @var string
|
|
*/
|
|
protected $model_class = \WP_Ultimo\Models\Site::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('after_setup_theme', [$this, 'additional_thumbnail_sizes']);
|
|
|
|
add_action('wp_ajax_wu_get_screenshot', [$this, 'get_site_screenshot']);
|
|
|
|
add_action('wu_async_take_screenshot', [$this, 'async_get_site_screenshot']);
|
|
|
|
add_action('init', [$this, 'lock_site']);
|
|
|
|
add_action('admin_init', [$this, 'add_no_index_warning']);
|
|
|
|
add_action('wp_head', [$this, 'prevent_site_template_indexing'], 0);
|
|
|
|
add_action('login_enqueue_scripts', [$this, 'custom_login_logo']);
|
|
|
|
add_filter('login_headerurl', [$this, 'login_header_url']);
|
|
|
|
add_filter('login_headertext', [$this, 'login_header_text']);
|
|
|
|
add_action('wu_pending_site_published', [$this, 'handle_site_published'], 10, 2);
|
|
|
|
add_action('load-sites.php', [$this, 'add_notices_to_default_site_page']);
|
|
|
|
add_action('load-site-new.php', [$this, 'add_notices_to_default_site_page']);
|
|
|
|
add_filter('mucd_string_to_replace', [$this, 'search_and_replace_on_duplication'], 10, 3);
|
|
|
|
add_filter('wu_site_created', [$this, 'search_and_replace_for_new_site'], 10, 2);
|
|
|
|
add_action('wu_handle_bulk_action_form_site_delete-pending', [$this, 'handle_delete_pending_sites'], 100, 3);
|
|
|
|
add_action('users_list_table_query_args', [$this, 'hide_super_admin_from_list'], 10, 1);
|
|
|
|
add_action('wu_before_handle_order_submission', [$this, 'maybe_validate_add_new_site'], 15);
|
|
|
|
add_action('wu_checkout_before_process_checkout', [$this, 'maybe_add_new_site'], 5);
|
|
|
|
add_action('pre_get_blogs_of_user', [$this, 'hide_customer_sites_from_super_admin_list'], 999, 3);
|
|
|
|
add_filter('wpmu_validate_blog_signup', [$this, 'allow_hyphens_in_site_name'], 10, 1);
|
|
|
|
add_action('wu_daily', [$this, 'delete_pending_sites']);
|
|
}
|
|
|
|
/**
|
|
* Allows for hyphens to be used, since WordPress supports it.
|
|
*
|
|
* @since 2.1.3
|
|
*
|
|
* @param array $result The wpmu_validate_blog_signup result.
|
|
* @return array
|
|
*/
|
|
public function allow_hyphens_in_site_name($result) {
|
|
|
|
$errors = $result['errors'];
|
|
|
|
$blogname_errors = $errors->get_error_messages('blogname');
|
|
|
|
$message_to_ignore = __('Site names can only contain lowercase letters (a-z) and numbers.');
|
|
|
|
$error_key = array_search($message_to_ignore, $blogname_errors, true);
|
|
|
|
/**
|
|
* Check if we have an error for only letters and numbers
|
|
* if so, we remove it and re-validate with our custom rule
|
|
* which is the same, but also allows for hyphens.
|
|
*/
|
|
if ( ! empty($blogname_errors) && false !== $error_key) {
|
|
unset($result['errors']->errors['blogname'][ $error_key ]);
|
|
|
|
if (empty($result['errors']->errors['blogname'])) {
|
|
unset($result['errors']->errors['blogname']);
|
|
}
|
|
|
|
if (preg_match('/[^a-z0-9-]+/', (string) $result['blogname'])) {
|
|
$result['errors']->add('blogname', __('Site names can only contain lowercase letters (a-z), numbers, and hyphens.', 'wp-multisite-waas'));
|
|
}
|
|
}
|
|
|
|
return $result;
|
|
}
|
|
|
|
/**
|
|
* Handles the request to add a new site, if that's the case.
|
|
*
|
|
* @since 2.0.11
|
|
*
|
|
* @param \WP_Ultimo\Checkout\Checkout $checkout The current checkout object.
|
|
* @return void
|
|
*/
|
|
public function maybe_validate_add_new_site($checkout): void {
|
|
|
|
global $wpdb;
|
|
|
|
if (wu_request('create-new-site') && wp_verify_nonce(wu_request('create-new-site'), 'create-new-site')) {
|
|
$errors = new \WP_Error();
|
|
|
|
$rules = [
|
|
'site_title' => 'min:4',
|
|
'site_url' => 'required|lowercase|unique_site',
|
|
];
|
|
|
|
if ($checkout->is_last_step()) {
|
|
$membership = WP_Ultimo()->currents->get_membership();
|
|
|
|
$customer = wu_get_current_customer();
|
|
|
|
if ( ! $customer || ! $membership || $customer->get_id() !== $membership->get_customer_id()) {
|
|
$errors->add('not-owner', __('You do not have the necessary permissions to create a site to this membership', 'wp-multisite-waas'));
|
|
}
|
|
|
|
if ($errors->has_errors() === false) {
|
|
$d = wu_get_site_domain_and_path(wu_request('site_url', ''), $checkout->request_or_session('site_domain'));
|
|
|
|
$pending_site = $membership->create_pending_site(
|
|
[
|
|
'domain' => $d->domain,
|
|
'path' => $d->path,
|
|
'template_id' => $checkout->request_or_session('template_id'),
|
|
'title' => $checkout->request_or_session('site_title'),
|
|
'customer_id' => $customer->get_id(),
|
|
'membership_id' => $membership->get_id(),
|
|
]
|
|
);
|
|
|
|
if (is_wp_error($pending_site)) {
|
|
wp_send_json_error($pending_site);
|
|
|
|
exit;
|
|
}
|
|
|
|
$results = $membership->publish_pending_site();
|
|
|
|
if (is_wp_error($results)) {
|
|
wp_send_json_error($errors);
|
|
}
|
|
} else {
|
|
wp_send_json_error($errors);
|
|
}
|
|
|
|
wp_send_json_success([]);
|
|
} else {
|
|
$validation = $checkout->validate($rules);
|
|
|
|
if (is_wp_error($validation)) {
|
|
wp_send_json_error($validation);
|
|
}
|
|
|
|
$wpdb->query('COMMIT');
|
|
|
|
wp_send_json_success([]);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Checks if the current request is a add new site request.
|
|
*
|
|
* @since 2.0.11
|
|
* @return void
|
|
*/
|
|
public function maybe_add_new_site(): void {
|
|
|
|
if (wu_request('create-new-site') && wp_verify_nonce(wu_request('create-new-site'), 'create-new-site')) {
|
|
$redirect_url = wu_request('redirect_url', admin_url('admin.php?page=sites'));
|
|
|
|
$redirect_url = add_query_arg(
|
|
[
|
|
'new_site_created' => true,
|
|
],
|
|
$redirect_url
|
|
);
|
|
|
|
wp_safe_redirect($redirect_url);
|
|
|
|
exit;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Triggers the do_event of the site publish successful.
|
|
*
|
|
* @since 2.0.0
|
|
*
|
|
* @param \WP_Ultimo\Models\Site $site The site.
|
|
* @param \WP_Ultimo\Models\Membership $membership The payment.
|
|
* @return void
|
|
*/
|
|
public function handle_site_published($site, $membership): void {
|
|
|
|
$payload = array_merge(
|
|
wu_generate_event_payload('site', $site),
|
|
wu_generate_event_payload('membership', $membership),
|
|
wu_generate_event_payload('customer', $membership->get_customer())
|
|
);
|
|
|
|
wu_do_event('site_published', $payload);
|
|
}
|
|
|
|
/**
|
|
* Locks the site front-end if the site is not public.
|
|
*
|
|
* @todo Let the admin chose the behavior. Maybe redirect to main site?
|
|
*
|
|
* @since 2.0.0
|
|
* @return void
|
|
*/
|
|
public function lock_site(): void {
|
|
|
|
if (is_main_site() || is_admin() || wu_is_login_page() || wp_doing_ajax() || wu_request('wu-ajax')) {
|
|
return;
|
|
}
|
|
|
|
$can_access = true;
|
|
|
|
$redirect_url = null;
|
|
|
|
$site = wu_get_current_site();
|
|
|
|
if ( ! $site->is_active()) {
|
|
$can_access = false;
|
|
}
|
|
|
|
$membership = $site->get_membership();
|
|
|
|
$status = $membership ? $membership->get_status() : false;
|
|
|
|
$is_cancelled = Membership_Status::CANCELLED === $status;
|
|
|
|
$is_inactive = $status && ! $membership->is_active() && Membership_Status::TRIALING !== $status;
|
|
|
|
if ($is_cancelled || ($is_inactive && wu_get_setting('block_frontend', false))) {
|
|
|
|
// If membership is cancelled we do not add the grace period
|
|
$grace_period = Membership_Status::CANCELLED !== $status ? (int) wu_get_setting('block_frontend_grace_period', 0) : 0;
|
|
|
|
$expiration_time = wu_date($membership->get_date_expiration())->getTimestamp() + $grace_period * DAY_IN_SECONDS;
|
|
|
|
if ($expiration_time < wu_date()->getTimestamp()) {
|
|
$checkout_pages = \WP_Ultimo\Checkout\Checkout_Pages::get_instance();
|
|
|
|
// We only show the url field when block_frontend is true
|
|
$redirect_url = wu_get_setting('block_frontend', false) ? $checkout_pages->get_page_url('block_frontend') : false;
|
|
|
|
$can_access = false;
|
|
}
|
|
}
|
|
|
|
if (false === $can_access) {
|
|
if ($redirect_url) {
|
|
wp_safe_redirect($redirect_url);
|
|
|
|
exit;
|
|
}
|
|
|
|
wp_die(
|
|
new \WP_Error( // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
|
'not-available',
|
|
sprintf(__('This site is not available at the moment.<br><small>If you are the site admin, click <a href="%s">here</a> to login.</small>', 'wp-multisite-waas'), wp_login_url()), // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
|
[
|
|
'title' => esc_html__('Site not available', 'wp-multisite-waas'),
|
|
]
|
|
),
|
|
'',
|
|
['code' => 200]
|
|
);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Takes screenshots asynchronously.
|
|
*
|
|
* @since 2.0.0
|
|
*
|
|
* @param int $site_id The site ID.
|
|
* @return mixed
|
|
*/
|
|
public function async_get_site_screenshot($site_id) {
|
|
|
|
$site = wu_get_site($site_id);
|
|
|
|
if ( ! $site) {
|
|
return false;
|
|
}
|
|
|
|
$domain = $site->get_active_site_url();
|
|
|
|
$attachment_id = Screenshot::take_screenshot($domain);
|
|
|
|
if ( ! $attachment_id) {
|
|
return false;
|
|
}
|
|
|
|
$site->set_featured_image_id($attachment_id);
|
|
|
|
return $site->save();
|
|
}
|
|
|
|
/**
|
|
* Listens for the ajax endpoint and generate the screenshot.
|
|
*
|
|
* @since 2.0.0
|
|
* @return void
|
|
*/
|
|
public function get_site_screenshot(): void {
|
|
|
|
$site_id = wu_request('site_id');
|
|
|
|
$site = wu_get_site($site_id);
|
|
|
|
if ( ! $site) {
|
|
wp_send_json_error(
|
|
new \WP_Error('missing-site', __('Site not found.', 'wp-multisite-waas'))
|
|
);
|
|
}
|
|
|
|
$domain = $site->get_active_site_url();
|
|
|
|
$attachment_id = Screenshot::take_screenshot($domain);
|
|
|
|
if ( ! $attachment_id) {
|
|
wp_send_json_error(
|
|
new \WP_Error('error', __('We were not able to fetch the screenshot.', 'wp-multisite-waas'))
|
|
);
|
|
}
|
|
|
|
$attachment_url = wp_get_attachment_image_src($attachment_id, 'wu-thumb-medium');
|
|
|
|
wp_send_json_success(
|
|
[
|
|
'attachment_id' => $attachment_id,
|
|
'attachment_url' => $attachment_url[0],
|
|
]
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Add the additional sizes required by WP Multisite WaaS.
|
|
*
|
|
* Add for the main site only.
|
|
*
|
|
* @since 2.0.0
|
|
* @return void
|
|
*/
|
|
public function additional_thumbnail_sizes(): void {
|
|
|
|
if (is_main_site()) {
|
|
add_image_size('wu-thumb-large', 900, 675, ['center', 'top']); // cropped
|
|
add_image_size('wu-thumb-medium', 400, 300, ['center', 'top']); // cropped
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Adds a notification if the no-index setting is active.
|
|
*
|
|
* @since 1.9.8
|
|
* @return void
|
|
*/
|
|
public function add_no_index_warning(): void {
|
|
|
|
if (wu_get_setting('stop_template_indexing', false)) {
|
|
add_meta_box('wu-warnings', __('WP Multisite WaaS - Search Engines', 'wp-multisite-waas'), [$this, 'render_no_index_warning'], 'dashboard-network', 'normal', 'high');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Renders the no indexing warning.
|
|
*
|
|
* @since 2.0.0
|
|
* @return void
|
|
*/
|
|
public function render_no_index_warning(): void {
|
|
?>
|
|
|
|
<div class="wu-styling">
|
|
|
|
<div class="wu-border-l-4 wu-border-yellow-500 wu-border-solid wu-border-0 wu-px-4 wu-py-2 wu--m-3">
|
|
|
|
<p><?php echo wp_kses_post(__('Your WP Multisite WaaS settings are configured to <strong>prevent search engines such as Google from indexing your template sites</strong>.', 'wp-multisite-waas')); ?></p>
|
|
|
|
<p><?php echo wp_kses_post(sprintf(__('If you are experiencing negative SEO impacts on other sites in your network, consider disabling this setting <a href="%s">here</a>.', 'wp-multisite-waas'), wu_network_admin_url('wp-ultimo-settings', ['tab' => 'sites']))); ?></p>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
<?php
|
|
}
|
|
|
|
/**
|
|
* Prevents Search Engines from indexing Site Templates.
|
|
*
|
|
* @since 1.6.0
|
|
* @return void
|
|
*/
|
|
public function prevent_site_template_indexing(): void {
|
|
|
|
if ( ! wu_get_setting('stop_template_indexing', false)) {
|
|
return;
|
|
}
|
|
|
|
$site = wu_get_current_site();
|
|
|
|
if ($site && $site->get_type() === Site_Type::SITE_TEMPLATE) {
|
|
if (function_exists('wp_robots_no_robots')) {
|
|
add_filter('wp_robots', 'wp_robots_no_robots'); // WordPress 5.7+
|
|
|
|
} else {
|
|
wp_no_robots(); // phpcs:ignore WordPress.WP.DeprecatedFunctions.wp_no_robotsFound
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Check if sub-site has a custom logo and change login logo.
|
|
*
|
|
* @since 2.0.0
|
|
*
|
|
* @return void
|
|
*/
|
|
public function custom_login_logo(): void {
|
|
|
|
if ( ! wu_get_setting('subsite_custom_login_logo', false) || ! has_custom_logo()) {
|
|
$logo = wu_get_network_logo();
|
|
} else {
|
|
$logo = wp_get_attachment_image_src(get_theme_mod('custom_logo'), 'full');
|
|
|
|
$logo = wu_get_isset($logo, 0, false);
|
|
}
|
|
|
|
if (empty($logo)) {
|
|
return;
|
|
}
|
|
|
|
// phpcs:disable
|
|
|
|
?>
|
|
|
|
<style type="text/css">
|
|
|
|
#login h1 a, .login h1 a {
|
|
background-image: url(<?php echo $logo; ?>);
|
|
background-position: center center;
|
|
background-size: contain;
|
|
}
|
|
|
|
</style>
|
|
|
|
<?php // phpcs:enable
|
|
}
|
|
|
|
/**
|
|
* Replaces the WordPress url with the site url.
|
|
*
|
|
* @since 2.0.0
|
|
* @return string
|
|
*/
|
|
public function login_header_url() {
|
|
|
|
return get_site_url();
|
|
}
|
|
|
|
/**
|
|
* Replaces the WordPress text with the site name.
|
|
*
|
|
* @since 2.0.0
|
|
* @return string
|
|
*/
|
|
public function login_header_text() {
|
|
|
|
return get_bloginfo('name');
|
|
}
|
|
|
|
/**
|
|
* Add notices to default site page, recommending the WP Multisite WaaS option.
|
|
*
|
|
* @since 2.0.0
|
|
* @return void
|
|
*/
|
|
public function add_notices_to_default_site_page(): void {
|
|
|
|
$notice = __('Hey there! We highly recommend managing your network sites using the WP Multisite WaaS → Sites page. <br>If you want to avoid confusion, you can also hide this page from the admin panel completely on the WP Multisite WaaS → Settings → Whitelabel options.', 'wp-multisite-waas');
|
|
|
|
WP_Ultimo()->notices->add(
|
|
$notice,
|
|
'info',
|
|
'network-admin',
|
|
'wu-sites-use-wp-ultimo',
|
|
[
|
|
[
|
|
'title' => __('Go to the WP Multisite WaaS Sites page →', 'wp-multisite-waas'),
|
|
'url' => wu_network_admin_url('wp-ultimo-sites'),
|
|
],
|
|
[
|
|
'title' => __('Go to the Whitelabel Settings →', 'wp-multisite-waas'),
|
|
'url' => wu_network_admin_url(
|
|
'wp-ultimo-settings',
|
|
[
|
|
'tab' => 'whitelabel',
|
|
]
|
|
),
|
|
],
|
|
]
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Add search and replace filter to be used on site duplication.
|
|
*
|
|
* @since 1.6.2
|
|
* @param array $search_and_replace List to search and replace.
|
|
* @param int $from_site_id original site id.
|
|
* @param int $to_site_id New site id.
|
|
* @return array
|
|
*/
|
|
public function search_and_replace_on_duplication($search_and_replace, $from_site_id, $to_site_id) {
|
|
|
|
$search_and_replace_settings = $this->get_search_and_replace_settings();
|
|
|
|
$additional_duplication = apply_filters('wu_search_and_replace_on_duplication', $search_and_replace_settings, $from_site_id, $to_site_id);
|
|
|
|
$final_list = array_merge($search_and_replace, $additional_duplication);
|
|
|
|
return $this->filter_illegal_search_keys($final_list);
|
|
}
|
|
|
|
/**
|
|
* Get search and replace settings
|
|
*
|
|
* @since 1.7.0
|
|
* @return array
|
|
*/
|
|
public function get_search_and_replace_settings() {
|
|
|
|
$search_and_replace = wu_get_setting('search_and_replace', []);
|
|
|
|
$pairs = [];
|
|
|
|
foreach ($search_and_replace as $item) {
|
|
if ((isset($item['search']) && ! empty($item['search'])) && isset($item['replace'])) {
|
|
$pairs[ $item['search'] ] = $item['replace'];
|
|
}
|
|
}
|
|
|
|
return $pairs;
|
|
}
|
|
|
|
/**
|
|
* Handles search and replace for new blogs from WordPress.
|
|
*
|
|
* @since 1.7.0
|
|
* @param array $data The date being saved.
|
|
* @param object $site The site object.
|
|
* @return void
|
|
*/
|
|
public static function search_and_replace_for_new_site($data, $site): void {
|
|
|
|
$to_site_id = $site->get_id();
|
|
|
|
if ( ! $to_site_id) {
|
|
return;
|
|
}
|
|
|
|
/**
|
|
* In order to be backwards compatible here, we'll have to do some crazy stuff,
|
|
* like overload the form session with the meta data saved on the pending site.
|
|
*/
|
|
$transient = wu_get_site($to_site_id)->get_meta('wu_form_data', []);
|
|
|
|
wu_get_session('signup')->set('form', $transient);
|
|
|
|
global $wpdb;
|
|
|
|
$to_blog_prefix = $wpdb->get_blog_prefix($to_site_id);
|
|
|
|
$string_to_replace = apply_filters('mucd_string_to_replace', [], false, $to_site_id); // phpcs:ignore
|
|
|
|
$tables = [];
|
|
|
|
$to_blog_prefix_like = $wpdb->esc_like($to_blog_prefix);
|
|
|
|
$results = \MUCD_Data::do_sql_query('SHOW TABLES LIKE \'' . $to_blog_prefix_like . '%\'', 'col', false);
|
|
|
|
foreach ($results as $k => $v) {
|
|
$tables[ str_replace($to_blog_prefix, '', (string) $v) ] = [];
|
|
}
|
|
|
|
foreach ( $tables as $table => $col) {
|
|
$results = \MUCD_Data::do_sql_query('SHOW COLUMNS FROM `' . $to_blog_prefix . $table . '`', 'col', false);
|
|
|
|
$columns = [];
|
|
|
|
foreach ($results as $k => $v) {
|
|
$columns[] = $v;
|
|
}
|
|
|
|
$tables[ $table ] = $columns;
|
|
}
|
|
|
|
$default_tables = \MUCD_Option::get_fields_to_update();
|
|
|
|
foreach ($default_tables as $table => $field) {
|
|
$tables[ $table ] = $field;
|
|
}
|
|
|
|
foreach ($tables as $table => $field) {
|
|
foreach ($string_to_replace as $from_string => $to_string) {
|
|
\MUCD_Data::update($to_blog_prefix . $table, $field, $from_string, $to_string);
|
|
}
|
|
}
|
|
}
|
|
/**
|
|
* Makes sure the search and replace array have no illegal values, such as null, false, etc
|
|
*
|
|
* @since 1.7.3
|
|
* @param array $search_and_replace The search and replace list.
|
|
*/
|
|
public function filter_illegal_search_keys($search_and_replace): array {
|
|
|
|
return array_filter($search_and_replace, fn($k) => ! is_null($k) && false !== $k && ! empty($k), ARRAY_FILTER_USE_KEY);
|
|
}
|
|
|
|
/**
|
|
* Handle the deletion of pending sites.
|
|
*
|
|
* @since 2.0.0
|
|
*
|
|
* @param string $action The action.
|
|
* @param string $model The model.
|
|
* @param array $ids The ids list.
|
|
* @return void
|
|
*/
|
|
public function handle_delete_pending_sites($action, $model, $ids): void {
|
|
|
|
foreach ($ids as $membership_id) {
|
|
$membership = wu_get_membership($membership_id);
|
|
|
|
if (empty($membership)) {
|
|
/*
|
|
* Make sure we are able to delete pending
|
|
* sites even when memberships no longer exist.
|
|
*/
|
|
delete_metadata('wu_membership', $membership_id, 'pending_site');
|
|
|
|
continue;
|
|
}
|
|
|
|
$membership->delete_pending_site();
|
|
}
|
|
|
|
wp_send_json_success(
|
|
[
|
|
'redirect_url' => add_query_arg('deleted', count($ids), wu_get_current_url()),
|
|
]
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Hide the super admin user from the sub-site table list.
|
|
*
|
|
* @since 2.0.0
|
|
*
|
|
* @param array $args List table user search arguments.
|
|
* @return array
|
|
*/
|
|
public function hide_super_admin_from_list($args) {
|
|
|
|
if ( ! is_super_admin()) {
|
|
$args['login__not_in'] = get_super_admins();
|
|
}
|
|
|
|
return $args;
|
|
}
|
|
|
|
/**
|
|
* Hides customer sites from the super admin user on listing.
|
|
*
|
|
* @since 2.0.11
|
|
*
|
|
* @param null|object[] $sites An array of site objects of which the user is a member.
|
|
* @param int $user_id User ID.
|
|
* @param bool $all Whether the returned array should contain all sites, including
|
|
* those marked 'deleted', 'archived', or 'spam'. Default false.
|
|
*/
|
|
public function hide_customer_sites_from_super_admin_list($sites, $user_id, $all) {
|
|
|
|
global $wpdb;
|
|
|
|
if ( ! is_super_admin()) {
|
|
return $sites;
|
|
}
|
|
|
|
$keys = get_user_meta($user_id);
|
|
|
|
if (empty($keys)) {
|
|
return $sites;
|
|
}
|
|
|
|
// List the main site at beginning of array.
|
|
if (isset($keys[ $wpdb->base_prefix . 'capabilities' ]) && defined('MULTISITE')) {
|
|
$site_ids[] = 1;
|
|
|
|
unset($keys[ $wpdb->base_prefix . 'capabilities' ]);
|
|
}
|
|
|
|
$keys = array_keys($keys);
|
|
|
|
foreach ($keys as $key) {
|
|
if (! str_ends_with($key, 'capabilities')) {
|
|
continue;
|
|
}
|
|
|
|
if ($wpdb->base_prefix && ! str_starts_with($key, (string) $wpdb->base_prefix)) {
|
|
continue;
|
|
}
|
|
|
|
$site_id = str_replace([$wpdb->base_prefix, '_capabilities'], '', $key);
|
|
|
|
if ( ! is_numeric($site_id)) {
|
|
continue;
|
|
}
|
|
|
|
$site_ids[] = (int) $site_id;
|
|
}
|
|
|
|
$sites = [];
|
|
|
|
if ( ! empty($site_ids)) {
|
|
|
|
/**
|
|
* Here we change the default WP behavior to filter
|
|
* sites with wu_type meta value different than
|
|
* Site_Type::CUSTOMER_OWNED or without this meta
|
|
*/
|
|
$args = [
|
|
'site__in' => $site_ids,
|
|
'update_site_meta_cache' => false,
|
|
'number' => 40,
|
|
'meta_query' => [ // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query
|
|
'relation' => 'OR',
|
|
[
|
|
'key' => 'wu_type',
|
|
'compare' => 'NOT EXISTS',
|
|
],
|
|
[
|
|
'key' => 'wu_type',
|
|
'compare' => 'NOT LIKE',
|
|
'value' => Site_Type::CUSTOMER_OWNED,
|
|
],
|
|
],
|
|
];
|
|
|
|
if ( ! $all) {
|
|
$args['archived'] = 0;
|
|
$args['spam'] = 0;
|
|
$args['deleted'] = 0;
|
|
}
|
|
|
|
$_sites = array_merge(
|
|
[
|
|
get_site(wu_get_main_site_id()),
|
|
],
|
|
get_sites($args),
|
|
);
|
|
|
|
foreach ($_sites as $site) {
|
|
if ( ! $site) {
|
|
continue;
|
|
}
|
|
|
|
$sites[ $site->id ] = (object) [
|
|
'userblog_id' => $site->id,
|
|
'blogname' => $site->blogname,
|
|
'domain' => $site->domain,
|
|
'path' => $site->path,
|
|
'site_id' => $site->network_id,
|
|
'siteurl' => $site->siteurl,
|
|
'archived' => $site->archived,
|
|
'mature' => $site->mature,
|
|
'spam' => $site->spam,
|
|
'deleted' => $site->deleted,
|
|
];
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Replicates the original WP Filter here, for good measure.
|
|
*
|
|
* Filters the list of sites a user belongs to.
|
|
*
|
|
* @since 2.0.11
|
|
*
|
|
* @param object[] $sites An array of site objects belonging to the user.
|
|
* @param int $user_id User ID.
|
|
* @param bool $all Whether the returned sites array should contain all sites, including
|
|
* those marked 'deleted', 'archived', or 'spam'. Default false.
|
|
*/
|
|
return apply_filters('get_blogs_of_user', $sites, $user_id, $all); // phpcs:ignore
|
|
}
|
|
|
|
/**
|
|
* Delete pending sites from non-pending memberships
|
|
*
|
|
* @since 2.1.3
|
|
*/
|
|
public function delete_pending_sites(): void {
|
|
|
|
$pending_sites = \WP_Ultimo\Models\Site::get_all_by_type('pending');
|
|
|
|
foreach ($pending_sites as $site) {
|
|
if ($site->is_publishing()) {
|
|
continue;
|
|
}
|
|
|
|
$membership = $site->get_membership();
|
|
|
|
if ($membership->is_active() || $membership->is_trialing()) {
|
|
|
|
// Check if the last modify has more than some time, to avoid the deletion of sites on creation process
|
|
if ($membership->get_date_modified() < gmdate('Y-m-d H:i:s', strtotime('-1 days'))) {
|
|
$membership->delete_pending_site();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|