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