balmet.com

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs

ms-functions.php (93902B)


      1 <?php
      2 /**
      3  * Multisite WordPress API
      4  *
      5  * @package WordPress
      6  * @subpackage Multisite
      7  * @since 3.0.0
      8  */
      9 
     10 /**
     11  * Gets the network's site and user counts.
     12  *
     13  * @since MU (3.0.0)
     14  *
     15  * @return int[] {
     16  *     Site and user count for the network.
     17  *
     18  *     @type int $blogs Number of sites on the network.
     19  *     @type int $users Number of users on the network.
     20  * }
     21  */
     22 function get_sitestats() {
     23 	$stats = array(
     24 		'blogs' => get_blog_count(),
     25 		'users' => get_user_count(),
     26 	);
     27 
     28 	return $stats;
     29 }
     30 
     31 /**
     32  * Get one of a user's active blogs
     33  *
     34  * Returns the user's primary blog, if they have one and
     35  * it is active. If it's inactive, function returns another
     36  * active blog of the user. If none are found, the user
     37  * is added as a Subscriber to the Dashboard Blog and that blog
     38  * is returned.
     39  *
     40  * @since MU (3.0.0)
     41  *
     42  * @param int $user_id The unique ID of the user
     43  * @return WP_Site|void The blog object
     44  */
     45 function get_active_blog_for_user( $user_id ) {
     46 	$blogs = get_blogs_of_user( $user_id );
     47 	if ( empty( $blogs ) ) {
     48 		return;
     49 	}
     50 
     51 	if ( ! is_multisite() ) {
     52 		return $blogs[ get_current_blog_id() ];
     53 	}
     54 
     55 	$primary_blog = get_user_meta( $user_id, 'primary_blog', true );
     56 	$first_blog   = current( $blogs );
     57 	if ( false !== $primary_blog ) {
     58 		if ( ! isset( $blogs[ $primary_blog ] ) ) {
     59 			update_user_meta( $user_id, 'primary_blog', $first_blog->userblog_id );
     60 			$primary = get_site( $first_blog->userblog_id );
     61 		} else {
     62 			$primary = get_site( $primary_blog );
     63 		}
     64 	} else {
     65 		// TODO: Review this call to add_user_to_blog too - to get here the user must have a role on this blog?
     66 		$result = add_user_to_blog( $first_blog->userblog_id, $user_id, 'subscriber' );
     67 
     68 		if ( ! is_wp_error( $result ) ) {
     69 			update_user_meta( $user_id, 'primary_blog', $first_blog->userblog_id );
     70 			$primary = $first_blog;
     71 		}
     72 	}
     73 
     74 	if ( ( ! is_object( $primary ) ) || ( 1 == $primary->archived || 1 == $primary->spam || 1 == $primary->deleted ) ) {
     75 		$blogs = get_blogs_of_user( $user_id, true ); // If a user's primary blog is shut down, check their other blogs.
     76 		$ret   = false;
     77 		if ( is_array( $blogs ) && count( $blogs ) > 0 ) {
     78 			foreach ( (array) $blogs as $blog_id => $blog ) {
     79 				if ( get_current_network_id() != $blog->site_id ) {
     80 					continue;
     81 				}
     82 				$details = get_site( $blog_id );
     83 				if ( is_object( $details ) && 0 == $details->archived && 0 == $details->spam && 0 == $details->deleted ) {
     84 					$ret = $details;
     85 					if ( get_user_meta( $user_id, 'primary_blog', true ) != $blog_id ) {
     86 						update_user_meta( $user_id, 'primary_blog', $blog_id );
     87 					}
     88 					if ( ! get_user_meta( $user_id, 'source_domain', true ) ) {
     89 						update_user_meta( $user_id, 'source_domain', $details->domain );
     90 					}
     91 					break;
     92 				}
     93 			}
     94 		} else {
     95 			return;
     96 		}
     97 		return $ret;
     98 	} else {
     99 		return $primary;
    100 	}
    101 }
    102 
    103 /**
    104  * The number of active users in your installation.
    105  *
    106  * The count is cached and updated twice daily. This is not a live count.
    107  *
    108  * @since MU (3.0.0)
    109  * @since 4.8.0 The `$network_id` parameter has been added.
    110  *
    111  * @param int|null $network_id ID of the network. Default is the current network.
    112  * @return int Number of active users on the network.
    113  */
    114 function get_user_count( $network_id = null ) {
    115 	return get_network_option( $network_id, 'user_count' );
    116 }
    117 
    118 /**
    119  * The number of active sites on your installation.
    120  *
    121  * The count is cached and updated twice daily. This is not a live count.
    122  *
    123  * @since MU (3.0.0)
    124  * @since 3.7.0 The `$network_id` parameter has been deprecated.
    125  * @since 4.8.0 The `$network_id` parameter is now being used.
    126  *
    127  * @param int|null $network_id ID of the network. Default is the current network.
    128  * @return int Number of active sites on the network.
    129  */
    130 function get_blog_count( $network_id = null ) {
    131 	return get_network_option( $network_id, 'blog_count' );
    132 }
    133 
    134 /**
    135  * Gets a blog post from any site on the network.
    136  *
    137  * This function is similar to get_post(), except that it can retrieve a post
    138  * from any site on the network, not just the current site.
    139  *
    140  * @since MU (3.0.0)
    141  *
    142  * @param int $blog_id ID of the blog.
    143  * @param int $post_id ID of the post being looked for.
    144  * @return WP_Post|null WP_Post object on success, null on failure
    145  */
    146 function get_blog_post( $blog_id, $post_id ) {
    147 	switch_to_blog( $blog_id );
    148 	$post = get_post( $post_id );
    149 	restore_current_blog();
    150 
    151 	return $post;
    152 }
    153 
    154 /**
    155  * Adds a user to a blog, along with specifying the user's role.
    156  *
    157  * Use the {@see 'add_user_to_blog'} action to fire an event when users are added to a blog.
    158  *
    159  * @since MU (3.0.0)
    160  *
    161  * @param int    $blog_id ID of the blog the user is being added to.
    162  * @param int    $user_id ID of the user being added.
    163  * @param string $role    The role you want the user to have.
    164  * @return true|WP_Error True on success or a WP_Error object if the user doesn't exist
    165  *                       or could not be added.
    166  */
    167 function add_user_to_blog( $blog_id, $user_id, $role ) {
    168 	switch_to_blog( $blog_id );
    169 
    170 	$user = get_userdata( $user_id );
    171 
    172 	if ( ! $user ) {
    173 		restore_current_blog();
    174 		return new WP_Error( 'user_does_not_exist', __( 'The requested user does not exist.' ) );
    175 	}
    176 
    177 	/**
    178 	 * Filters whether a user should be added to a site.
    179 	 *
    180 	 * @since 4.9.0
    181 	 *
    182 	 * @param true|WP_Error $retval  True if the user should be added to the site, error
    183 	 *                               object otherwise.
    184 	 * @param int           $user_id User ID.
    185 	 * @param string        $role    User role.
    186 	 * @param int           $blog_id Site ID.
    187 	 */
    188 	$can_add_user = apply_filters( 'can_add_user_to_blog', true, $user_id, $role, $blog_id );
    189 
    190 	if ( true !== $can_add_user ) {
    191 		restore_current_blog();
    192 
    193 		if ( is_wp_error( $can_add_user ) ) {
    194 			return $can_add_user;
    195 		}
    196 
    197 		return new WP_Error( 'user_cannot_be_added', __( 'User cannot be added to this site.' ) );
    198 	}
    199 
    200 	if ( ! get_user_meta( $user_id, 'primary_blog', true ) ) {
    201 		update_user_meta( $user_id, 'primary_blog', $blog_id );
    202 		$site = get_site( $blog_id );
    203 		update_user_meta( $user_id, 'source_domain', $site->domain );
    204 	}
    205 
    206 	$user->set_role( $role );
    207 
    208 	/**
    209 	 * Fires immediately after a user is added to a site.
    210 	 *
    211 	 * @since MU (3.0.0)
    212 	 *
    213 	 * @param int    $user_id User ID.
    214 	 * @param string $role    User role.
    215 	 * @param int    $blog_id Blog ID.
    216 	 */
    217 	do_action( 'add_user_to_blog', $user_id, $role, $blog_id );
    218 
    219 	clean_user_cache( $user_id );
    220 	wp_cache_delete( $blog_id . '_user_count', 'blog-details' );
    221 
    222 	restore_current_blog();
    223 
    224 	return true;
    225 }
    226 
    227 /**
    228  * Remove a user from a blog.
    229  *
    230  * Use the {@see 'remove_user_from_blog'} action to fire an event when
    231  * users are removed from a blog.
    232  *
    233  * Accepts an optional `$reassign` parameter, if you want to
    234  * reassign the user's blog posts to another user upon removal.
    235  *
    236  * @since MU (3.0.0)
    237  *
    238  * @global wpdb $wpdb WordPress database abstraction object.
    239  *
    240  * @param int $user_id  ID of the user being removed.
    241  * @param int $blog_id  Optional. ID of the blog the user is being removed from. Default 0.
    242  * @param int $reassign Optional. ID of the user to whom to reassign posts. Default 0.
    243  * @return true|WP_Error True on success or a WP_Error object if the user doesn't exist.
    244  */
    245 function remove_user_from_blog( $user_id, $blog_id = 0, $reassign = 0 ) {
    246 	global $wpdb;
    247 
    248 	switch_to_blog( $blog_id );
    249 	$user_id = (int) $user_id;
    250 
    251 	/**
    252 	 * Fires before a user is removed from a site.
    253 	 *
    254 	 * @since MU (3.0.0)
    255 	 * @since 5.4.0 Added the `$reassign` parameter.
    256 	 *
    257 	 * @param int $user_id  ID of the user being removed.
    258 	 * @param int $blog_id  ID of the blog the user is being removed from.
    259 	 * @param int $reassign ID of the user to whom to reassign posts.
    260 	 */
    261 	do_action( 'remove_user_from_blog', $user_id, $blog_id, $reassign );
    262 
    263 	// If being removed from the primary blog, set a new primary
    264 	// if the user is assigned to multiple blogs.
    265 	$primary_blog = get_user_meta( $user_id, 'primary_blog', true );
    266 	if ( $primary_blog == $blog_id ) {
    267 		$new_id     = '';
    268 		$new_domain = '';
    269 		$blogs      = get_blogs_of_user( $user_id );
    270 		foreach ( (array) $blogs as $blog ) {
    271 			if ( $blog->userblog_id == $blog_id ) {
    272 				continue;
    273 			}
    274 			$new_id     = $blog->userblog_id;
    275 			$new_domain = $blog->domain;
    276 			break;
    277 		}
    278 
    279 		update_user_meta( $user_id, 'primary_blog', $new_id );
    280 		update_user_meta( $user_id, 'source_domain', $new_domain );
    281 	}
    282 
    283 	// wp_revoke_user( $user_id );
    284 	$user = get_userdata( $user_id );
    285 	if ( ! $user ) {
    286 		restore_current_blog();
    287 		return new WP_Error( 'user_does_not_exist', __( 'That user does not exist.' ) );
    288 	}
    289 
    290 	$user->remove_all_caps();
    291 
    292 	$blogs = get_blogs_of_user( $user_id );
    293 	if ( count( $blogs ) == 0 ) {
    294 		update_user_meta( $user_id, 'primary_blog', '' );
    295 		update_user_meta( $user_id, 'source_domain', '' );
    296 	}
    297 
    298 	if ( $reassign ) {
    299 		$reassign = (int) $reassign;
    300 		$post_ids = $wpdb->get_col( $wpdb->prepare( "SELECT ID FROM $wpdb->posts WHERE post_author = %d", $user_id ) );
    301 		$link_ids = $wpdb->get_col( $wpdb->prepare( "SELECT link_id FROM $wpdb->links WHERE link_owner = %d", $user_id ) );
    302 
    303 		if ( ! empty( $post_ids ) ) {
    304 			$wpdb->query( $wpdb->prepare( "UPDATE $wpdb->posts SET post_author = %d WHERE post_author = %d", $reassign, $user_id ) );
    305 			array_walk( $post_ids, 'clean_post_cache' );
    306 		}
    307 
    308 		if ( ! empty( $link_ids ) ) {
    309 			$wpdb->query( $wpdb->prepare( "UPDATE $wpdb->links SET link_owner = %d WHERE link_owner = %d", $reassign, $user_id ) );
    310 			array_walk( $link_ids, 'clean_bookmark_cache' );
    311 		}
    312 	}
    313 
    314 	restore_current_blog();
    315 
    316 	return true;
    317 }
    318 
    319 /**
    320  * Get the permalink for a post on another blog.
    321  *
    322  * @since MU (3.0.0) 1.0
    323  *
    324  * @param int $blog_id ID of the source blog.
    325  * @param int $post_id ID of the desired post.
    326  * @return string The post's permalink
    327  */
    328 function get_blog_permalink( $blog_id, $post_id ) {
    329 	switch_to_blog( $blog_id );
    330 	$link = get_permalink( $post_id );
    331 	restore_current_blog();
    332 
    333 	return $link;
    334 }
    335 
    336 /**
    337  * Get a blog's numeric ID from its URL.
    338  *
    339  * On a subdirectory installation like example.com/blog1/,
    340  * $domain will be the root 'example.com' and $path the
    341  * subdirectory '/blog1/'. With subdomains like blog1.example.com,
    342  * $domain is 'blog1.example.com' and $path is '/'.
    343  *
    344  * @since MU (3.0.0)
    345  *
    346  * @global wpdb $wpdb WordPress database abstraction object.
    347  *
    348  * @param string $domain
    349  * @param string $path   Optional. Not required for subdomain installations.
    350  * @return int 0 if no blog found, otherwise the ID of the matching blog
    351  */
    352 function get_blog_id_from_url( $domain, $path = '/' ) {
    353 	$domain = strtolower( $domain );
    354 	$path   = strtolower( $path );
    355 	$id     = wp_cache_get( md5( $domain . $path ), 'blog-id-cache' );
    356 
    357 	if ( -1 == $id ) { // Blog does not exist.
    358 		return 0;
    359 	} elseif ( $id ) {
    360 		return (int) $id;
    361 	}
    362 
    363 	$args   = array(
    364 		'domain'                 => $domain,
    365 		'path'                   => $path,
    366 		'fields'                 => 'ids',
    367 		'number'                 => 1,
    368 		'update_site_meta_cache' => false,
    369 	);
    370 	$result = get_sites( $args );
    371 	$id     = array_shift( $result );
    372 
    373 	if ( ! $id ) {
    374 		wp_cache_set( md5( $domain . $path ), -1, 'blog-id-cache' );
    375 		return 0;
    376 	}
    377 
    378 	wp_cache_set( md5( $domain . $path ), $id, 'blog-id-cache' );
    379 
    380 	return $id;
    381 }
    382 
    383 //
    384 // Admin functions.
    385 //
    386 
    387 /**
    388  * Checks an email address against a list of banned domains.
    389  *
    390  * This function checks against the Banned Email Domains list
    391  * at wp-admin/network/settings.php. The check is only run on
    392  * self-registrations; user creation at wp-admin/network/users.php
    393  * bypasses this check.
    394  *
    395  * @since MU (3.0.0)
    396  *
    397  * @param string $user_email The email provided by the user at registration.
    398  * @return bool True when the email address is banned, false otherwise.
    399  */
    400 function is_email_address_unsafe( $user_email ) {
    401 	$banned_names = get_site_option( 'banned_email_domains' );
    402 	if ( $banned_names && ! is_array( $banned_names ) ) {
    403 		$banned_names = explode( "\n", $banned_names );
    404 	}
    405 
    406 	$is_email_address_unsafe = false;
    407 
    408 	if ( $banned_names && is_array( $banned_names ) && false !== strpos( $user_email, '@', 1 ) ) {
    409 		$banned_names     = array_map( 'strtolower', $banned_names );
    410 		$normalized_email = strtolower( $user_email );
    411 
    412 		list( $email_local_part, $email_domain ) = explode( '@', $normalized_email );
    413 
    414 		foreach ( $banned_names as $banned_domain ) {
    415 			if ( ! $banned_domain ) {
    416 				continue;
    417 			}
    418 
    419 			if ( $email_domain == $banned_domain ) {
    420 				$is_email_address_unsafe = true;
    421 				break;
    422 			}
    423 
    424 			$dotted_domain = ".$banned_domain";
    425 			if ( substr( $normalized_email, -strlen( $dotted_domain ) ) === $dotted_domain ) {
    426 				$is_email_address_unsafe = true;
    427 				break;
    428 			}
    429 		}
    430 	}
    431 
    432 	/**
    433 	 * Filters whether an email address is unsafe.
    434 	 *
    435 	 * @since 3.5.0
    436 	 *
    437 	 * @param bool   $is_email_address_unsafe Whether the email address is "unsafe". Default false.
    438 	 * @param string $user_email              User email address.
    439 	 */
    440 	return apply_filters( 'is_email_address_unsafe', $is_email_address_unsafe, $user_email );
    441 }
    442 
    443 /**
    444  * Sanitize and validate data required for a user sign-up.
    445  *
    446  * Verifies the validity and uniqueness of user names and user email addresses,
    447  * and checks email addresses against allowed and disallowed domains provided by
    448  * administrators.
    449  *
    450  * The {@see 'wpmu_validate_user_signup'} hook provides an easy way to modify the sign-up
    451  * process. The value $result, which is passed to the hook, contains both the user-provided
    452  * info and the error messages created by the function. {@see 'wpmu_validate_user_signup'}
    453  * allows you to process the data in any way you'd like, and unset the relevant errors if
    454  * necessary.
    455  *
    456  * @since MU (3.0.0)
    457  *
    458  * @global wpdb $wpdb WordPress database abstraction object.
    459  *
    460  * @param string $user_name  The login name provided by the user.
    461  * @param string $user_email The email provided by the user.
    462  * @return array {
    463  *     The array of user name, email, and the error messages.
    464  *
    465  *     @type string   $user_name     Sanitized and unique username.
    466  *     @type string   $orig_username Original username.
    467  *     @type string   $user_email    User email address.
    468  *     @type WP_Error $errors        WP_Error object containing any errors found.
    469  * }
    470  */
    471 function wpmu_validate_user_signup( $user_name, $user_email ) {
    472 	global $wpdb;
    473 
    474 	$errors = new WP_Error();
    475 
    476 	$orig_username = $user_name;
    477 	$user_name     = preg_replace( '/\s+/', '', sanitize_user( $user_name, true ) );
    478 
    479 	if ( $user_name != $orig_username || preg_match( '/[^a-z0-9]/', $user_name ) ) {
    480 		$errors->add( 'user_name', __( 'Usernames can only contain lowercase letters (a-z) and numbers.' ) );
    481 		$user_name = $orig_username;
    482 	}
    483 
    484 	$user_email = sanitize_email( $user_email );
    485 
    486 	if ( empty( $user_name ) ) {
    487 		$errors->add( 'user_name', __( 'Please enter a username.' ) );
    488 	}
    489 
    490 	$illegal_names = get_site_option( 'illegal_names' );
    491 	if ( ! is_array( $illegal_names ) ) {
    492 		$illegal_names = array( 'www', 'web', 'root', 'admin', 'main', 'invite', 'administrator' );
    493 		add_site_option( 'illegal_names', $illegal_names );
    494 	}
    495 	if ( in_array( $user_name, $illegal_names, true ) ) {
    496 		$errors->add( 'user_name', __( 'Sorry, that username is not allowed.' ) );
    497 	}
    498 
    499 	/** This filter is documented in wp-includes/user.php */
    500 	$illegal_logins = (array) apply_filters( 'illegal_user_logins', array() );
    501 
    502 	if ( in_array( strtolower( $user_name ), array_map( 'strtolower', $illegal_logins ), true ) ) {
    503 		$errors->add( 'user_name', __( 'Sorry, that username is not allowed.' ) );
    504 	}
    505 
    506 	if ( ! is_email( $user_email ) ) {
    507 		$errors->add( 'user_email', __( 'Please enter a valid email address.' ) );
    508 	} elseif ( is_email_address_unsafe( $user_email ) ) {
    509 		$errors->add( 'user_email', __( 'You cannot use that email address to signup. We are having problems with them blocking some of our email. Please use another email provider.' ) );
    510 	}
    511 
    512 	if ( strlen( $user_name ) < 4 ) {
    513 		$errors->add( 'user_name', __( 'Username must be at least 4 characters.' ) );
    514 	}
    515 
    516 	if ( strlen( $user_name ) > 60 ) {
    517 		$errors->add( 'user_name', __( 'Username may not be longer than 60 characters.' ) );
    518 	}
    519 
    520 	// All numeric?
    521 	if ( preg_match( '/^[0-9]*$/', $user_name ) ) {
    522 		$errors->add( 'user_name', __( 'Sorry, usernames must have letters too!' ) );
    523 	}
    524 
    525 	$limited_email_domains = get_site_option( 'limited_email_domains' );
    526 	if ( is_array( $limited_email_domains ) && ! empty( $limited_email_domains ) ) {
    527 		$limited_email_domains = array_map( 'strtolower', $limited_email_domains );
    528 		$emaildomain           = strtolower( substr( $user_email, 1 + strpos( $user_email, '@' ) ) );
    529 		if ( ! in_array( $emaildomain, $limited_email_domains, true ) ) {
    530 			$errors->add( 'user_email', __( 'Sorry, that email address is not allowed!' ) );
    531 		}
    532 	}
    533 
    534 	// Check if the username has been used already.
    535 	if ( username_exists( $user_name ) ) {
    536 		$errors->add( 'user_name', __( 'Sorry, that username already exists!' ) );
    537 	}
    538 
    539 	// Check if the email address has been used already.
    540 	if ( email_exists( $user_email ) ) {
    541 		$errors->add( 'user_email', __( 'Sorry, that email address is already used!' ) );
    542 	}
    543 
    544 	// Has someone already signed up for this username?
    545 	$signup = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM $wpdb->signups WHERE user_login = %s", $user_name ) );
    546 	if ( $signup instanceof stdClass ) {
    547 		$registered_at = mysql2date( 'U', $signup->registered );
    548 		$now           = time();
    549 		$diff          = $now - $registered_at;
    550 		// If registered more than two days ago, cancel registration and let this signup go through.
    551 		if ( $diff > 2 * DAY_IN_SECONDS ) {
    552 			$wpdb->delete( $wpdb->signups, array( 'user_login' => $user_name ) );
    553 		} else {
    554 			$errors->add( 'user_name', __( 'That username is currently reserved but may be available in a couple of days.' ) );
    555 		}
    556 	}
    557 
    558 	$signup = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM $wpdb->signups WHERE user_email = %s", $user_email ) );
    559 	if ( $signup instanceof stdClass ) {
    560 		$diff = time() - mysql2date( 'U', $signup->registered );
    561 		// If registered more than two days ago, cancel registration and let this signup go through.
    562 		if ( $diff > 2 * DAY_IN_SECONDS ) {
    563 			$wpdb->delete( $wpdb->signups, array( 'user_email' => $user_email ) );
    564 		} else {
    565 			$errors->add( 'user_email', __( 'That email address has already been used. Please check your inbox for an activation email. It will become available in a couple of days if you do nothing.' ) );
    566 		}
    567 	}
    568 
    569 	$result = array(
    570 		'user_name'     => $user_name,
    571 		'orig_username' => $orig_username,
    572 		'user_email'    => $user_email,
    573 		'errors'        => $errors,
    574 	);
    575 
    576 	/**
    577 	 * Filters the validated user registration details.
    578 	 *
    579 	 * This does not allow you to override the username or email of the user during
    580 	 * registration. The values are solely used for validation and error handling.
    581 	 *
    582 	 * @since MU (3.0.0)
    583 	 *
    584 	 * @param array $result {
    585 	 *     The array of user name, email, and the error messages.
    586 	 *
    587 	 *     @type string   $user_name     Sanitized and unique username.
    588 	 *     @type string   $orig_username Original username.
    589 	 *     @type string   $user_email    User email address.
    590 	 *     @type WP_Error $errors        WP_Error object containing any errors found.
    591 	 * }
    592 	 */
    593 	return apply_filters( 'wpmu_validate_user_signup', $result );
    594 }
    595 
    596 /**
    597  * Processes new site registrations.
    598  *
    599  * Checks the data provided by the user during blog signup. Verifies
    600  * the validity and uniqueness of blog paths and domains.
    601  *
    602  * This function prevents the current user from registering a new site
    603  * with a blogname equivalent to another user's login name. Passing the
    604  * $user parameter to the function, where $user is the other user, is
    605  * effectively an override of this limitation.
    606  *
    607  * Filter {@see 'wpmu_validate_blog_signup'} if you want to modify
    608  * the way that WordPress validates new site signups.
    609  *
    610  * @since MU (3.0.0)
    611  *
    612  * @global wpdb   $wpdb   WordPress database abstraction object.
    613  * @global string $domain
    614  *
    615  * @param string         $blogname   The blog name provided by the user. Must be unique.
    616  * @param string         $blog_title The blog title provided by the user.
    617  * @param WP_User|string $user       Optional. The user object to check against the new site name.
    618  * @return array {
    619  *     Array of domain, path, blog name, blog title, user and error messages.
    620  *
    621  *     @type string         $domain     Domain for the site.
    622  *     @type string         $path       Path for the site. Used in subdirectory installations.
    623  *     @type string         $blogname   The unique site name (slug).
    624  *     @type string         $blog_title Blog title.
    625  *     @type string|WP_User $user       By default, an empty string. A user object if provided.
    626  *     @type WP_Error       $errors     WP_Error containing any errors found.
    627  * }
    628  */
    629 function wpmu_validate_blog_signup( $blogname, $blog_title, $user = '' ) {
    630 	global $wpdb, $domain;
    631 
    632 	$current_network = get_network();
    633 	$base            = $current_network->path;
    634 
    635 	$blog_title = strip_tags( $blog_title );
    636 
    637 	$errors        = new WP_Error();
    638 	$illegal_names = get_site_option( 'illegal_names' );
    639 	if ( false == $illegal_names ) {
    640 		$illegal_names = array( 'www', 'web', 'root', 'admin', 'main', 'invite', 'administrator' );
    641 		add_site_option( 'illegal_names', $illegal_names );
    642 	}
    643 
    644 	/*
    645 	 * On sub dir installations, some names are so illegal, only a filter can
    646 	 * spring them from jail.
    647 	 */
    648 	if ( ! is_subdomain_install() ) {
    649 		$illegal_names = array_merge( $illegal_names, get_subdirectory_reserved_names() );
    650 	}
    651 
    652 	if ( empty( $blogname ) ) {
    653 		$errors->add( 'blogname', __( 'Please enter a site name.' ) );
    654 	}
    655 
    656 	if ( preg_match( '/[^a-z0-9]+/', $blogname ) ) {
    657 		$errors->add( 'blogname', __( 'Site names can only contain lowercase letters (a-z) and numbers.' ) );
    658 	}
    659 
    660 	if ( in_array( $blogname, $illegal_names, true ) ) {
    661 		$errors->add( 'blogname', __( 'That name is not allowed.' ) );
    662 	}
    663 
    664 	/**
    665 	 * Filters the minimum site name length required when validating a site signup.
    666 	 *
    667 	 * @since 4.8.0
    668 	 *
    669 	 * @param int $length The minimum site name length. Default 4.
    670 	 */
    671 	$minimum_site_name_length = apply_filters( 'minimum_site_name_length', 4 );
    672 
    673 	if ( strlen( $blogname ) < $minimum_site_name_length ) {
    674 		/* translators: %s: Minimum site name length. */
    675 		$errors->add( 'blogname', sprintf( _n( 'Site name must be at least %s character.', 'Site name must be at least %s characters.', $minimum_site_name_length ), number_format_i18n( $minimum_site_name_length ) ) );
    676 	}
    677 
    678 	// Do not allow users to create a blog that conflicts with a page on the main blog.
    679 	if ( ! is_subdomain_install() && $wpdb->get_var( $wpdb->prepare( 'SELECT post_name FROM ' . $wpdb->get_blog_prefix( $current_network->site_id ) . "posts WHERE post_type = 'page' AND post_name = %s", $blogname ) ) ) {
    680 		$errors->add( 'blogname', __( 'Sorry, you may not use that site name.' ) );
    681 	}
    682 
    683 	// All numeric?
    684 	if ( preg_match( '/^[0-9]*$/', $blogname ) ) {
    685 		$errors->add( 'blogname', __( 'Sorry, site names must have letters too!' ) );
    686 	}
    687 
    688 	/**
    689 	 * Filters the new site name during registration.
    690 	 *
    691 	 * The name is the site's subdomain or the site's subdirectory
    692 	 * path depending on the network settings.
    693 	 *
    694 	 * @since MU (3.0.0)
    695 	 *
    696 	 * @param string $blogname Site name.
    697 	 */
    698 	$blogname = apply_filters( 'newblogname', $blogname );
    699 
    700 	$blog_title = wp_unslash( $blog_title );
    701 
    702 	if ( empty( $blog_title ) ) {
    703 		$errors->add( 'blog_title', __( 'Please enter a site title.' ) );
    704 	}
    705 
    706 	// Check if the domain/path has been used already.
    707 	if ( is_subdomain_install() ) {
    708 		$mydomain = $blogname . '.' . preg_replace( '|^www\.|', '', $domain );
    709 		$path     = $base;
    710 	} else {
    711 		$mydomain = $domain;
    712 		$path     = $base . $blogname . '/';
    713 	}
    714 	if ( domain_exists( $mydomain, $path, $current_network->id ) ) {
    715 		$errors->add( 'blogname', __( 'Sorry, that site already exists!' ) );
    716 	}
    717 
    718 	if ( username_exists( $blogname ) ) {
    719 		if ( ! is_object( $user ) || ( is_object( $user ) && ( $user->user_login != $blogname ) ) ) {
    720 			$errors->add( 'blogname', __( 'Sorry, that site is reserved!' ) );
    721 		}
    722 	}
    723 
    724 	// Has someone already signed up for this domain?
    725 	// TODO: Check email too?
    726 	$signup = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM $wpdb->signups WHERE domain = %s AND path = %s", $mydomain, $path ) );
    727 	if ( $signup instanceof stdClass ) {
    728 		$diff = time() - mysql2date( 'U', $signup->registered );
    729 		// If registered more than two days ago, cancel registration and let this signup go through.
    730 		if ( $diff > 2 * DAY_IN_SECONDS ) {
    731 			$wpdb->delete(
    732 				$wpdb->signups,
    733 				array(
    734 					'domain' => $mydomain,
    735 					'path'   => $path,
    736 				)
    737 			);
    738 		} else {
    739 			$errors->add( 'blogname', __( 'That site is currently reserved but may be available in a couple days.' ) );
    740 		}
    741 	}
    742 
    743 	$result = array(
    744 		'domain'     => $mydomain,
    745 		'path'       => $path,
    746 		'blogname'   => $blogname,
    747 		'blog_title' => $blog_title,
    748 		'user'       => $user,
    749 		'errors'     => $errors,
    750 	);
    751 
    752 	/**
    753 	 * Filters site details and error messages following registration.
    754 	 *
    755 	 * @since MU (3.0.0)
    756 	 *
    757 	 * @param array $result {
    758 	 *     Array of domain, path, blog name, blog title, user and error messages.
    759 	 *
    760 	 *     @type string         $domain     Domain for the site.
    761 	 *     @type string         $path       Path for the site. Used in subdirectory installations.
    762 	 *     @type string         $blogname   The unique site name (slug).
    763 	 *     @type string         $blog_title Blog title.
    764 	 *     @type string|WP_User $user       By default, an empty string. A user object if provided.
    765 	 *     @type WP_Error       $errors     WP_Error containing any errors found.
    766 	 * }
    767 	 */
    768 	return apply_filters( 'wpmu_validate_blog_signup', $result );
    769 }
    770 
    771 /**
    772  * Record site signup information for future activation.
    773  *
    774  * @since MU (3.0.0)
    775  *
    776  * @global wpdb $wpdb WordPress database abstraction object.
    777  *
    778  * @param string $domain     The requested domain.
    779  * @param string $path       The requested path.
    780  * @param string $title      The requested site title.
    781  * @param string $user       The user's requested login name.
    782  * @param string $user_email The user's email address.
    783  * @param array  $meta       Optional. Signup meta data. By default, contains the requested privacy setting and lang_id.
    784  */
    785 function wpmu_signup_blog( $domain, $path, $title, $user, $user_email, $meta = array() ) {
    786 	global $wpdb;
    787 
    788 	$key = substr( md5( time() . wp_rand() . $domain ), 0, 16 );
    789 
    790 	/**
    791 	 * Filters the metadata for a site signup.
    792 	 *
    793 	 * The metadata will be serialized prior to storing it in the database.
    794 	 *
    795 	 * @since 4.8.0
    796 	 *
    797 	 * @param array  $meta       Signup meta data. Default empty array.
    798 	 * @param string $domain     The requested domain.
    799 	 * @param string $path       The requested path.
    800 	 * @param string $title      The requested site title.
    801 	 * @param string $user       The user's requested login name.
    802 	 * @param string $user_email The user's email address.
    803 	 * @param string $key        The user's activation key.
    804 	 */
    805 	$meta = apply_filters( 'signup_site_meta', $meta, $domain, $path, $title, $user, $user_email, $key );
    806 
    807 	$wpdb->insert(
    808 		$wpdb->signups,
    809 		array(
    810 			'domain'         => $domain,
    811 			'path'           => $path,
    812 			'title'          => $title,
    813 			'user_login'     => $user,
    814 			'user_email'     => $user_email,
    815 			'registered'     => current_time( 'mysql', true ),
    816 			'activation_key' => $key,
    817 			'meta'           => serialize( $meta ),
    818 		)
    819 	);
    820 
    821 	/**
    822 	 * Fires after site signup information has been written to the database.
    823 	 *
    824 	 * @since 4.4.0
    825 	 *
    826 	 * @param string $domain     The requested domain.
    827 	 * @param string $path       The requested path.
    828 	 * @param string $title      The requested site title.
    829 	 * @param string $user       The user's requested login name.
    830 	 * @param string $user_email The user's email address.
    831 	 * @param string $key        The user's activation key.
    832 	 * @param array  $meta       Signup meta data. By default, contains the requested privacy setting and lang_id.
    833 	 */
    834 	do_action( 'after_signup_site', $domain, $path, $title, $user, $user_email, $key, $meta );
    835 }
    836 
    837 /**
    838  * Record user signup information for future activation.
    839  *
    840  * This function is used when user registration is open but
    841  * new site registration is not.
    842  *
    843  * @since MU (3.0.0)
    844  *
    845  * @global wpdb $wpdb WordPress database abstraction object.
    846  *
    847  * @param string $user       The user's requested login name.
    848  * @param string $user_email The user's email address.
    849  * @param array  $meta       Optional. Signup meta data. Default empty array.
    850  */
    851 function wpmu_signup_user( $user, $user_email, $meta = array() ) {
    852 	global $wpdb;
    853 
    854 	// Format data.
    855 	$user       = preg_replace( '/\s+/', '', sanitize_user( $user, true ) );
    856 	$user_email = sanitize_email( $user_email );
    857 	$key        = substr( md5( time() . wp_rand() . $user_email ), 0, 16 );
    858 
    859 	/**
    860 	 * Filters the metadata for a user signup.
    861 	 *
    862 	 * The metadata will be serialized prior to storing it in the database.
    863 	 *
    864 	 * @since 4.8.0
    865 	 *
    866 	 * @param array  $meta       Signup meta data. Default empty array.
    867 	 * @param string $user       The user's requested login name.
    868 	 * @param string $user_email The user's email address.
    869 	 * @param string $key        The user's activation key.
    870 	 */
    871 	$meta = apply_filters( 'signup_user_meta', $meta, $user, $user_email, $key );
    872 
    873 	$wpdb->insert(
    874 		$wpdb->signups,
    875 		array(
    876 			'domain'         => '',
    877 			'path'           => '',
    878 			'title'          => '',
    879 			'user_login'     => $user,
    880 			'user_email'     => $user_email,
    881 			'registered'     => current_time( 'mysql', true ),
    882 			'activation_key' => $key,
    883 			'meta'           => serialize( $meta ),
    884 		)
    885 	);
    886 
    887 	/**
    888 	 * Fires after a user's signup information has been written to the database.
    889 	 *
    890 	 * @since 4.4.0
    891 	 *
    892 	 * @param string $user       The user's requested login name.
    893 	 * @param string $user_email The user's email address.
    894 	 * @param string $key        The user's activation key.
    895 	 * @param array  $meta       Signup meta data. Default empty array.
    896 	 */
    897 	do_action( 'after_signup_user', $user, $user_email, $key, $meta );
    898 }
    899 
    900 /**
    901  * Send a confirmation request email to a user when they sign up for a new site. The new site will not become active
    902  * until the confirmation link is clicked.
    903  *
    904  * This is the notification function used when site registration
    905  * is enabled.
    906  *
    907  * Filter {@see 'wpmu_signup_blog_notification'} to bypass this function or
    908  * replace it with your own notification behavior.
    909  *
    910  * Filter {@see 'wpmu_signup_blog_notification_email'} and
    911  * {@see 'wpmu_signup_blog_notification_subject'} to change the content
    912  * and subject line of the email sent to newly registered users.
    913  *
    914  * @since MU (3.0.0)
    915  *
    916  * @param string $domain     The new blog domain.
    917  * @param string $path       The new blog path.
    918  * @param string $title      The site title.
    919  * @param string $user_login The user's login name.
    920  * @param string $user_email The user's email address.
    921  * @param string $key        The activation key created in wpmu_signup_blog()
    922  * @param array  $meta       Optional. Signup meta data. By default, contains the requested privacy setting and lang_id.
    923  * @return bool
    924  */
    925 function wpmu_signup_blog_notification( $domain, $path, $title, $user_login, $user_email, $key, $meta = array() ) {
    926 	/**
    927 	 * Filters whether to bypass the new site email notification.
    928 	 *
    929 	 * @since MU (3.0.0)
    930 	 *
    931 	 * @param string|false $domain     Site domain, or false to prevent the email from sending.
    932 	 * @param string       $path       Site path.
    933 	 * @param string       $title      Site title.
    934 	 * @param string       $user_login User login name.
    935 	 * @param string       $user_email User email address.
    936 	 * @param string       $key        Activation key created in wpmu_signup_blog().
    937 	 * @param array        $meta       Signup meta data. By default, contains the requested privacy setting and lang_id.
    938 	 */
    939 	if ( ! apply_filters( 'wpmu_signup_blog_notification', $domain, $path, $title, $user_login, $user_email, $key, $meta ) ) {
    940 		return false;
    941 	}
    942 
    943 	// Send email with activation link.
    944 	if ( ! is_subdomain_install() || get_current_network_id() != 1 ) {
    945 		$activate_url = network_site_url( "wp-activate.php?key=$key" );
    946 	} else {
    947 		$activate_url = "http://{$domain}{$path}wp-activate.php?key=$key"; // @todo Use *_url() API.
    948 	}
    949 
    950 	$activate_url = esc_url( $activate_url );
    951 
    952 	$admin_email = get_site_option( 'admin_email' );
    953 
    954 	if ( '' === $admin_email ) {
    955 		$admin_email = 'support@' . wp_parse_url( network_home_url(), PHP_URL_HOST );
    956 	}
    957 
    958 	$from_name       = ( '' !== get_site_option( 'site_name' ) ) ? esc_html( get_site_option( 'site_name' ) ) : 'WordPress';
    959 	$message_headers = "From: \"{$from_name}\" <{$admin_email}>\n" . 'Content-Type: text/plain; charset="' . get_option( 'blog_charset' ) . "\"\n";
    960 
    961 	$user            = get_user_by( 'login', $user_login );
    962 	$switched_locale = switch_to_locale( get_user_locale( $user ) );
    963 
    964 	$message = sprintf(
    965 		/**
    966 		 * Filters the message content of the new blog notification email.
    967 		 *
    968 		 * Content should be formatted for transmission via wp_mail().
    969 		 *
    970 		 * @since MU (3.0.0)
    971 		 *
    972 		 * @param string $content    Content of the notification email.
    973 		 * @param string $domain     Site domain.
    974 		 * @param string $path       Site path.
    975 		 * @param string $title      Site title.
    976 		 * @param string $user_login User login name.
    977 		 * @param string $user_email User email address.
    978 		 * @param string $key        Activation key created in wpmu_signup_blog().
    979 		 * @param array  $meta       Signup meta data. By default, contains the requested privacy setting and lang_id.
    980 		 */
    981 		apply_filters(
    982 			'wpmu_signup_blog_notification_email',
    983 			/* translators: New site notification email. 1: Activation URL, 2: New site URL. */
    984 			__( "To activate your blog, please click the following link:\n\n%1\$s\n\nAfter you activate, you will receive *another email* with your login.\n\nAfter you activate, you can visit your site here:\n\n%2\$s" ),
    985 			$domain,
    986 			$path,
    987 			$title,
    988 			$user_login,
    989 			$user_email,
    990 			$key,
    991 			$meta
    992 		),
    993 		$activate_url,
    994 		esc_url( "http://{$domain}{$path}" ),
    995 		$key
    996 	);
    997 
    998 	$subject = sprintf(
    999 		/**
   1000 		 * Filters the subject of the new blog notification email.
   1001 		 *
   1002 		 * @since MU (3.0.0)
   1003 		 *
   1004 		 * @param string $subject    Subject of the notification email.
   1005 		 * @param string $domain     Site domain.
   1006 		 * @param string $path       Site path.
   1007 		 * @param string $title      Site title.
   1008 		 * @param string $user_login User login name.
   1009 		 * @param string $user_email User email address.
   1010 		 * @param string $key        Activation key created in wpmu_signup_blog().
   1011 		 * @param array  $meta       Signup meta data. By default, contains the requested privacy setting and lang_id.
   1012 		 */
   1013 		apply_filters(
   1014 			'wpmu_signup_blog_notification_subject',
   1015 			/* translators: New site notification email subject. 1: Network title, 2: New site URL. */
   1016 			_x( '[%1$s] Activate %2$s', 'New site notification email subject' ),
   1017 			$domain,
   1018 			$path,
   1019 			$title,
   1020 			$user_login,
   1021 			$user_email,
   1022 			$key,
   1023 			$meta
   1024 		),
   1025 		$from_name,
   1026 		esc_url( 'http://' . $domain . $path )
   1027 	);
   1028 
   1029 	wp_mail( $user_email, wp_specialchars_decode( $subject ), $message, $message_headers );
   1030 
   1031 	if ( $switched_locale ) {
   1032 		restore_previous_locale();
   1033 	}
   1034 
   1035 	return true;
   1036 }
   1037 
   1038 /**
   1039  * Send a confirmation request email to a user when they sign up for a new user account (without signing up for a site
   1040  * at the same time). The user account will not become active until the confirmation link is clicked.
   1041  *
   1042  * This is the notification function used when no new site has
   1043  * been requested.
   1044  *
   1045  * Filter {@see 'wpmu_signup_user_notification'} to bypass this function or
   1046  * replace it with your own notification behavior.
   1047  *
   1048  * Filter {@see 'wpmu_signup_user_notification_email'} and
   1049  * {@see 'wpmu_signup_user_notification_subject'} to change the content
   1050  * and subject line of the email sent to newly registered users.
   1051  *
   1052  * @since MU (3.0.0)
   1053  *
   1054  * @param string $user_login The user's login name.
   1055  * @param string $user_email The user's email address.
   1056  * @param string $key        The activation key created in wpmu_signup_user()
   1057  * @param array  $meta       Optional. Signup meta data. Default empty array.
   1058  * @return bool
   1059  */
   1060 function wpmu_signup_user_notification( $user_login, $user_email, $key, $meta = array() ) {
   1061 	/**
   1062 	 * Filters whether to bypass the email notification for new user sign-up.
   1063 	 *
   1064 	 * @since MU (3.0.0)
   1065 	 *
   1066 	 * @param string $user_login User login name.
   1067 	 * @param string $user_email User email address.
   1068 	 * @param string $key        Activation key created in wpmu_signup_user().
   1069 	 * @param array  $meta       Signup meta data. Default empty array.
   1070 	 */
   1071 	if ( ! apply_filters( 'wpmu_signup_user_notification', $user_login, $user_email, $key, $meta ) ) {
   1072 		return false;
   1073 	}
   1074 
   1075 	$user            = get_user_by( 'login', $user_login );
   1076 	$switched_locale = switch_to_locale( get_user_locale( $user ) );
   1077 
   1078 	// Send email with activation link.
   1079 	$admin_email = get_site_option( 'admin_email' );
   1080 
   1081 	if ( '' === $admin_email ) {
   1082 		$admin_email = 'support@' . wp_parse_url( network_home_url(), PHP_URL_HOST );
   1083 	}
   1084 
   1085 	$from_name       = ( '' !== get_site_option( 'site_name' ) ) ? esc_html( get_site_option( 'site_name' ) ) : 'WordPress';
   1086 	$message_headers = "From: \"{$from_name}\" <{$admin_email}>\n" . 'Content-Type: text/plain; charset="' . get_option( 'blog_charset' ) . "\"\n";
   1087 	$message         = sprintf(
   1088 		/**
   1089 		 * Filters the content of the notification email for new user sign-up.
   1090 		 *
   1091 		 * Content should be formatted for transmission via wp_mail().
   1092 		 *
   1093 		 * @since MU (3.0.0)
   1094 		 *
   1095 		 * @param string $content    Content of the notification email.
   1096 		 * @param string $user_login User login name.
   1097 		 * @param string $user_email User email address.
   1098 		 * @param string $key        Activation key created in wpmu_signup_user().
   1099 		 * @param array  $meta       Signup meta data. Default empty array.
   1100 		 */
   1101 		apply_filters(
   1102 			'wpmu_signup_user_notification_email',
   1103 			/* translators: New user notification email. %s: Activation URL. */
   1104 			__( "To activate your user, please click the following link:\n\n%s\n\nAfter you activate, you will receive *another email* with your login." ),
   1105 			$user_login,
   1106 			$user_email,
   1107 			$key,
   1108 			$meta
   1109 		),
   1110 		site_url( "wp-activate.php?key=$key" )
   1111 	);
   1112 
   1113 	$subject = sprintf(
   1114 		/**
   1115 		 * Filters the subject of the notification email of new user signup.
   1116 		 *
   1117 		 * @since MU (3.0.0)
   1118 		 *
   1119 		 * @param string $subject    Subject of the notification email.
   1120 		 * @param string $user_login User login name.
   1121 		 * @param string $user_email User email address.
   1122 		 * @param string $key        Activation key created in wpmu_signup_user().
   1123 		 * @param array  $meta       Signup meta data. Default empty array.
   1124 		 */
   1125 		apply_filters(
   1126 			'wpmu_signup_user_notification_subject',
   1127 			/* translators: New user notification email subject. 1: Network title, 2: New user login. */
   1128 			_x( '[%1$s] Activate %2$s', 'New user notification email subject' ),
   1129 			$user_login,
   1130 			$user_email,
   1131 			$key,
   1132 			$meta
   1133 		),
   1134 		$from_name,
   1135 		$user_login
   1136 	);
   1137 
   1138 	wp_mail( $user_email, wp_specialchars_decode( $subject ), $message, $message_headers );
   1139 
   1140 	if ( $switched_locale ) {
   1141 		restore_previous_locale();
   1142 	}
   1143 
   1144 	return true;
   1145 }
   1146 
   1147 /**
   1148  * Activate a signup.
   1149  *
   1150  * Hook to {@see 'wpmu_activate_user'} or {@see 'wpmu_activate_blog'} for events
   1151  * that should happen only when users or sites are self-created (since
   1152  * those actions are not called when users and sites are created
   1153  * by a Super Admin).
   1154  *
   1155  * @since MU (3.0.0)
   1156  *
   1157  * @global wpdb $wpdb WordPress database abstraction object.
   1158  *
   1159  * @param string $key The activation key provided to the user.
   1160  * @return array|WP_Error An array containing information about the activated user and/or blog
   1161  */
   1162 function wpmu_activate_signup( $key ) {
   1163 	global $wpdb;
   1164 
   1165 	$signup = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM $wpdb->signups WHERE activation_key = %s", $key ) );
   1166 
   1167 	if ( empty( $signup ) ) {
   1168 		return new WP_Error( 'invalid_key', __( 'Invalid activation key.' ) );
   1169 	}
   1170 
   1171 	if ( $signup->active ) {
   1172 		if ( empty( $signup->domain ) ) {
   1173 			return new WP_Error( 'already_active', __( 'The user is already active.' ), $signup );
   1174 		} else {
   1175 			return new WP_Error( 'already_active', __( 'The site is already active.' ), $signup );
   1176 		}
   1177 	}
   1178 
   1179 	$meta     = maybe_unserialize( $signup->meta );
   1180 	$password = wp_generate_password( 12, false );
   1181 
   1182 	$user_id = username_exists( $signup->user_login );
   1183 
   1184 	if ( ! $user_id ) {
   1185 		$user_id = wpmu_create_user( $signup->user_login, $password, $signup->user_email );
   1186 	} else {
   1187 		$user_already_exists = true;
   1188 	}
   1189 
   1190 	if ( ! $user_id ) {
   1191 		return new WP_Error( 'create_user', __( 'Could not create user' ), $signup );
   1192 	}
   1193 
   1194 	$now = current_time( 'mysql', true );
   1195 
   1196 	if ( empty( $signup->domain ) ) {
   1197 		$wpdb->update(
   1198 			$wpdb->signups,
   1199 			array(
   1200 				'active'    => 1,
   1201 				'activated' => $now,
   1202 			),
   1203 			array( 'activation_key' => $key )
   1204 		);
   1205 
   1206 		if ( isset( $user_already_exists ) ) {
   1207 			return new WP_Error( 'user_already_exists', __( 'That username is already activated.' ), $signup );
   1208 		}
   1209 
   1210 		/**
   1211 		 * Fires immediately after a new user is activated.
   1212 		 *
   1213 		 * @since MU (3.0.0)
   1214 		 *
   1215 		 * @param int    $user_id  User ID.
   1216 		 * @param string $password User password.
   1217 		 * @param array  $meta     Signup meta data.
   1218 		 */
   1219 		do_action( 'wpmu_activate_user', $user_id, $password, $meta );
   1220 
   1221 		return array(
   1222 			'user_id'  => $user_id,
   1223 			'password' => $password,
   1224 			'meta'     => $meta,
   1225 		);
   1226 	}
   1227 
   1228 	$blog_id = wpmu_create_blog( $signup->domain, $signup->path, $signup->title, $user_id, $meta, get_current_network_id() );
   1229 
   1230 	// TODO: What to do if we create a user but cannot create a blog?
   1231 	if ( is_wp_error( $blog_id ) ) {
   1232 		/*
   1233 		 * If blog is taken, that means a previous attempt to activate this blog
   1234 		 * failed in between creating the blog and setting the activation flag.
   1235 		 * Let's just set the active flag and instruct the user to reset their password.
   1236 		 */
   1237 		if ( 'blog_taken' === $blog_id->get_error_code() ) {
   1238 			$blog_id->add_data( $signup );
   1239 			$wpdb->update(
   1240 				$wpdb->signups,
   1241 				array(
   1242 					'active'    => 1,
   1243 					'activated' => $now,
   1244 				),
   1245 				array( 'activation_key' => $key )
   1246 			);
   1247 		}
   1248 		return $blog_id;
   1249 	}
   1250 
   1251 	$wpdb->update(
   1252 		$wpdb->signups,
   1253 		array(
   1254 			'active'    => 1,
   1255 			'activated' => $now,
   1256 		),
   1257 		array( 'activation_key' => $key )
   1258 	);
   1259 
   1260 	/**
   1261 	 * Fires immediately after a site is activated.
   1262 	 *
   1263 	 * @since MU (3.0.0)
   1264 	 *
   1265 	 * @param int    $blog_id       Blog ID.
   1266 	 * @param int    $user_id       User ID.
   1267 	 * @param int    $password      User password.
   1268 	 * @param string $signup_title  Site title.
   1269 	 * @param array  $meta          Signup meta data. By default, contains the requested privacy setting and lang_id.
   1270 	 */
   1271 	do_action( 'wpmu_activate_blog', $blog_id, $user_id, $password, $signup->title, $meta );
   1272 
   1273 	return array(
   1274 		'blog_id'  => $blog_id,
   1275 		'user_id'  => $user_id,
   1276 		'password' => $password,
   1277 		'title'    => $signup->title,
   1278 		'meta'     => $meta,
   1279 	);
   1280 }
   1281 
   1282 /**
   1283  * Deletes am associated signup entry when a user is deleted from the database.
   1284  *
   1285  * @since 5.5.0
   1286  *
   1287  * @param int      $id       ID of the user to delete.
   1288  * @param int|null $reassign ID of the user to reassign posts and links to.
   1289  * @param WP_User  $user     User object.
   1290  */
   1291 function wp_delete_signup_on_user_delete( $id, $reassign, $user ) {
   1292 	global $wpdb;
   1293 
   1294 	$wpdb->delete( $wpdb->signups, array( 'user_login' => $user->user_login ) );
   1295 }
   1296 
   1297 /**
   1298  * Create a user.
   1299  *
   1300  * This function runs when a user self-registers as well as when
   1301  * a Super Admin creates a new user. Hook to {@see 'wpmu_new_user'} for events
   1302  * that should affect all new users, but only on Multisite (otherwise
   1303  * use {@see'user_register'}).
   1304  *
   1305  * @since MU (3.0.0)
   1306  *
   1307  * @param string $user_name The new user's login name.
   1308  * @param string $password  The new user's password.
   1309  * @param string $email     The new user's email address.
   1310  * @return int|false Returns false on failure, or int $user_id on success
   1311  */
   1312 function wpmu_create_user( $user_name, $password, $email ) {
   1313 	$user_name = preg_replace( '/\s+/', '', sanitize_user( $user_name, true ) );
   1314 
   1315 	$user_id = wp_create_user( $user_name, $password, $email );
   1316 	if ( is_wp_error( $user_id ) ) {
   1317 		return false;
   1318 	}
   1319 
   1320 	// Newly created users have no roles or caps until they are added to a blog.
   1321 	delete_user_option( $user_id, 'capabilities' );
   1322 	delete_user_option( $user_id, 'user_level' );
   1323 
   1324 	/**
   1325 	 * Fires immediately after a new user is created.
   1326 	 *
   1327 	 * @since MU (3.0.0)
   1328 	 *
   1329 	 * @param int $user_id User ID.
   1330 	 */
   1331 	do_action( 'wpmu_new_user', $user_id );
   1332 
   1333 	return $user_id;
   1334 }
   1335 
   1336 /**
   1337  * Create a site.
   1338  *
   1339  * This function runs when a user self-registers a new site as well
   1340  * as when a Super Admin creates a new site. Hook to {@see 'wpmu_new_blog'}
   1341  * for events that should affect all new sites.
   1342  *
   1343  * On subdirectory installations, $domain is the same as the main site's
   1344  * domain, and the path is the subdirectory name (eg 'example.com'
   1345  * and '/blog1/'). On subdomain installations, $domain is the new subdomain +
   1346  * root domain (eg 'blog1.example.com'), and $path is '/'.
   1347  *
   1348  * @since MU (3.0.0)
   1349  *
   1350  * @param string $domain     The new site's domain.
   1351  * @param string $path       The new site's path.
   1352  * @param string $title      The new site's title.
   1353  * @param int    $user_id    The user ID of the new site's admin.
   1354  * @param array  $options    Optional. Array of key=>value pairs used to set initial site options.
   1355  *                           If valid status keys are included ('public', 'archived', 'mature',
   1356  *                           'spam', 'deleted', or 'lang_id') the given site status(es) will be
   1357  *                           updated. Otherwise, keys and values will be used to set options for
   1358  *                           the new site. Default empty array.
   1359  * @param int    $network_id Optional. Network ID. Only relevant on multi-network installations.
   1360  * @return int|WP_Error Returns WP_Error object on failure, the new site ID on success.
   1361  */
   1362 function wpmu_create_blog( $domain, $path, $title, $user_id, $options = array(), $network_id = 1 ) {
   1363 	$defaults = array(
   1364 		'public' => 0,
   1365 	);
   1366 	$options  = wp_parse_args( $options, $defaults );
   1367 
   1368 	$title   = strip_tags( $title );
   1369 	$user_id = (int) $user_id;
   1370 
   1371 	// Check if the domain has been used already. We should return an error message.
   1372 	if ( domain_exists( $domain, $path, $network_id ) ) {
   1373 		return new WP_Error( 'blog_taken', __( 'Sorry, that site already exists!' ) );
   1374 	}
   1375 
   1376 	if ( ! wp_installing() ) {
   1377 		wp_installing( true );
   1378 	}
   1379 
   1380 	$allowed_data_fields = array( 'public', 'archived', 'mature', 'spam', 'deleted', 'lang_id' );
   1381 
   1382 	$site_data = array_merge(
   1383 		array(
   1384 			'domain'     => $domain,
   1385 			'path'       => $path,
   1386 			'network_id' => $network_id,
   1387 		),
   1388 		array_intersect_key( $options, array_flip( $allowed_data_fields ) )
   1389 	);
   1390 
   1391 	// Data to pass to wp_initialize_site().
   1392 	$site_initialization_data = array(
   1393 		'title'   => $title,
   1394 		'user_id' => $user_id,
   1395 		'options' => array_diff_key( $options, array_flip( $allowed_data_fields ) ),
   1396 	);
   1397 
   1398 	$blog_id = wp_insert_site( array_merge( $site_data, $site_initialization_data ) );
   1399 
   1400 	if ( is_wp_error( $blog_id ) ) {
   1401 		return $blog_id;
   1402 	}
   1403 
   1404 	wp_cache_set( 'last_changed', microtime(), 'sites' );
   1405 
   1406 	return $blog_id;
   1407 }
   1408 
   1409 /**
   1410  * Notifies the network admin that a new site has been activated.
   1411  *
   1412  * Filter {@see 'newblog_notify_siteadmin'} to change the content of
   1413  * the notification email.
   1414  *
   1415  * @since MU (3.0.0)
   1416  * @since 5.1.0 $blog_id now supports input from the {@see 'wp_initialize_site'} action.
   1417  *
   1418  * @param WP_Site|int $blog_id    The new site's object or ID.
   1419  * @param string      $deprecated Not used.
   1420  * @return bool
   1421  */
   1422 function newblog_notify_siteadmin( $blog_id, $deprecated = '' ) {
   1423 	if ( is_object( $blog_id ) ) {
   1424 		$blog_id = $blog_id->blog_id;
   1425 	}
   1426 
   1427 	if ( 'yes' !== get_site_option( 'registrationnotification' ) ) {
   1428 		return false;
   1429 	}
   1430 
   1431 	$email = get_site_option( 'admin_email' );
   1432 
   1433 	if ( is_email( $email ) == false ) {
   1434 		return false;
   1435 	}
   1436 
   1437 	$options_site_url = esc_url( network_admin_url( 'settings.php' ) );
   1438 
   1439 	switch_to_blog( $blog_id );
   1440 	$blogname = get_option( 'blogname' );
   1441 	$siteurl  = site_url();
   1442 	restore_current_blog();
   1443 
   1444 	$msg = sprintf(
   1445 		/* translators: New site notification email. 1: Site URL, 2: User IP address, 3: URL to Network Settings screen. */
   1446 		__(
   1447 			'New Site: %1$s
   1448 URL: %2$s
   1449 Remote IP address: %3$s
   1450 
   1451 Disable these notifications: %4$s'
   1452 		),
   1453 		$blogname,
   1454 		$siteurl,
   1455 		wp_unslash( $_SERVER['REMOTE_ADDR'] ),
   1456 		$options_site_url
   1457 	);
   1458 	/**
   1459 	 * Filters the message body of the new site activation email sent
   1460 	 * to the network administrator.
   1461 	 *
   1462 	 * @since MU (3.0.0)
   1463 	 * @since 5.4.0 The `$blog_id` parameter was added.
   1464 	 *
   1465 	 * @param string $msg     Email body.
   1466 	 * @param int    $blog_id The new site's ID.
   1467 	 */
   1468 	$msg = apply_filters( 'newblog_notify_siteadmin', $msg, $blog_id );
   1469 
   1470 	/* translators: New site notification email subject. %s: New site URL. */
   1471 	wp_mail( $email, sprintf( __( 'New Site Registration: %s' ), $siteurl ), $msg );
   1472 
   1473 	return true;
   1474 }
   1475 
   1476 /**
   1477  * Notifies the network admin that a new user has been activated.
   1478  *
   1479  * Filter {@see 'newuser_notify_siteadmin'} to change the content of
   1480  * the notification email.
   1481  *
   1482  * @since MU (3.0.0)
   1483  *
   1484  * @param int $user_id The new user's ID.
   1485  * @return bool
   1486  */
   1487 function newuser_notify_siteadmin( $user_id ) {
   1488 	if ( 'yes' !== get_site_option( 'registrationnotification' ) ) {
   1489 		return false;
   1490 	}
   1491 
   1492 	$email = get_site_option( 'admin_email' );
   1493 
   1494 	if ( is_email( $email ) == false ) {
   1495 		return false;
   1496 	}
   1497 
   1498 	$user = get_userdata( $user_id );
   1499 
   1500 	$options_site_url = esc_url( network_admin_url( 'settings.php' ) );
   1501 
   1502 	$msg = sprintf(
   1503 		/* translators: New user notification email. 1: User login, 2: User IP address, 3: URL to Network Settings screen. */
   1504 		__(
   1505 			'New User: %1$s
   1506 Remote IP address: %2$s
   1507 
   1508 Disable these notifications: %3$s'
   1509 		),
   1510 		$user->user_login,
   1511 		wp_unslash( $_SERVER['REMOTE_ADDR'] ),
   1512 		$options_site_url
   1513 	);
   1514 
   1515 	/**
   1516 	 * Filters the message body of the new user activation email sent
   1517 	 * to the network administrator.
   1518 	 *
   1519 	 * @since MU (3.0.0)
   1520 	 *
   1521 	 * @param string  $msg  Email body.
   1522 	 * @param WP_User $user WP_User instance of the new user.
   1523 	 */
   1524 	$msg = apply_filters( 'newuser_notify_siteadmin', $msg, $user );
   1525 
   1526 	/* translators: New user notification email subject. %s: User login. */
   1527 	wp_mail( $email, sprintf( __( 'New User Registration: %s' ), $user->user_login ), $msg );
   1528 
   1529 	return true;
   1530 }
   1531 
   1532 /**
   1533  * Checks whether a site name is already taken.
   1534  *
   1535  * The name is the site's subdomain or the site's subdirectory
   1536  * path depending on the network settings.
   1537  *
   1538  * Used during the new site registration process to ensure
   1539  * that each site name is unique.
   1540  *
   1541  * @since MU (3.0.0)
   1542  *
   1543  * @param string $domain     The domain to be checked.
   1544  * @param string $path       The path to be checked.
   1545  * @param int    $network_id Optional. Network ID. Relevant only on multi-network installations.
   1546  * @return int|null The site ID if the site name exists, null otherwise.
   1547  */
   1548 function domain_exists( $domain, $path, $network_id = 1 ) {
   1549 	$path   = trailingslashit( $path );
   1550 	$args   = array(
   1551 		'network_id'             => $network_id,
   1552 		'domain'                 => $domain,
   1553 		'path'                   => $path,
   1554 		'fields'                 => 'ids',
   1555 		'number'                 => 1,
   1556 		'update_site_meta_cache' => false,
   1557 	);
   1558 	$result = get_sites( $args );
   1559 	$result = array_shift( $result );
   1560 
   1561 	/**
   1562 	 * Filters whether a site name is taken.
   1563 	 *
   1564 	 * The name is the site's subdomain or the site's subdirectory
   1565 	 * path depending on the network settings.
   1566 	 *
   1567 	 * @since 3.5.0
   1568 	 *
   1569 	 * @param int|null $result     The site ID if the site name exists, null otherwise.
   1570 	 * @param string   $domain     Domain to be checked.
   1571 	 * @param string   $path       Path to be checked.
   1572 	 * @param int      $network_id Network ID. Relevant only on multi-network installations.
   1573 	 */
   1574 	return apply_filters( 'domain_exists', $result, $domain, $path, $network_id );
   1575 }
   1576 
   1577 /**
   1578  * Notifies the site administrator that their site activation was successful.
   1579  *
   1580  * Filter {@see 'wpmu_welcome_notification'} to disable or bypass.
   1581  *
   1582  * Filter {@see 'update_welcome_email'} and {@see 'update_welcome_subject'} to
   1583  * modify the content and subject line of the notification email.
   1584  *
   1585  * @since MU (3.0.0)
   1586  *
   1587  * @param int    $blog_id  Site ID.
   1588  * @param int    $user_id  User ID.
   1589  * @param string $password User password, or "N/A" if the user account is not new.
   1590  * @param string $title    Site title.
   1591  * @param array  $meta     Optional. Signup meta data. By default, contains the requested privacy setting and lang_id.
   1592  * @return bool Whether the email notification was sent.
   1593  */
   1594 function wpmu_welcome_notification( $blog_id, $user_id, $password, $title, $meta = array() ) {
   1595 	$current_network = get_network();
   1596 
   1597 	/**
   1598 	 * Filters whether to bypass the welcome email sent to the site administrator after site activation.
   1599 	 *
   1600 	 * Returning false disables the welcome email.
   1601 	 *
   1602 	 * @since MU (3.0.0)
   1603 	 *
   1604 	 * @param int|false $blog_id  Site ID, or false to prevent the email from sending.
   1605 	 * @param int       $user_id  User ID of the site administrator.
   1606 	 * @param string    $password User password, or "N/A" if the user account is not new.
   1607 	 * @param string    $title    Site title.
   1608 	 * @param array     $meta     Signup meta data. By default, contains the requested privacy setting and lang_id.
   1609 	 */
   1610 	if ( ! apply_filters( 'wpmu_welcome_notification', $blog_id, $user_id, $password, $title, $meta ) ) {
   1611 		return false;
   1612 	}
   1613 
   1614 	$user = get_userdata( $user_id );
   1615 
   1616 	$switched_locale = switch_to_locale( get_user_locale( $user ) );
   1617 
   1618 	$welcome_email = get_site_option( 'welcome_email' );
   1619 	if ( false == $welcome_email ) {
   1620 		/* translators: Do not translate USERNAME, SITE_NAME, BLOG_URL, PASSWORD: those are placeholders. */
   1621 		$welcome_email = __(
   1622 			'Howdy USERNAME,
   1623 
   1624 Your new SITE_NAME site has been successfully set up at:
   1625 BLOG_URL
   1626 
   1627 You can log in to the administrator account with the following information:
   1628 
   1629 Username: USERNAME
   1630 Password: PASSWORD
   1631 Log in here: BLOG_URLwp-login.php
   1632 
   1633 We hope you enjoy your new site. Thanks!
   1634 
   1635 --The Team @ SITE_NAME'
   1636 		);
   1637 	}
   1638 
   1639 	$url = get_blogaddress_by_id( $blog_id );
   1640 
   1641 	$welcome_email = str_replace( 'SITE_NAME', $current_network->site_name, $welcome_email );
   1642 	$welcome_email = str_replace( 'BLOG_TITLE', $title, $welcome_email );
   1643 	$welcome_email = str_replace( 'BLOG_URL', $url, $welcome_email );
   1644 	$welcome_email = str_replace( 'USERNAME', $user->user_login, $welcome_email );
   1645 	$welcome_email = str_replace( 'PASSWORD', $password, $welcome_email );
   1646 
   1647 	/**
   1648 	 * Filters the content of the welcome email sent to the site administrator after site activation.
   1649 	 *
   1650 	 * Content should be formatted for transmission via wp_mail().
   1651 	 *
   1652 	 * @since MU (3.0.0)
   1653 	 *
   1654 	 * @param string $welcome_email Message body of the email.
   1655 	 * @param int    $blog_id       Site ID.
   1656 	 * @param int    $user_id       User ID of the site administrator.
   1657 	 * @param string $password      User password, or "N/A" if the user account is not new.
   1658 	 * @param string $title         Site title.
   1659 	 * @param array  $meta          Signup meta data. By default, contains the requested privacy setting and lang_id.
   1660 	 */
   1661 	$welcome_email = apply_filters( 'update_welcome_email', $welcome_email, $blog_id, $user_id, $password, $title, $meta );
   1662 
   1663 	$admin_email = get_site_option( 'admin_email' );
   1664 
   1665 	if ( '' === $admin_email ) {
   1666 		$admin_email = 'support@' . wp_parse_url( network_home_url(), PHP_URL_HOST );
   1667 	}
   1668 
   1669 	$from_name       = ( '' !== get_site_option( 'site_name' ) ) ? esc_html( get_site_option( 'site_name' ) ) : 'WordPress';
   1670 	$message_headers = "From: \"{$from_name}\" <{$admin_email}>\n" . 'Content-Type: text/plain; charset="' . get_option( 'blog_charset' ) . "\"\n";
   1671 	$message         = $welcome_email;
   1672 
   1673 	if ( empty( $current_network->site_name ) ) {
   1674 		$current_network->site_name = 'WordPress';
   1675 	}
   1676 
   1677 	/* translators: New site notification email subject. 1: Network title, 2: New site title. */
   1678 	$subject = __( 'New %1$s Site: %2$s' );
   1679 
   1680 	/**
   1681 	 * Filters the subject of the welcome email sent to the site administrator after site activation.
   1682 	 *
   1683 	 * @since MU (3.0.0)
   1684 	 *
   1685 	 * @param string $subject Subject of the email.
   1686 	 */
   1687 	$subject = apply_filters( 'update_welcome_subject', sprintf( $subject, $current_network->site_name, wp_unslash( $title ) ) );
   1688 
   1689 	wp_mail( $user->user_email, wp_specialchars_decode( $subject ), $message, $message_headers );
   1690 
   1691 	if ( $switched_locale ) {
   1692 		restore_previous_locale();
   1693 	}
   1694 
   1695 	return true;
   1696 }
   1697 
   1698 /**
   1699  * Notifies the Multisite network administrator that a new site was created.
   1700  *
   1701  * Filter {@see 'send_new_site_email'} to disable or bypass.
   1702  *
   1703  * Filter {@see 'new_site_email'} to filter the contents.
   1704  *
   1705  * @since 5.6.0
   1706  *
   1707  * @param int $site_id Site ID of the new site.
   1708  * @param int $user_id User ID of the administrator of the new site.
   1709  * @return bool Whether the email notification was sent.
   1710  */
   1711 function wpmu_new_site_admin_notification( $site_id, $user_id ) {
   1712 	$site  = get_site( $site_id );
   1713 	$user  = get_userdata( $user_id );
   1714 	$email = get_site_option( 'admin_email' );
   1715 
   1716 	if ( ! $site || ! $user || ! $email ) {
   1717 		return false;
   1718 	}
   1719 
   1720 	/**
   1721 	 * Filters whether to send an email to the Multisite network administrator when a new site is created.
   1722 	 *
   1723 	 * Return false to disable sending the email.
   1724 	 *
   1725 	 * @since 5.6.0
   1726 	 *
   1727 	 * @param bool    $send Whether to send the email.
   1728 	 * @param WP_Site $site Site object of the new site.
   1729 	 * @param WP_User $user User object of the administrator of the new site.
   1730 	 */
   1731 	if ( ! apply_filters( 'send_new_site_email', true, $site, $user ) ) {
   1732 		return false;
   1733 	}
   1734 
   1735 	$switched_locale = false;
   1736 	$network_admin   = get_user_by( 'email', $email );
   1737 
   1738 	if ( $network_admin ) {
   1739 		// If the network admin email address corresponds to a user, switch to their locale.
   1740 		$switched_locale = switch_to_locale( get_user_locale( $network_admin ) );
   1741 	} else {
   1742 		// Otherwise switch to the locale of the current site.
   1743 		$switched_locale = switch_to_locale( get_locale() );
   1744 	}
   1745 
   1746 	$subject = sprintf(
   1747 		/* translators: New site notification email subject. %s: Network title. */
   1748 		__( '[%s] New Site Created' ),
   1749 		get_network()->site_name
   1750 	);
   1751 
   1752 	$message = sprintf(
   1753 		/* translators: New site notification email. 1: User login, 2: Site URL, 3: Site title. */
   1754 		__(
   1755 			'New site created by %1$s
   1756 
   1757 Address: %2$s
   1758 Name: %3$s'
   1759 		),
   1760 		$user->user_login,
   1761 		get_site_url( $site->id ),
   1762 		get_blog_option( $site->id, 'blogname' )
   1763 	);
   1764 
   1765 	$header = sprintf(
   1766 		'From: "%1$s" <%2$s>',
   1767 		_x( 'Site Admin', 'email "From" field' ),
   1768 		$email
   1769 	);
   1770 
   1771 	$new_site_email = array(
   1772 		'to'      => $email,
   1773 		'subject' => $subject,
   1774 		'message' => $message,
   1775 		'headers' => $header,
   1776 	);
   1777 
   1778 	/**
   1779 	 * Filters the content of the email sent to the Multisite network administrator when a new site is created.
   1780 	 *
   1781 	 * Content should be formatted for transmission via wp_mail().
   1782 	 *
   1783 	 * @since 5.6.0
   1784 	 *
   1785 	 * @param array $new_site_email {
   1786 	 *     Used to build wp_mail().
   1787 	 *
   1788 	 *     @type string $to      The email address of the recipient.
   1789 	 *     @type string $subject The subject of the email.
   1790 	 *     @type string $message The content of the email.
   1791 	 *     @type string $headers Headers.
   1792 	 * }
   1793 	 * @param WP_Site $site         Site object of the new site.
   1794 	 * @param WP_User $user         User object of the administrator of the new site.
   1795 	 */
   1796 	$new_site_email = apply_filters( 'new_site_email', $new_site_email, $site, $user );
   1797 
   1798 	wp_mail(
   1799 		$new_site_email['to'],
   1800 		wp_specialchars_decode( $new_site_email['subject'] ),
   1801 		$new_site_email['message'],
   1802 		$new_site_email['headers']
   1803 	);
   1804 
   1805 	if ( $switched_locale ) {
   1806 		restore_previous_locale();
   1807 	}
   1808 
   1809 	return true;
   1810 }
   1811 
   1812 /**
   1813  * Notify a user that their account activation has been successful.
   1814  *
   1815  * Filter {@see 'wpmu_welcome_user_notification'} to disable or bypass.
   1816  *
   1817  * Filter {@see 'update_welcome_user_email'} and {@see 'update_welcome_user_subject'} to
   1818  * modify the content and subject line of the notification email.
   1819  *
   1820  * @since MU (3.0.0)
   1821  *
   1822  * @param int    $user_id  User ID.
   1823  * @param string $password User password.
   1824  * @param array  $meta     Optional. Signup meta data. Default empty array.
   1825  * @return bool
   1826  */
   1827 function wpmu_welcome_user_notification( $user_id, $password, $meta = array() ) {
   1828 	$current_network = get_network();
   1829 
   1830 	/**
   1831 	 * Filters whether to bypass the welcome email after user activation.
   1832 	 *
   1833 	 * Returning false disables the welcome email.
   1834 	 *
   1835 	 * @since MU (3.0.0)
   1836 	 *
   1837 	 * @param int    $user_id  User ID.
   1838 	 * @param string $password User password.
   1839 	 * @param array  $meta     Signup meta data. Default empty array.
   1840 	 */
   1841 	if ( ! apply_filters( 'wpmu_welcome_user_notification', $user_id, $password, $meta ) ) {
   1842 		return false;
   1843 	}
   1844 
   1845 	$welcome_email = get_site_option( 'welcome_user_email' );
   1846 
   1847 	$user = get_userdata( $user_id );
   1848 
   1849 	$switched_locale = switch_to_locale( get_user_locale( $user ) );
   1850 
   1851 	/**
   1852 	 * Filters the content of the welcome email after user activation.
   1853 	 *
   1854 	 * Content should be formatted for transmission via wp_mail().
   1855 	 *
   1856 	 * @since MU (3.0.0)
   1857 	 *
   1858 	 * @param string $welcome_email The message body of the account activation success email.
   1859 	 * @param int    $user_id       User ID.
   1860 	 * @param string $password      User password.
   1861 	 * @param array  $meta          Signup meta data. Default empty array.
   1862 	 */
   1863 	$welcome_email = apply_filters( 'update_welcome_user_email', $welcome_email, $user_id, $password, $meta );
   1864 	$welcome_email = str_replace( 'SITE_NAME', $current_network->site_name, $welcome_email );
   1865 	$welcome_email = str_replace( 'USERNAME', $user->user_login, $welcome_email );
   1866 	$welcome_email = str_replace( 'PASSWORD', $password, $welcome_email );
   1867 	$welcome_email = str_replace( 'LOGINLINK', wp_login_url(), $welcome_email );
   1868 
   1869 	$admin_email = get_site_option( 'admin_email' );
   1870 
   1871 	if ( '' === $admin_email ) {
   1872 		$admin_email = 'support@' . wp_parse_url( network_home_url(), PHP_URL_HOST );
   1873 	}
   1874 
   1875 	$from_name       = ( '' !== get_site_option( 'site_name' ) ) ? esc_html( get_site_option( 'site_name' ) ) : 'WordPress';
   1876 	$message_headers = "From: \"{$from_name}\" <{$admin_email}>\n" . 'Content-Type: text/plain; charset="' . get_option( 'blog_charset' ) . "\"\n";
   1877 	$message         = $welcome_email;
   1878 
   1879 	if ( empty( $current_network->site_name ) ) {
   1880 		$current_network->site_name = 'WordPress';
   1881 	}
   1882 
   1883 	/* translators: New user notification email subject. 1: Network title, 2: New user login. */
   1884 	$subject = __( 'New %1$s User: %2$s' );
   1885 
   1886 	/**
   1887 	 * Filters the subject of the welcome email after user activation.
   1888 	 *
   1889 	 * @since MU (3.0.0)
   1890 	 *
   1891 	 * @param string $subject Subject of the email.
   1892 	 */
   1893 	$subject = apply_filters( 'update_welcome_user_subject', sprintf( $subject, $current_network->site_name, $user->user_login ) );
   1894 
   1895 	wp_mail( $user->user_email, wp_specialchars_decode( $subject ), $message, $message_headers );
   1896 
   1897 	if ( $switched_locale ) {
   1898 		restore_previous_locale();
   1899 	}
   1900 
   1901 	return true;
   1902 }
   1903 
   1904 /**
   1905  * Get the current network.
   1906  *
   1907  * Returns an object containing the 'id', 'domain', 'path', and 'site_name'
   1908  * properties of the network being viewed.
   1909  *
   1910  * @see wpmu_current_site()
   1911  *
   1912  * @since MU (3.0.0)
   1913  *
   1914  * @global WP_Network $current_site
   1915  *
   1916  * @return WP_Network
   1917  */
   1918 function get_current_site() {
   1919 	global $current_site;
   1920 	return $current_site;
   1921 }
   1922 
   1923 /**
   1924  * Get a user's most recent post.
   1925  *
   1926  * Walks through each of a user's blogs to find the post with
   1927  * the most recent post_date_gmt.
   1928  *
   1929  * @since MU (3.0.0)
   1930  *
   1931  * @global wpdb $wpdb WordPress database abstraction object.
   1932  *
   1933  * @param int $user_id
   1934  * @return array Contains the blog_id, post_id, post_date_gmt, and post_gmt_ts
   1935  */
   1936 function get_most_recent_post_of_user( $user_id ) {
   1937 	global $wpdb;
   1938 
   1939 	$user_blogs       = get_blogs_of_user( (int) $user_id );
   1940 	$most_recent_post = array();
   1941 
   1942 	// Walk through each blog and get the most recent post
   1943 	// published by $user_id.
   1944 	foreach ( (array) $user_blogs as $blog ) {
   1945 		$prefix      = $wpdb->get_blog_prefix( $blog->userblog_id );
   1946 		$recent_post = $wpdb->get_row( $wpdb->prepare( "SELECT ID, post_date_gmt FROM {$prefix}posts WHERE post_author = %d AND post_type = 'post' AND post_status = 'publish' ORDER BY post_date_gmt DESC LIMIT 1", $user_id ), ARRAY_A );
   1947 
   1948 		// Make sure we found a post.
   1949 		if ( isset( $recent_post['ID'] ) ) {
   1950 			$post_gmt_ts = strtotime( $recent_post['post_date_gmt'] );
   1951 
   1952 			/*
   1953 			 * If this is the first post checked
   1954 			 * or if this post is newer than the current recent post,
   1955 			 * make it the new most recent post.
   1956 			 */
   1957 			if ( ! isset( $most_recent_post['post_gmt_ts'] ) || ( $post_gmt_ts > $most_recent_post['post_gmt_ts'] ) ) {
   1958 				$most_recent_post = array(
   1959 					'blog_id'       => $blog->userblog_id,
   1960 					'post_id'       => $recent_post['ID'],
   1961 					'post_date_gmt' => $recent_post['post_date_gmt'],
   1962 					'post_gmt_ts'   => $post_gmt_ts,
   1963 				);
   1964 			}
   1965 		}
   1966 	}
   1967 
   1968 	return $most_recent_post;
   1969 }
   1970 
   1971 //
   1972 // Misc functions.
   1973 //
   1974 
   1975 /**
   1976  * Check an array of MIME types against a list of allowed types.
   1977  *
   1978  * WordPress ships with a set of allowed upload filetypes,
   1979  * which is defined in wp-includes/functions.php in
   1980  * get_allowed_mime_types(). This function is used to filter
   1981  * that list against the filetypes allowed provided by Multisite
   1982  * Super Admins at wp-admin/network/settings.php.
   1983  *
   1984  * @since MU (3.0.0)
   1985  *
   1986  * @param array $mimes
   1987  * @return array
   1988  */
   1989 function check_upload_mimes( $mimes ) {
   1990 	$site_exts  = explode( ' ', get_site_option( 'upload_filetypes', 'jpg jpeg png gif' ) );
   1991 	$site_mimes = array();
   1992 	foreach ( $site_exts as $ext ) {
   1993 		foreach ( $mimes as $ext_pattern => $mime ) {
   1994 			if ( '' !== $ext && false !== strpos( $ext_pattern, $ext ) ) {
   1995 				$site_mimes[ $ext_pattern ] = $mime;
   1996 			}
   1997 		}
   1998 	}
   1999 	return $site_mimes;
   2000 }
   2001 
   2002 /**
   2003  * Update a blog's post count.
   2004  *
   2005  * WordPress MS stores a blog's post count as an option so as
   2006  * to avoid extraneous COUNTs when a blog's details are fetched
   2007  * with get_site(). This function is called when posts are published
   2008  * or unpublished to make sure the count stays current.
   2009  *
   2010  * @since MU (3.0.0)
   2011  *
   2012  * @global wpdb $wpdb WordPress database abstraction object.
   2013  *
   2014  * @param string $deprecated Not used.
   2015  */
   2016 function update_posts_count( $deprecated = '' ) {
   2017 	global $wpdb;
   2018 	update_option( 'post_count', (int) $wpdb->get_var( "SELECT COUNT(ID) FROM {$wpdb->posts} WHERE post_status = 'publish' and post_type = 'post'" ) );
   2019 }
   2020 
   2021 /**
   2022  * Logs the user email, IP, and registration date of a new site.
   2023  *
   2024  * @since MU (3.0.0)
   2025  * @since 5.1.0 Parameters now support input from the {@see 'wp_initialize_site'} action.
   2026  *
   2027  * @global wpdb $wpdb WordPress database abstraction object.
   2028  *
   2029  * @param WP_Site|int $blog_id The new site's object or ID.
   2030  * @param int|array   $user_id User ID, or array of arguments including 'user_id'.
   2031  */
   2032 function wpmu_log_new_registrations( $blog_id, $user_id ) {
   2033 	global $wpdb;
   2034 
   2035 	if ( is_object( $blog_id ) ) {
   2036 		$blog_id = $blog_id->blog_id;
   2037 	}
   2038 
   2039 	if ( is_array( $user_id ) ) {
   2040 		$user_id = ! empty( $user_id['user_id'] ) ? $user_id['user_id'] : 0;
   2041 	}
   2042 
   2043 	$user = get_userdata( (int) $user_id );
   2044 	if ( $user ) {
   2045 		$wpdb->insert(
   2046 			$wpdb->registration_log,
   2047 			array(
   2048 				'email'           => $user->user_email,
   2049 				'IP'              => preg_replace( '/[^0-9., ]/', '', wp_unslash( $_SERVER['REMOTE_ADDR'] ) ),
   2050 				'blog_id'         => $blog_id,
   2051 				'date_registered' => current_time( 'mysql' ),
   2052 			)
   2053 		);
   2054 	}
   2055 }
   2056 
   2057 /**
   2058  * Maintains a canonical list of terms by syncing terms created for each blog with the global terms table.
   2059  *
   2060  * @since 3.0.0
   2061  *
   2062  * @see term_id_filter
   2063  *
   2064  * @global wpdb $wpdb WordPress database abstraction object.
   2065  *
   2066  * @param int    $term_id    An ID for a term on the current blog.
   2067  * @param string $deprecated Not used.
   2068  * @return int An ID from the global terms table mapped from $term_id.
   2069  */
   2070 function global_terms( $term_id, $deprecated = '' ) {
   2071 	global $wpdb;
   2072 	static $global_terms_recurse = null;
   2073 
   2074 	if ( ! global_terms_enabled() ) {
   2075 		return $term_id;
   2076 	}
   2077 
   2078 	// Prevent a race condition.
   2079 	$recurse_start = false;
   2080 	if ( null === $global_terms_recurse ) {
   2081 		$recurse_start        = true;
   2082 		$global_terms_recurse = 1;
   2083 	} elseif ( 10 < $global_terms_recurse++ ) {
   2084 		return $term_id;
   2085 	}
   2086 
   2087 	$term_id = (int) $term_id;
   2088 	$c       = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM $wpdb->terms WHERE term_id = %d", $term_id ) );
   2089 
   2090 	$global_id = $wpdb->get_var( $wpdb->prepare( "SELECT cat_ID FROM $wpdb->sitecategories WHERE category_nicename = %s", $c->slug ) );
   2091 	if ( null == $global_id ) {
   2092 		$used_global_id = $wpdb->get_var( $wpdb->prepare( "SELECT cat_ID FROM $wpdb->sitecategories WHERE cat_ID = %d", $c->term_id ) );
   2093 		if ( null == $used_global_id ) {
   2094 			$wpdb->insert(
   2095 				$wpdb->sitecategories,
   2096 				array(
   2097 					'cat_ID'            => $term_id,
   2098 					'cat_name'          => $c->name,
   2099 					'category_nicename' => $c->slug,
   2100 				)
   2101 			);
   2102 			$global_id = $wpdb->insert_id;
   2103 			if ( empty( $global_id ) ) {
   2104 				return $term_id;
   2105 			}
   2106 		} else {
   2107 			$max_global_id = $wpdb->get_var( "SELECT MAX(cat_ID) FROM $wpdb->sitecategories" );
   2108 			$max_local_id  = $wpdb->get_var( "SELECT MAX(term_id) FROM $wpdb->terms" );
   2109 			$new_global_id = max( $max_global_id, $max_local_id ) + mt_rand( 100, 400 );
   2110 			$wpdb->insert(
   2111 				$wpdb->sitecategories,
   2112 				array(
   2113 					'cat_ID'            => $new_global_id,
   2114 					'cat_name'          => $c->name,
   2115 					'category_nicename' => $c->slug,
   2116 				)
   2117 			);
   2118 			$global_id = $wpdb->insert_id;
   2119 		}
   2120 	} elseif ( $global_id != $term_id ) {
   2121 		$local_id = $wpdb->get_var( $wpdb->prepare( "SELECT term_id FROM $wpdb->terms WHERE term_id = %d", $global_id ) );
   2122 		if ( null != $local_id ) {
   2123 			global_terms( $local_id );
   2124 			if ( 10 < $global_terms_recurse ) {
   2125 				$global_id = $term_id;
   2126 			}
   2127 		}
   2128 	}
   2129 
   2130 	if ( $global_id != $term_id ) {
   2131 		if ( get_option( 'default_category' ) == $term_id ) {
   2132 			update_option( 'default_category', $global_id );
   2133 		}
   2134 
   2135 		$wpdb->update( $wpdb->terms, array( 'term_id' => $global_id ), array( 'term_id' => $term_id ) );
   2136 		$wpdb->update( $wpdb->term_taxonomy, array( 'term_id' => $global_id ), array( 'term_id' => $term_id ) );
   2137 		$wpdb->update( $wpdb->term_taxonomy, array( 'parent' => $global_id ), array( 'parent' => $term_id ) );
   2138 
   2139 		clean_term_cache( $term_id );
   2140 	}
   2141 	if ( $recurse_start ) {
   2142 		$global_terms_recurse = null;
   2143 	}
   2144 
   2145 	return $global_id;
   2146 }
   2147 
   2148 /**
   2149  * Ensure that the current site's domain is listed in the allowed redirect host list.
   2150  *
   2151  * @see wp_validate_redirect()
   2152  * @since MU (3.0.0)
   2153  *
   2154  * @param array|string $deprecated Not used.
   2155  * @return string[] {
   2156  *     An array containing the current site's domain.
   2157  *
   2158  *     @type string $0 The current site's domain.
   2159  * }
   2160  */
   2161 function redirect_this_site( $deprecated = '' ) {
   2162 	return array( get_network()->domain );
   2163 }
   2164 
   2165 /**
   2166  * Check whether an upload is too big.
   2167  *
   2168  * @since MU (3.0.0)
   2169  *
   2170  * @blessed
   2171  *
   2172  * @param array $upload
   2173  * @return string|array If the upload is under the size limit, $upload is returned. Otherwise returns an error message.
   2174  */
   2175 function upload_is_file_too_big( $upload ) {
   2176 	if ( ! is_array( $upload ) || defined( 'WP_IMPORTING' ) || get_site_option( 'upload_space_check_disabled' ) ) {
   2177 		return $upload;
   2178 	}
   2179 
   2180 	if ( strlen( $upload['bits'] ) > ( KB_IN_BYTES * get_site_option( 'fileupload_maxk', 1500 ) ) ) {
   2181 		/* translators: %s: Maximum allowed file size in kilobytes. */
   2182 		return sprintf( __( 'This file is too big. Files must be less than %s KB in size.' ) . '<br />', get_site_option( 'fileupload_maxk', 1500 ) );
   2183 	}
   2184 
   2185 	return $upload;
   2186 }
   2187 
   2188 /**
   2189  * Add a nonce field to the signup page.
   2190  *
   2191  * @since MU (3.0.0)
   2192  */
   2193 function signup_nonce_fields() {
   2194 	$id = mt_rand();
   2195 	echo "<input type='hidden' name='signup_form_id' value='{$id}' />";
   2196 	wp_nonce_field( 'signup_form_' . $id, '_signup_form', false );
   2197 }
   2198 
   2199 /**
   2200  * Process the signup nonce created in signup_nonce_fields().
   2201  *
   2202  * @since MU (3.0.0)
   2203  *
   2204  * @param array $result
   2205  * @return array
   2206  */
   2207 function signup_nonce_check( $result ) {
   2208 	if ( ! strpos( $_SERVER['PHP_SELF'], 'wp-signup.php' ) ) {
   2209 		return $result;
   2210 	}
   2211 
   2212 	if ( ! wp_verify_nonce( $_POST['_signup_form'], 'signup_form_' . $_POST['signup_form_id'] ) ) {
   2213 		$result['errors']->add( 'invalid_nonce', __( 'Unable to submit this form, please try again.' ) );
   2214 	}
   2215 
   2216 	return $result;
   2217 }
   2218 
   2219 /**
   2220  * Correct 404 redirects when NOBLOGREDIRECT is defined.
   2221  *
   2222  * @since MU (3.0.0)
   2223  */
   2224 function maybe_redirect_404() {
   2225 	if ( is_main_site() && is_404() && defined( 'NOBLOGREDIRECT' ) ) {
   2226 		/**
   2227 		 * Filters the redirect URL for 404s on the main site.
   2228 		 *
   2229 		 * The filter is only evaluated if the NOBLOGREDIRECT constant is defined.
   2230 		 *
   2231 		 * @since 3.0.0
   2232 		 *
   2233 		 * @param string $no_blog_redirect The redirect URL defined in NOBLOGREDIRECT.
   2234 		 */
   2235 		$destination = apply_filters( 'blog_redirect_404', NOBLOGREDIRECT );
   2236 
   2237 		if ( $destination ) {
   2238 			if ( '%siteurl%' === $destination ) {
   2239 				$destination = network_home_url();
   2240 			}
   2241 
   2242 			wp_redirect( $destination );
   2243 			exit;
   2244 		}
   2245 	}
   2246 }
   2247 
   2248 /**
   2249  * Add a new user to a blog by visiting /newbloguser/{key}/.
   2250  *
   2251  * This will only work when the user's details are saved as an option
   2252  * keyed as 'new_user_{key}', where '{key}' is a hash generated for the user to be
   2253  * added, as when a user is invited through the regular WP Add User interface.
   2254  *
   2255  * @since MU (3.0.0)
   2256  */
   2257 function maybe_add_existing_user_to_blog() {
   2258 	if ( false === strpos( $_SERVER['REQUEST_URI'], '/newbloguser/' ) ) {
   2259 		return;
   2260 	}
   2261 
   2262 	$parts = explode( '/', $_SERVER['REQUEST_URI'] );
   2263 	$key   = array_pop( $parts );
   2264 
   2265 	if ( '' === $key ) {
   2266 		$key = array_pop( $parts );
   2267 	}
   2268 
   2269 	$details = get_option( 'new_user_' . $key );
   2270 	if ( ! empty( $details ) ) {
   2271 		delete_option( 'new_user_' . $key );
   2272 	}
   2273 
   2274 	if ( empty( $details ) || is_wp_error( add_existing_user_to_blog( $details ) ) ) {
   2275 		wp_die(
   2276 			sprintf(
   2277 				/* translators: %s: Home URL. */
   2278 				__( 'An error occurred adding you to this site. Go to the <a href="%s">homepage</a>.' ),
   2279 				home_url()
   2280 			)
   2281 		);
   2282 	}
   2283 
   2284 	wp_die(
   2285 		sprintf(
   2286 			/* translators: 1: Home URL, 2: Admin URL. */
   2287 			__( 'You have been added to this site. Please visit the <a href="%1$s">homepage</a> or <a href="%2$s">log in</a> using your username and password.' ),
   2288 			home_url(),
   2289 			admin_url()
   2290 		),
   2291 		__( 'WordPress &rsaquo; Success' ),
   2292 		array( 'response' => 200 )
   2293 	);
   2294 }
   2295 
   2296 /**
   2297  * Add a user to a blog based on details from maybe_add_existing_user_to_blog().
   2298  *
   2299  * @since MU (3.0.0)
   2300  *
   2301  * @param array|false $details {
   2302  *     User details. Must at least contain values for the keys listed below.
   2303  *
   2304  *     @type int    $user_id The ID of the user being added to the current blog.
   2305  *     @type string $role    The role to be assigned to the user.
   2306  * }
   2307  * @return true|WP_Error|void True on success or a WP_Error object if the user doesn't exist
   2308  *                            or could not be added. Void if $details array was not provided.
   2309  */
   2310 function add_existing_user_to_blog( $details = false ) {
   2311 	if ( is_array( $details ) ) {
   2312 		$blog_id = get_current_blog_id();
   2313 		$result  = add_user_to_blog( $blog_id, $details['user_id'], $details['role'] );
   2314 
   2315 		/**
   2316 		 * Fires immediately after an existing user is added to a site.
   2317 		 *
   2318 		 * @since MU (3.0.0)
   2319 		 *
   2320 		 * @param int           $user_id User ID.
   2321 		 * @param true|WP_Error $result  True on success or a WP_Error object if the user doesn't exist
   2322 		 *                               or could not be added.
   2323 		 */
   2324 		do_action( 'added_existing_user', $details['user_id'], $result );
   2325 
   2326 		return $result;
   2327 	}
   2328 }
   2329 
   2330 /**
   2331  * Adds a newly created user to the appropriate blog
   2332  *
   2333  * To add a user in general, use add_user_to_blog(). This function
   2334  * is specifically hooked into the {@see 'wpmu_activate_user'} action.
   2335  *
   2336  * @since MU (3.0.0)
   2337  *
   2338  * @see add_user_to_blog()
   2339  *
   2340  * @param int    $user_id  User ID.
   2341  * @param string $password User password. Ignored.
   2342  * @param array  $meta     Signup meta data.
   2343  */
   2344 function add_new_user_to_blog( $user_id, $password, $meta ) {
   2345 	if ( ! empty( $meta['add_to_blog'] ) ) {
   2346 		$blog_id = $meta['add_to_blog'];
   2347 		$role    = $meta['new_role'];
   2348 		remove_user_from_blog( $user_id, get_network()->site_id ); // Remove user from main blog.
   2349 
   2350 		$result = add_user_to_blog( $blog_id, $user_id, $role );
   2351 
   2352 		if ( ! is_wp_error( $result ) ) {
   2353 			update_user_meta( $user_id, 'primary_blog', $blog_id );
   2354 		}
   2355 	}
   2356 }
   2357 
   2358 /**
   2359  * Correct From host on outgoing mail to match the site domain
   2360  *
   2361  * @since MU (3.0.0)
   2362  *
   2363  * @param PHPMailer $phpmailer The PHPMailer instance (passed by reference).
   2364  */
   2365 function fix_phpmailer_messageid( $phpmailer ) {
   2366 	$phpmailer->Hostname = get_network()->domain;
   2367 }
   2368 
   2369 /**
   2370  * Check to see whether a user is marked as a spammer, based on user login.
   2371  *
   2372  * @since MU (3.0.0)
   2373  *
   2374  * @param string|WP_User $user Optional. Defaults to current user. WP_User object,
   2375  *                             or user login name as a string.
   2376  * @return bool
   2377  */
   2378 function is_user_spammy( $user = null ) {
   2379 	if ( ! ( $user instanceof WP_User ) ) {
   2380 		if ( $user ) {
   2381 			$user = get_user_by( 'login', $user );
   2382 		} else {
   2383 			$user = wp_get_current_user();
   2384 		}
   2385 	}
   2386 
   2387 	return $user && isset( $user->spam ) && 1 == $user->spam;
   2388 }
   2389 
   2390 /**
   2391  * Update this blog's 'public' setting in the global blogs table.
   2392  *
   2393  * Public blogs have a setting of 1, private blogs are 0.
   2394  *
   2395  * @since MU (3.0.0)
   2396  *
   2397  * @param int $old_value
   2398  * @param int $value     The new public value
   2399  */
   2400 function update_blog_public( $old_value, $value ) {
   2401 	update_blog_status( get_current_blog_id(), 'public', (int) $value );
   2402 }
   2403 
   2404 /**
   2405  * Check whether users can self-register, based on Network settings.
   2406  *
   2407  * @since MU (3.0.0)
   2408  *
   2409  * @return bool
   2410  */
   2411 function users_can_register_signup_filter() {
   2412 	$registration = get_site_option( 'registration' );
   2413 	return ( 'all' === $registration || 'user' === $registration );
   2414 }
   2415 
   2416 /**
   2417  * Ensure that the welcome message is not empty. Currently unused.
   2418  *
   2419  * @since MU (3.0.0)
   2420  *
   2421  * @param string $text
   2422  * @return string
   2423  */
   2424 function welcome_user_msg_filter( $text ) {
   2425 	if ( ! $text ) {
   2426 		remove_filter( 'site_option_welcome_user_email', 'welcome_user_msg_filter' );
   2427 
   2428 		/* translators: Do not translate USERNAME, PASSWORD, LOGINLINK, SITE_NAME: those are placeholders. */
   2429 		$text = __(
   2430 			'Howdy USERNAME,
   2431 
   2432 Your new account is set up.
   2433 
   2434 You can log in with the following information:
   2435 Username: USERNAME
   2436 Password: PASSWORD
   2437 LOGINLINK
   2438 
   2439 Thanks!
   2440 
   2441 --The Team @ SITE_NAME'
   2442 		);
   2443 		update_site_option( 'welcome_user_email', $text );
   2444 	}
   2445 	return $text;
   2446 }
   2447 
   2448 /**
   2449  * Whether to force SSL on content.
   2450  *
   2451  * @since 2.8.5
   2452  *
   2453  * @param bool $force
   2454  * @return bool True if forced, false if not forced.
   2455  */
   2456 function force_ssl_content( $force = '' ) {
   2457 	static $forced_content = false;
   2458 
   2459 	if ( ! $force ) {
   2460 		$old_forced     = $forced_content;
   2461 		$forced_content = $force;
   2462 		return $old_forced;
   2463 	}
   2464 
   2465 	return $forced_content;
   2466 }
   2467 
   2468 /**
   2469  * Formats a URL to use https.
   2470  *
   2471  * Useful as a filter.
   2472  *
   2473  * @since 2.8.5
   2474  *
   2475  * @param string $url URL
   2476  * @return string URL with https as the scheme
   2477  */
   2478 function filter_SSL( $url ) {  // phpcs:ignore WordPress.NamingConventions.ValidFunctionName.FunctionNameInvalid
   2479 	if ( ! is_string( $url ) ) {
   2480 		return get_bloginfo( 'url' ); // Return home blog URL with proper scheme.
   2481 	}
   2482 
   2483 	if ( force_ssl_content() && is_ssl() ) {
   2484 		$url = set_url_scheme( $url, 'https' );
   2485 	}
   2486 
   2487 	return $url;
   2488 }
   2489 
   2490 /**
   2491  * Schedule update of the network-wide counts for the current network.
   2492  *
   2493  * @since 3.1.0
   2494  */
   2495 function wp_schedule_update_network_counts() {
   2496 	if ( ! is_main_site() ) {
   2497 		return;
   2498 	}
   2499 
   2500 	if ( ! wp_next_scheduled( 'update_network_counts' ) && ! wp_installing() ) {
   2501 		wp_schedule_event( time(), 'twicedaily', 'update_network_counts' );
   2502 	}
   2503 }
   2504 
   2505 /**
   2506  * Update the network-wide counts for the current network.
   2507  *
   2508  * @since 3.1.0
   2509  * @since 4.8.0 The `$network_id` parameter has been added.
   2510  *
   2511  * @param int|null $network_id ID of the network. Default is the current network.
   2512  */
   2513 function wp_update_network_counts( $network_id = null ) {
   2514 	wp_update_network_user_counts( $network_id );
   2515 	wp_update_network_site_counts( $network_id );
   2516 }
   2517 
   2518 /**
   2519  * Update the count of sites for the current network.
   2520  *
   2521  * If enabled through the {@see 'enable_live_network_counts'} filter, update the sites count
   2522  * on a network when a site is created or its status is updated.
   2523  *
   2524  * @since 3.7.0
   2525  * @since 4.8.0 The `$network_id` parameter has been added.
   2526  *
   2527  * @param int|null $network_id ID of the network. Default is the current network.
   2528  */
   2529 function wp_maybe_update_network_site_counts( $network_id = null ) {
   2530 	$is_small_network = ! wp_is_large_network( 'sites', $network_id );
   2531 
   2532 	/**
   2533 	 * Filters whether to update network site or user counts when a new site is created.
   2534 	 *
   2535 	 * @since 3.7.0
   2536 	 *
   2537 	 * @see wp_is_large_network()
   2538 	 *
   2539 	 * @param bool   $small_network Whether the network is considered small.
   2540 	 * @param string $context       Context. Either 'users' or 'sites'.
   2541 	 */
   2542 	if ( ! apply_filters( 'enable_live_network_counts', $is_small_network, 'sites' ) ) {
   2543 		return;
   2544 	}
   2545 
   2546 	wp_update_network_site_counts( $network_id );
   2547 }
   2548 
   2549 /**
   2550  * Update the network-wide users count.
   2551  *
   2552  * If enabled through the {@see 'enable_live_network_counts'} filter, update the users count
   2553  * on a network when a user is created or its status is updated.
   2554  *
   2555  * @since 3.7.0
   2556  * @since 4.8.0 The `$network_id` parameter has been added.
   2557  *
   2558  * @param int|null $network_id ID of the network. Default is the current network.
   2559  */
   2560 function wp_maybe_update_network_user_counts( $network_id = null ) {
   2561 	$is_small_network = ! wp_is_large_network( 'users', $network_id );
   2562 
   2563 	/** This filter is documented in wp-includes/ms-functions.php */
   2564 	if ( ! apply_filters( 'enable_live_network_counts', $is_small_network, 'users' ) ) {
   2565 		return;
   2566 	}
   2567 
   2568 	wp_update_network_user_counts( $network_id );
   2569 }
   2570 
   2571 /**
   2572  * Update the network-wide site count.
   2573  *
   2574  * @since 3.7.0
   2575  * @since 4.8.0 The `$network_id` parameter has been added.
   2576  *
   2577  * @param int|null $network_id ID of the network. Default is the current network.
   2578  */
   2579 function wp_update_network_site_counts( $network_id = null ) {
   2580 	$network_id = (int) $network_id;
   2581 	if ( ! $network_id ) {
   2582 		$network_id = get_current_network_id();
   2583 	}
   2584 
   2585 	$count = get_sites(
   2586 		array(
   2587 			'network_id'             => $network_id,
   2588 			'spam'                   => 0,
   2589 			'deleted'                => 0,
   2590 			'archived'               => 0,
   2591 			'count'                  => true,
   2592 			'update_site_meta_cache' => false,
   2593 		)
   2594 	);
   2595 
   2596 	update_network_option( $network_id, 'blog_count', $count );
   2597 }
   2598 
   2599 /**
   2600  * Update the network-wide user count.
   2601  *
   2602  * @since 3.7.0
   2603  * @since 4.8.0 The `$network_id` parameter has been added.
   2604  *
   2605  * @global wpdb $wpdb WordPress database abstraction object.
   2606  *
   2607  * @param int|null $network_id ID of the network. Default is the current network.
   2608  */
   2609 function wp_update_network_user_counts( $network_id = null ) {
   2610 	global $wpdb;
   2611 
   2612 	$count = $wpdb->get_var( "SELECT COUNT(ID) as c FROM $wpdb->users WHERE spam = '0' AND deleted = '0'" );
   2613 	update_network_option( $network_id, 'user_count', $count );
   2614 }
   2615 
   2616 /**
   2617  * Returns the space used by the current site.
   2618  *
   2619  * @since 3.5.0
   2620  *
   2621  * @return int Used space in megabytes.
   2622  */
   2623 function get_space_used() {
   2624 	/**
   2625 	 * Filters the amount of storage space used by the current site, in megabytes.
   2626 	 *
   2627 	 * @since 3.5.0
   2628 	 *
   2629 	 * @param int|false $space_used The amount of used space, in megabytes. Default false.
   2630 	 */
   2631 	$space_used = apply_filters( 'pre_get_space_used', false );
   2632 
   2633 	if ( false === $space_used ) {
   2634 		$upload_dir = wp_upload_dir();
   2635 		$space_used = get_dirsize( $upload_dir['basedir'] ) / MB_IN_BYTES;
   2636 	}
   2637 
   2638 	return $space_used;
   2639 }
   2640 
   2641 /**
   2642  * Returns the upload quota for the current blog.
   2643  *
   2644  * @since MU (3.0.0)
   2645  *
   2646  * @return int Quota in megabytes
   2647  */
   2648 function get_space_allowed() {
   2649 	$space_allowed = get_option( 'blog_upload_space' );
   2650 
   2651 	if ( ! is_numeric( $space_allowed ) ) {
   2652 		$space_allowed = get_site_option( 'blog_upload_space' );
   2653 	}
   2654 
   2655 	if ( ! is_numeric( $space_allowed ) ) {
   2656 		$space_allowed = 100;
   2657 	}
   2658 
   2659 	/**
   2660 	 * Filters the upload quota for the current site.
   2661 	 *
   2662 	 * @since 3.7.0
   2663 	 *
   2664 	 * @param int $space_allowed Upload quota in megabytes for the current blog.
   2665 	 */
   2666 	return apply_filters( 'get_space_allowed', $space_allowed );
   2667 }
   2668 
   2669 /**
   2670  * Determines if there is any upload space left in the current blog's quota.
   2671  *
   2672  * @since 3.0.0
   2673  *
   2674  * @return int of upload space available in bytes
   2675  */
   2676 function get_upload_space_available() {
   2677 	$allowed = get_space_allowed();
   2678 	if ( $allowed < 0 ) {
   2679 		$allowed = 0;
   2680 	}
   2681 	$space_allowed = $allowed * MB_IN_BYTES;
   2682 	if ( get_site_option( 'upload_space_check_disabled' ) ) {
   2683 		return $space_allowed;
   2684 	}
   2685 
   2686 	$space_used = get_space_used() * MB_IN_BYTES;
   2687 
   2688 	if ( ( $space_allowed - $space_used ) <= 0 ) {
   2689 		return 0;
   2690 	}
   2691 
   2692 	return $space_allowed - $space_used;
   2693 }
   2694 
   2695 /**
   2696  * Determines if there is any upload space left in the current blog's quota.
   2697  *
   2698  * @since 3.0.0
   2699  * @return bool True if space is available, false otherwise.
   2700  */
   2701 function is_upload_space_available() {
   2702 	if ( get_site_option( 'upload_space_check_disabled' ) ) {
   2703 		return true;
   2704 	}
   2705 
   2706 	return (bool) get_upload_space_available();
   2707 }
   2708 
   2709 /**
   2710  * Filters the maximum upload file size allowed, in bytes.
   2711  *
   2712  * @since 3.0.0
   2713  *
   2714  * @param int $size Upload size limit in bytes.
   2715  * @return int Upload size limit in bytes.
   2716  */
   2717 function upload_size_limit_filter( $size ) {
   2718 	$fileupload_maxk = KB_IN_BYTES * get_site_option( 'fileupload_maxk', 1500 );
   2719 	if ( get_site_option( 'upload_space_check_disabled' ) ) {
   2720 		return min( $size, $fileupload_maxk );
   2721 	}
   2722 
   2723 	return min( $size, $fileupload_maxk, get_upload_space_available() );
   2724 }
   2725 
   2726 /**
   2727  * Whether or not we have a large network.
   2728  *
   2729  * The default criteria for a large network is either more than 10,000 users or more than 10,000 sites.
   2730  * Plugins can alter this criteria using the {@see 'wp_is_large_network'} filter.
   2731  *
   2732  * @since 3.3.0
   2733  * @since 4.8.0 The `$network_id` parameter has been added.
   2734  *
   2735  * @param string   $using      'sites or 'users'. Default is 'sites'.
   2736  * @param int|null $network_id ID of the network. Default is the current network.
   2737  * @return bool True if the network meets the criteria for large. False otherwise.
   2738  */
   2739 function wp_is_large_network( $using = 'sites', $network_id = null ) {
   2740 	$network_id = (int) $network_id;
   2741 	if ( ! $network_id ) {
   2742 		$network_id = get_current_network_id();
   2743 	}
   2744 
   2745 	if ( 'users' === $using ) {
   2746 		$count = get_user_count( $network_id );
   2747 		/**
   2748 		 * Filters whether the network is considered large.
   2749 		 *
   2750 		 * @since 3.3.0
   2751 		 * @since 4.8.0 The `$network_id` parameter has been added.
   2752 		 *
   2753 		 * @param bool   $is_large_network Whether the network has more than 10000 users or sites.
   2754 		 * @param string $component        The component to count. Accepts 'users', or 'sites'.
   2755 		 * @param int    $count            The count of items for the component.
   2756 		 * @param int    $network_id       The ID of the network being checked.
   2757 		 */
   2758 		return apply_filters( 'wp_is_large_network', $count > 10000, 'users', $count, $network_id );
   2759 	}
   2760 
   2761 	$count = get_blog_count( $network_id );
   2762 
   2763 	/** This filter is documented in wp-includes/ms-functions.php */
   2764 	return apply_filters( 'wp_is_large_network', $count > 10000, 'sites', $count, $network_id );
   2765 }
   2766 
   2767 /**
   2768  * Retrieves a list of reserved site on a sub-directory Multisite installation.
   2769  *
   2770  * @since 4.4.0
   2771  *
   2772  * @return string[] Array of reserved names.
   2773  */
   2774 function get_subdirectory_reserved_names() {
   2775 	$names = array(
   2776 		'page',
   2777 		'comments',
   2778 		'blog',
   2779 		'files',
   2780 		'feed',
   2781 		'wp-admin',
   2782 		'wp-content',
   2783 		'wp-includes',
   2784 		'wp-json',
   2785 		'embed',
   2786 	);
   2787 
   2788 	/**
   2789 	 * Filters reserved site names on a sub-directory Multisite installation.
   2790 	 *
   2791 	 * @since 3.0.0
   2792 	 * @since 4.4.0 'wp-admin', 'wp-content', 'wp-includes', 'wp-json', and 'embed' were added
   2793 	 *              to the reserved names list.
   2794 	 *
   2795 	 * @param string[] $subdirectory_reserved_names Array of reserved names.
   2796 	 */
   2797 	return apply_filters( 'subdirectory_reserved_names', $names );
   2798 }
   2799 
   2800 /**
   2801  * Send a confirmation request email when a change of network admin email address is attempted.
   2802  *
   2803  * The new network admin address will not become active until confirmed.
   2804  *
   2805  * @since 4.9.0
   2806  *
   2807  * @param string $old_value The old network admin email address.
   2808  * @param string $value     The proposed new network admin email address.
   2809  */
   2810 function update_network_option_new_admin_email( $old_value, $value ) {
   2811 	if ( get_site_option( 'admin_email' ) === $value || ! is_email( $value ) ) {
   2812 		return;
   2813 	}
   2814 
   2815 	$hash            = md5( $value . time() . mt_rand() );
   2816 	$new_admin_email = array(
   2817 		'hash'     => $hash,
   2818 		'newemail' => $value,
   2819 	);
   2820 	update_site_option( 'network_admin_hash', $new_admin_email );
   2821 
   2822 	$switched_locale = switch_to_locale( get_user_locale() );
   2823 
   2824 	/* translators: Do not translate USERNAME, ADMIN_URL, EMAIL, SITENAME, SITEURL: those are placeholders. */
   2825 	$email_text = __(
   2826 		'Howdy ###USERNAME###,
   2827 
   2828 You recently requested to have the network admin email address on
   2829 your network changed.
   2830 
   2831 If this is correct, please click on the following link to change it:
   2832 ###ADMIN_URL###
   2833 
   2834 You can safely ignore and delete this email if you do not want to
   2835 take this action.
   2836 
   2837 This email has been sent to ###EMAIL###
   2838 
   2839 Regards,
   2840 All at ###SITENAME###
   2841 ###SITEURL###'
   2842 	);
   2843 
   2844 	/**
   2845 	 * Filters the text of the email sent when a change of network admin email address is attempted.
   2846 	 *
   2847 	 * The following strings have a special meaning and will get replaced dynamically:
   2848 	 * ###USERNAME###  The current user's username.
   2849 	 * ###ADMIN_URL### The link to click on to confirm the email change.
   2850 	 * ###EMAIL###     The proposed new network admin email address.
   2851 	 * ###SITENAME###  The name of the network.
   2852 	 * ###SITEURL###   The URL to the network.
   2853 	 *
   2854 	 * @since 4.9.0
   2855 	 *
   2856 	 * @param string $email_text      Text in the email.
   2857 	 * @param array  $new_admin_email {
   2858 	 *     Data relating to the new network admin email address.
   2859 	 *
   2860 	 *     @type string $hash     The secure hash used in the confirmation link URL.
   2861 	 *     @type string $newemail The proposed new network admin email address.
   2862 	 * }
   2863 	 */
   2864 	$content = apply_filters( 'new_network_admin_email_content', $email_text, $new_admin_email );
   2865 
   2866 	$current_user = wp_get_current_user();
   2867 	$content      = str_replace( '###USERNAME###', $current_user->user_login, $content );
   2868 	$content      = str_replace( '###ADMIN_URL###', esc_url( network_admin_url( 'settings.php?network_admin_hash=' . $hash ) ), $content );
   2869 	$content      = str_replace( '###EMAIL###', $value, $content );
   2870 	$content      = str_replace( '###SITENAME###', wp_specialchars_decode( get_site_option( 'site_name' ), ENT_QUOTES ), $content );
   2871 	$content      = str_replace( '###SITEURL###', network_home_url(), $content );
   2872 
   2873 	wp_mail(
   2874 		$value,
   2875 		sprintf(
   2876 			/* translators: Email change notification email subject. %s: Network title. */
   2877 			__( '[%s] Network Admin Email Change Request' ),
   2878 			wp_specialchars_decode( get_site_option( 'site_name' ), ENT_QUOTES )
   2879 		),
   2880 		$content
   2881 	);
   2882 
   2883 	if ( $switched_locale ) {
   2884 		restore_previous_locale();
   2885 	}
   2886 }
   2887 
   2888 /**
   2889  * Send an email to the old network admin email address when the network admin email address changes.
   2890  *
   2891  * @since 4.9.0
   2892  *
   2893  * @param string $option_name The relevant database option name.
   2894  * @param string $new_email   The new network admin email address.
   2895  * @param string $old_email   The old network admin email address.
   2896  * @param int    $network_id  ID of the network.
   2897  */
   2898 function wp_network_admin_email_change_notification( $option_name, $new_email, $old_email, $network_id ) {
   2899 	$send = true;
   2900 
   2901 	// Don't send the notification to the default 'admin_email' value.
   2902 	if ( 'you@example.com' === $old_email ) {
   2903 		$send = false;
   2904 	}
   2905 
   2906 	/**
   2907 	 * Filters whether to send the network admin email change notification email.
   2908 	 *
   2909 	 * @since 4.9.0
   2910 	 *
   2911 	 * @param bool   $send       Whether to send the email notification.
   2912 	 * @param string $old_email  The old network admin email address.
   2913 	 * @param string $new_email  The new network admin email address.
   2914 	 * @param int    $network_id ID of the network.
   2915 	 */
   2916 	$send = apply_filters( 'send_network_admin_email_change_email', $send, $old_email, $new_email, $network_id );
   2917 
   2918 	if ( ! $send ) {
   2919 		return;
   2920 	}
   2921 
   2922 	/* translators: Do not translate OLD_EMAIL, NEW_EMAIL, SITENAME, SITEURL: those are placeholders. */
   2923 	$email_change_text = __(
   2924 		'Hi,
   2925 
   2926 This notice confirms that the network admin email address was changed on ###SITENAME###.
   2927 
   2928 The new network admin email address is ###NEW_EMAIL###.
   2929 
   2930 This email has been sent to ###OLD_EMAIL###
   2931 
   2932 Regards,
   2933 All at ###SITENAME###
   2934 ###SITEURL###'
   2935 	);
   2936 
   2937 	$email_change_email = array(
   2938 		'to'      => $old_email,
   2939 		/* translators: Network admin email change notification email subject. %s: Network title. */
   2940 		'subject' => __( '[%s] Network Admin Email Changed' ),
   2941 		'message' => $email_change_text,
   2942 		'headers' => '',
   2943 	);
   2944 	// Get network name.
   2945 	$network_name = wp_specialchars_decode( get_site_option( 'site_name' ), ENT_QUOTES );
   2946 
   2947 	/**
   2948 	 * Filters the contents of the email notification sent when the network admin email address is changed.
   2949 	 *
   2950 	 * @since 4.9.0
   2951 	 *
   2952 	 * @param array $email_change_email {
   2953 	 *     Used to build wp_mail().
   2954 	 *
   2955 	 *     @type string $to      The intended recipient.
   2956 	 *     @type string $subject The subject of the email.
   2957 	 *     @type string $message The content of the email.
   2958 	 *         The following strings have a special meaning and will get replaced dynamically:
   2959 	 *         - ###OLD_EMAIL### The old network admin email address.
   2960 	 *         - ###NEW_EMAIL### The new network admin email address.
   2961 	 *         - ###SITENAME###  The name of the network.
   2962 	 *         - ###SITEURL###   The URL to the site.
   2963 	 *     @type string $headers Headers.
   2964 	 * }
   2965 	 * @param string $old_email  The old network admin email address.
   2966 	 * @param string $new_email  The new network admin email address.
   2967 	 * @param int    $network_id ID of the network.
   2968 	 */
   2969 	$email_change_email = apply_filters( 'network_admin_email_change_email', $email_change_email, $old_email, $new_email, $network_id );
   2970 
   2971 	$email_change_email['message'] = str_replace( '###OLD_EMAIL###', $old_email, $email_change_email['message'] );
   2972 	$email_change_email['message'] = str_replace( '###NEW_EMAIL###', $new_email, $email_change_email['message'] );
   2973 	$email_change_email['message'] = str_replace( '###SITENAME###', $network_name, $email_change_email['message'] );
   2974 	$email_change_email['message'] = str_replace( '###SITEURL###', home_url(), $email_change_email['message'] );
   2975 
   2976 	wp_mail(
   2977 		$email_change_email['to'],
   2978 		sprintf(
   2979 			$email_change_email['subject'],
   2980 			$network_name
   2981 		),
   2982 		$email_change_email['message'],
   2983 		$email_change_email['headers']
   2984 	);
   2985 }