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) && $error_key !== false) {
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-ultimo'));
}
}
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-ultimo'));
}
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_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 = $status === Membership_Status::CANCELLED;
$is_inactive = $status && ! $membership->is_active() && $status !== Membership_Status::TRIALING;
if ($is_cancelled || ($is_inactive && wu_get_setting('block_frontend', false))) {
// If membership is cancelled we do not add the grace period
$grace_period = $status !== Membership_Status::CANCELLED ? (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 ($can_access === false) {
if ($redirect_url) {
wp_redirect($redirect_url);
exit;
}
wp_die(
new \WP_Error(
'not-available',
// phpcs:ignore
sprintf( __('This site is not available at the moment.
If you are the site admin, click here to login.', 'wp-ultimo'), wp_login_url()),
[
'title' => __('Site not available', 'wp-ultimo'),
]
),
'',
['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-ultimo'))
);
}
$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-ultimo'))
);
}
$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-ultimo'), [$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 { // phpcs:disable ?>
prevent search engines such as Google from indexing your template sites.', 'wp-ultimo'); ?>
here.', 'wp-ultimo'), wu_network_admin_url('wp-ultimo-settings', ['tab' => 'sites'])); ?>