PageRenderTime 114ms CodeModel.GetById 18ms app.highlight 77ms RepoModel.GetById 2ms app.codeStats 0ms

/wp-includes/ms-functions.php

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