| [ 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 * @phpstan-return array{ 1194 * user_id: int, 1195 * password: string, 1196 * meta: array<string, mixed>, 1197 * }|array{ 1198 * blog_id: int, 1199 * user_id: int, 1200 * password: string, 1201 * title: string, 1202 * meta: array<string, mixed>, 1203 * }|WP_Error 1204 */ 1205 function wpmu_activate_signup( 1206 #[\SensitiveParameter] 1207 $key 1208 ) { 1209 global $wpdb; 1210 1211 /** @var object{ signup_id: string, domain: string, path: string, title: string, user_login: string, user_email: string, registered: string, activated: string, active: string, activation_key: string, meta: string|null }|null $signup */ 1212 $signup = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM $wpdb->signups WHERE activation_key = %s", $key ) ); 1213 1214 if ( empty( $signup ) ) { 1215 return new WP_Error( 'invalid_key', __( 'Invalid activation key.' ) ); 1216 } 1217 1218 if ( $signup->active ) { 1219 if ( empty( $signup->domain ) ) { 1220 return new WP_Error( 'already_active', __( 'The user is already active.' ), $signup ); 1221 } else { 1222 return new WP_Error( 'already_active', __( 'The site is already active.' ), $signup ); 1223 } 1224 } 1225 1226 if ( is_string( $signup->meta ) ) { 1227 /** @var array<string, mixed> $meta */ 1228 $meta = maybe_unserialize( $signup->meta ); 1229 } else { 1230 $meta = array(); 1231 } 1232 $password = wp_generate_password( 12, false ); 1233 1234 $user_id = username_exists( $signup->user_login ); 1235 1236 if ( ! $user_id ) { 1237 $user_id = wpmu_create_user( $signup->user_login, $password, $signup->user_email ); 1238 } else { 1239 $user_already_exists = true; 1240 } 1241 1242 if ( ! $user_id ) { 1243 return new WP_Error( 'create_user', __( 'Could not create user' ), $signup ); 1244 } 1245 1246 $now = current_time( 'mysql', true ); 1247 1248 if ( empty( $signup->domain ) ) { 1249 $wpdb->update( 1250 (string) $wpdb->signups, 1251 array( 1252 'active' => 1, 1253 'activated' => $now, 1254 ), 1255 array( 'activation_key' => $key ) 1256 ); 1257 1258 if ( isset( $user_already_exists ) ) { 1259 return new WP_Error( 'user_already_exists', __( 'That username is already activated.' ), $signup ); 1260 } 1261 1262 /** 1263 * Fires immediately after a new user is activated. 1264 * 1265 * @since MU (3.0.0) 1266 * 1267 * @param int $user_id User ID. 1268 * @param string $password User password. 1269 * @param array $meta Signup meta data. 1270 */ 1271 do_action( 'wpmu_activate_user', $user_id, $password, $meta ); 1272 1273 return array( 1274 'user_id' => $user_id, 1275 'password' => $password, 1276 'meta' => $meta, 1277 ); 1278 } 1279 1280 $blog_id = wpmu_create_blog( $signup->domain, $signup->path, $signup->title, $user_id, $meta, get_current_network_id() ); 1281 1282 // TODO: What to do if we create a user but cannot create a blog? 1283 if ( is_wp_error( $blog_id ) ) { 1284 /* 1285 * If blog is taken, that means a previous attempt to activate this blog 1286 * failed in between creating the blog and setting the activation flag. 1287 * Let's just set the active flag and instruct the user to reset their password. 1288 */ 1289 if ( 'blog_taken' === $blog_id->get_error_code() ) { 1290 $blog_id->add_data( $signup ); 1291 $wpdb->update( 1292 (string) $wpdb->signups, 1293 array( 1294 'active' => 1, 1295 'activated' => $now, 1296 ), 1297 array( 'activation_key' => $key ) 1298 ); 1299 } 1300 return $blog_id; 1301 } 1302 1303 $wpdb->update( 1304 (string) $wpdb->signups, 1305 array( 1306 'active' => 1, 1307 'activated' => $now, 1308 ), 1309 array( 'activation_key' => $key ) 1310 ); 1311 1312 /** 1313 * Fires immediately after a site is activated. 1314 * 1315 * @since MU (3.0.0) 1316 * 1317 * @param int $blog_id Blog ID. 1318 * @param int $user_id User ID. 1319 * @param string $password User password. 1320 * @param string $signup_title Site title. 1321 * @param array $meta Signup meta data. By default, contains the requested privacy setting and lang_id. 1322 */ 1323 do_action( 'wpmu_activate_blog', $blog_id, $user_id, $password, $signup->title, $meta ); 1324 1325 return array( 1326 'blog_id' => $blog_id, 1327 'user_id' => $user_id, 1328 'password' => $password, 1329 'title' => $signup->title, 1330 'meta' => $meta, 1331 ); 1332 } 1333 1334 /** 1335 * Deletes an associated signup entry when a user is deleted from the database. 1336 * 1337 * @since 5.5.0 1338 * 1339 * @global wpdb $wpdb WordPress database abstraction object. 1340 * 1341 * @param int $id ID of the user to delete. 1342 * @param int|null $reassign ID of the user to reassign posts and links to. 1343 * @param WP_User $user User object. 1344 */ 1345 function wp_delete_signup_on_user_delete( $id, $reassign, $user ) { 1346 global $wpdb; 1347 1348 $wpdb->delete( $wpdb->signups, array( 'user_login' => $user->user_login ) ); 1349 } 1350 1351 /** 1352 * Creates a user. 1353 * 1354 * This function runs when a user self-registers as well as when 1355 * a Super Admin creates a new user. Hook to {@see 'wpmu_new_user'} for events 1356 * that should affect all new users, but only on Multisite (otherwise 1357 * use {@see 'user_register'}). 1358 * 1359 * @since MU (3.0.0) 1360 * 1361 * @param string $user_name The new user's login name. 1362 * @param string $password The new user's password. 1363 * @param string $email The new user's email address. 1364 * @return int|false Returns false on failure, or int $user_id on success. 1365 */ 1366 function wpmu_create_user( 1367 $user_name, 1368 #[\SensitiveParameter] 1369 $password, 1370 $email 1371 ) { 1372 $user_name = preg_replace( '/\s+/', '', sanitize_user( $user_name, true ) ); 1373 1374 $user_id = wp_create_user( $user_name, $password, $email ); 1375 if ( is_wp_error( $user_id ) ) { 1376 return false; 1377 } 1378 1379 // Newly created users have no roles or caps until they are added to a blog. 1380 delete_user_option( $user_id, 'capabilities' ); 1381 delete_user_option( $user_id, 'user_level' ); 1382 1383 /** 1384 * Fires immediately after a new user is created. 1385 * 1386 * @since MU (3.0.0) 1387 * 1388 * @param int $user_id User ID. 1389 */ 1390 do_action( 'wpmu_new_user', $user_id ); 1391 1392 return $user_id; 1393 } 1394 1395 /** 1396 * Creates a site. 1397 * 1398 * This function runs when a user self-registers a new site as well 1399 * as when a Super Admin creates a new site. Hook to {@see 'wpmu_new_blog'} 1400 * for events that should affect all new sites. 1401 * 1402 * On subdirectory installations, $domain is the same as the main site's 1403 * domain, and the path is the subdirectory name (eg 'example.com' 1404 * and '/blog1/'). On subdomain installations, $domain is the new subdomain + 1405 * root domain (eg 'blog1.example.com'), and $path is '/'. 1406 * 1407 * @since MU (3.0.0) 1408 * 1409 * @param string $domain The new site's domain. 1410 * @param string $path The new site's path. 1411 * @param string $title The new site's title. 1412 * @param int $user_id The user ID of the new site's admin. 1413 * @param array $options Optional. Array of key=>value pairs used to set initial site options. 1414 * If valid status keys are included ('public', 'archived', 'mature', 1415 * 'spam', 'deleted', or 'lang_id') the given site status(es) will be 1416 * updated. Otherwise, keys and values will be used to set options for 1417 * the new site. Default empty array. 1418 * @param int $network_id Optional. Network ID. Only relevant on multi-network installations. 1419 * Default 1. 1420 * @return int|WP_Error Returns WP_Error object on failure, the new site ID on success. 1421 */ 1422 function wpmu_create_blog( $domain, $path, $title, $user_id, $options = array(), $network_id = 1 ) { 1423 $defaults = array( 1424 'public' => 0, 1425 ); 1426 $options = wp_parse_args( $options, $defaults ); 1427 1428 $title = strip_tags( $title ); 1429 $user_id = (int) $user_id; 1430 1431 // Check if the domain has been used already. We should return an error message. 1432 if ( domain_exists( $domain, $path, $network_id ) ) { 1433 return new WP_Error( 'blog_taken', __( 'Sorry, that site already exists!' ) ); 1434 } 1435 1436 if ( ! wp_installing() ) { 1437 wp_installing( true ); 1438 } 1439 1440 $allowed_data_fields = array( 'public', 'archived', 'mature', 'spam', 'deleted', 'lang_id' ); 1441 1442 $site_data = array_merge( 1443 array( 1444 'domain' => $domain, 1445 'path' => $path, 1446 'network_id' => $network_id, 1447 ), 1448 array_intersect_key( $options, array_flip( $allowed_data_fields ) ) 1449 ); 1450 1451 // Data to pass to wp_initialize_site(). 1452 $site_initialization_data = array( 1453 'title' => $title, 1454 'user_id' => $user_id, 1455 'options' => array_diff_key( $options, array_flip( $allowed_data_fields ) ), 1456 ); 1457 1458 $blog_id = wp_insert_site( array_merge( $site_data, $site_initialization_data ) ); 1459 1460 if ( is_wp_error( $blog_id ) ) { 1461 return $blog_id; 1462 } 1463 1464 wp_cache_set_sites_last_changed(); 1465 1466 return $blog_id; 1467 } 1468 1469 /** 1470 * Notifies the network admin that a new site has been activated. 1471 * 1472 * Filter {@see 'newblog_notify_siteadmin'} to change the content of 1473 * the notification email. 1474 * 1475 * @since MU (3.0.0) 1476 * @since 5.1.0 $blog_id now supports input from the {@see 'wp_initialize_site'} action. 1477 * 1478 * @param WP_Site|int $blog_id The new site's object or ID. 1479 * @param string $deprecated Not used. 1480 * @return bool 1481 */ 1482 function newblog_notify_siteadmin( $blog_id, $deprecated = '' ) { 1483 if ( is_object( $blog_id ) ) { 1484 $blog_id = $blog_id->blog_id; 1485 } 1486 1487 if ( 'yes' !== get_site_option( 'registrationnotification' ) ) { 1488 return false; 1489 } 1490 1491 $email = get_site_option( 'admin_email' ); 1492 1493 if ( ! is_email( $email ) ) { 1494 return false; 1495 } 1496 1497 $options_site_url = esc_url( network_admin_url( 'settings.php' ) ); 1498 1499 switch_to_blog( $blog_id ); 1500 $blogname = get_option( 'blogname' ); 1501 $siteurl = site_url(); 1502 restore_current_blog(); 1503 1504 $msg = sprintf( 1505 /* translators: New site notification email. 1: Site URL, 2: User IP address, 3: URL to Network Settings screen. */ 1506 __( 1507 'New Site: %1$s 1508 URL: %2$s 1509 Remote IP address: %3$s 1510 1511 Disable these notifications: %4$s' 1512 ), 1513 $blogname, 1514 $siteurl, 1515 wp_unslash( $_SERVER['REMOTE_ADDR'] ), 1516 $options_site_url 1517 ); 1518 /** 1519 * Filters the message body of the new site activation email sent 1520 * to the network administrator. 1521 * 1522 * @since MU (3.0.0) 1523 * @since 5.4.0 The `$blog_id` parameter was added. 1524 * 1525 * @param string $msg Email body. 1526 * @param int|string $blog_id The new site's ID as an integer or numeric string. 1527 */ 1528 $msg = apply_filters( 'newblog_notify_siteadmin', $msg, $blog_id ); 1529 1530 /* translators: New site notification email subject. %s: New site URL. */ 1531 wp_mail( $email, sprintf( __( 'New Site Registration: %s' ), $siteurl ), $msg ); 1532 1533 return true; 1534 } 1535 1536 /** 1537 * Notifies the network admin that a new user has been activated. 1538 * 1539 * Filter {@see 'newuser_notify_siteadmin'} to change the content of 1540 * the notification email. 1541 * 1542 * @since MU (3.0.0) 1543 * 1544 * @param int $user_id The new user's ID. 1545 * @return bool 1546 */ 1547 function newuser_notify_siteadmin( $user_id ) { 1548 if ( 'yes' !== get_site_option( 'registrationnotification' ) ) { 1549 return false; 1550 } 1551 1552 $email = get_site_option( 'admin_email' ); 1553 1554 if ( ! is_email( $email ) ) { 1555 return false; 1556 } 1557 1558 $user = get_userdata( $user_id ); 1559 1560 $options_site_url = esc_url( network_admin_url( 'settings.php' ) ); 1561 1562 $msg = sprintf( 1563 /* translators: New user notification email. 1: User login, 2: User IP address, 3: URL to Network Settings screen. */ 1564 __( 1565 'New User: %1$s 1566 Remote IP address: %2$s 1567 1568 Disable these notifications: %3$s' 1569 ), 1570 $user->user_login, 1571 wp_unslash( $_SERVER['REMOTE_ADDR'] ), 1572 $options_site_url 1573 ); 1574 1575 /** 1576 * Filters the message body of the new user activation email sent 1577 * to the network administrator. 1578 * 1579 * @since MU (3.0.0) 1580 * 1581 * @param string $msg Email body. 1582 * @param WP_User $user WP_User instance of the new user. 1583 */ 1584 $msg = apply_filters( 'newuser_notify_siteadmin', $msg, $user ); 1585 1586 /* translators: New user notification email subject. %s: User login. */ 1587 wp_mail( $email, sprintf( __( 'New User Registration: %s' ), $user->user_login ), $msg ); 1588 1589 return true; 1590 } 1591 1592 /** 1593 * Checks whether a site name is already taken. 1594 * 1595 * The name is the site's subdomain or the site's subdirectory 1596 * path depending on the network settings. 1597 * 1598 * Used during the new site registration process to ensure 1599 * that each site name is unique. 1600 * 1601 * @since MU (3.0.0) 1602 * 1603 * @param string $domain The domain to be checked. 1604 * @param string $path The path to be checked. 1605 * @param int $network_id Optional. Network ID. Only relevant on multi-network installations. 1606 * Default 1. 1607 * @return int|null The site ID if the site name exists, null otherwise. 1608 */ 1609 function domain_exists( $domain, $path, $network_id = 1 ) { 1610 $path = trailingslashit( $path ); 1611 $args = array( 1612 'network_id' => $network_id, 1613 'domain' => $domain, 1614 'path' => $path, 1615 'fields' => 'ids', 1616 'number' => 1, 1617 'update_site_meta_cache' => false, 1618 ); 1619 $result = get_sites( $args ); 1620 $result = array_shift( $result ); 1621 1622 /** 1623 * Filters whether a site name is taken. 1624 * 1625 * The name is the site's subdomain or the site's subdirectory 1626 * path depending on the network settings. 1627 * 1628 * @since 3.5.0 1629 * 1630 * @param int|null $result The site ID if the site name exists, null otherwise. 1631 * @param string $domain Domain to be checked. 1632 * @param string $path Path to be checked. 1633 * @param int $network_id Network ID. Only relevant on multi-network installations. 1634 */ 1635 return apply_filters( 'domain_exists', $result, $domain, $path, $network_id ); 1636 } 1637 1638 /** 1639 * Notifies the site administrator that their site activation was successful. 1640 * 1641 * Filter {@see 'wpmu_welcome_notification'} to disable or bypass. 1642 * 1643 * Filter {@see 'update_welcome_email'} and {@see 'update_welcome_subject'} to 1644 * modify the content and subject line of the notification email. 1645 * 1646 * @since MU (3.0.0) 1647 * 1648 * @param int $blog_id Site ID. 1649 * @param int $user_id User ID. 1650 * @param string $password User password, or "N/A" if the user account is not new. 1651 * @param string $title Site title. 1652 * @param array $meta Optional. Signup meta data. By default, contains the requested privacy setting and lang_id. 1653 * @return bool Whether the email notification was sent. 1654 */ 1655 function wpmu_welcome_notification( 1656 $blog_id, 1657 $user_id, 1658 #[\SensitiveParameter] 1659 $password, 1660 $title, 1661 $meta = array() 1662 ) { 1663 $current_network = get_network(); 1664 1665 /** 1666 * Filters whether to bypass the welcome email sent to the site administrator after site activation. 1667 * 1668 * Returning false disables the welcome email. 1669 * 1670 * @since MU (3.0.0) 1671 * 1672 * @param int|false $blog_id Site ID, or false to prevent the email from sending. 1673 * @param int $user_id User ID of the site administrator. 1674 * @param string $password User password, or "N/A" if the user account is not new. 1675 * @param string $title Site title. 1676 * @param array $meta Signup meta data. By default, contains the requested privacy setting and lang_id. 1677 */ 1678 if ( ! apply_filters( 'wpmu_welcome_notification', $blog_id, $user_id, $password, $title, $meta ) ) { 1679 return false; 1680 } 1681 1682 $user = get_userdata( $user_id ); 1683 1684 $switched_locale = switch_to_user_locale( $user_id ); 1685 1686 $welcome_email = get_site_option( 'welcome_email' ); 1687 1688 if ( ! $welcome_email ) { 1689 /* translators: Do not translate USERNAME, SITE_NAME, BLOG_URL, PASSWORD: those are placeholders. */ 1690 $welcome_email = __( 1691 'Howdy USERNAME, 1692 1693 Your new SITE_NAME site has been successfully set up at: 1694 BLOG_URL 1695 1696 You can log in to the administrator account with the following information: 1697 1698 Username: USERNAME 1699 Password: PASSWORD 1700 Log in here: BLOG_URLwp-login.php 1701 1702 We hope you enjoy your new site. Thanks! 1703 1704 --The Team @ SITE_NAME' 1705 ); 1706 } 1707 1708 $url = get_blogaddress_by_id( $blog_id ); 1709 1710 $welcome_email = str_replace( 'SITE_NAME', $current_network->site_name, $welcome_email ); 1711 $welcome_email = str_replace( 'BLOG_TITLE', $title, $welcome_email ); 1712 $welcome_email = str_replace( 'BLOG_URL', $url, $welcome_email ); 1713 $welcome_email = str_replace( 'USERNAME', $user->user_login, $welcome_email ); 1714 $welcome_email = str_replace( 'PASSWORD', $password, $welcome_email ); 1715 1716 /** 1717 * Filters the content of the welcome email sent to the site administrator after site activation. 1718 * 1719 * Content should be formatted for transmission via wp_mail(). 1720 * 1721 * @since MU (3.0.0) 1722 * 1723 * @param string $welcome_email Message body of the email. 1724 * @param int $blog_id Site ID. 1725 * @param int $user_id User ID of the site administrator. 1726 * @param string $password User password, or "N/A" if the user account is not new. 1727 * @param string $title Site title. 1728 * @param array $meta Signup meta data. By default, contains the requested privacy setting and lang_id. 1729 */ 1730 $welcome_email = apply_filters( 'update_welcome_email', $welcome_email, $blog_id, $user_id, $password, $title, $meta ); 1731 1732 $admin_email = get_site_option( 'admin_email' ); 1733 1734 if ( '' === $admin_email ) { 1735 $admin_email = 'support@' . wp_parse_url( network_home_url(), PHP_URL_HOST ); 1736 } 1737 1738 $from_name = ( '' !== get_site_option( 'site_name' ) ) ? esc_html( get_site_option( 'site_name' ) ) : 'WordPress'; 1739 $message_headers = "From: \"{$from_name}\" <{$admin_email}>\n" . 'Content-Type: text/plain; charset="' . get_option( 'blog_charset' ) . "\"\n"; 1740 $message = $welcome_email; 1741 1742 if ( empty( $current_network->site_name ) ) { 1743 $current_network->site_name = 'WordPress'; 1744 } 1745 1746 /* translators: New site notification email subject. 1: Network title, 2: New site title. */ 1747 $subject = __( 'New %1$s Site: %2$s' ); 1748 1749 /** 1750 * Filters the subject of the welcome email sent to the site administrator after site activation. 1751 * 1752 * @since MU (3.0.0) 1753 * 1754 * @param string $subject Subject of the email. 1755 */ 1756 $subject = apply_filters( 'update_welcome_subject', sprintf( $subject, $current_network->site_name, wp_unslash( $title ) ) ); 1757 1758 wp_mail( $user->user_email, wp_specialchars_decode( $subject ), $message, $message_headers ); 1759 1760 if ( $switched_locale ) { 1761 restore_previous_locale(); 1762 } 1763 1764 return true; 1765 } 1766 1767 /** 1768 * Notifies the Multisite network administrator that a new site was created. 1769 * 1770 * Filter {@see 'send_new_site_email'} to disable or bypass. 1771 * 1772 * Filter {@see 'new_site_email'} to filter the contents. 1773 * 1774 * @since 5.6.0 1775 * 1776 * @param int $site_id Site ID of the new site. 1777 * @param int $user_id User ID of the administrator of the new site. 1778 * @return bool Whether the email notification was sent. 1779 */ 1780 function wpmu_new_site_admin_notification( $site_id, $user_id ) { 1781 $site = get_site( $site_id ); 1782 $user = get_userdata( $user_id ); 1783 $email = get_site_option( 'admin_email' ); 1784 1785 if ( ! $site || ! $user || ! $email ) { 1786 return false; 1787 } 1788 1789 /** 1790 * Filters whether to send an email to the Multisite network administrator when a new site is created. 1791 * 1792 * Return false to disable sending the email. 1793 * 1794 * @since 5.6.0 1795 * 1796 * @param bool $send Whether to send the email. 1797 * @param WP_Site $site Site object of the new site. 1798 * @param WP_User $user User object of the administrator of the new site. 1799 */ 1800 if ( ! apply_filters( 'send_new_site_email', true, $site, $user ) ) { 1801 return false; 1802 } 1803 1804 $switched_locale = false; 1805 $network_admin = get_user_by( 'email', $email ); 1806 1807 if ( $network_admin ) { 1808 // If the network admin email address corresponds to a user, switch to their locale. 1809 $switched_locale = switch_to_user_locale( $network_admin->ID ); 1810 } else { 1811 // Otherwise switch to the locale of the current site. 1812 $switched_locale = switch_to_locale( get_locale() ); 1813 } 1814 1815 $subject = sprintf( 1816 /* translators: New site notification email subject. %s: Network title. */ 1817 __( '[%s] New Site Created' ), 1818 get_network()->site_name 1819 ); 1820 1821 $message = sprintf( 1822 /* translators: New site notification email. 1: User login, 2: Site URL, 3: Site title. */ 1823 __( 1824 'New site created by %1$s 1825 1826 Address: %2$s 1827 Name: %3$s' 1828 ), 1829 $user->user_login, 1830 get_site_url( $site->id ), 1831 get_blog_option( $site->id, 'blogname' ) 1832 ); 1833 1834 $header = sprintf( 1835 'From: "%1$s" <%2$s>', 1836 _x( 'Site Admin', 'email "From" field' ), 1837 $email 1838 ); 1839 1840 $new_site_email = array( 1841 'to' => $email, 1842 'subject' => $subject, 1843 'message' => $message, 1844 'headers' => $header, 1845 ); 1846 1847 /** 1848 * Filters the content of the email sent to the Multisite network administrator when a new site is created. 1849 * 1850 * Content should be formatted for transmission via wp_mail(). 1851 * 1852 * @since 5.6.0 1853 * 1854 * @param array $new_site_email { 1855 * Used to build wp_mail(). 1856 * 1857 * @type string $to The email address of the recipient. 1858 * @type string $subject The subject of the email. 1859 * @type string $message The content of the email. 1860 * @type string $headers Headers. 1861 * } 1862 * @param WP_Site $site Site object of the new site. 1863 * @param WP_User $user User object of the administrator of the new site. 1864 */ 1865 $new_site_email = apply_filters( 'new_site_email', $new_site_email, $site, $user ); 1866 1867 wp_mail( 1868 $new_site_email['to'], 1869 wp_specialchars_decode( $new_site_email['subject'] ), 1870 $new_site_email['message'], 1871 $new_site_email['headers'] 1872 ); 1873 1874 if ( $switched_locale ) { 1875 restore_previous_locale(); 1876 } 1877 1878 return true; 1879 } 1880 1881 /** 1882 * Notifies a user that their account activation has been successful. 1883 * 1884 * Filter {@see 'wpmu_welcome_user_notification'} to disable or bypass. 1885 * 1886 * Filter {@see 'update_welcome_user_email'} and {@see 'update_welcome_user_subject'} to 1887 * modify the content and subject line of the notification email. 1888 * 1889 * @since MU (3.0.0) 1890 * 1891 * @param int $user_id User ID. 1892 * @param string $password User password. 1893 * @param array $meta Optional. Signup meta data. Default empty array. 1894 * @return bool 1895 */ 1896 function wpmu_welcome_user_notification( 1897 $user_id, 1898 #[\SensitiveParameter] 1899 $password, 1900 $meta = array() 1901 ) { 1902 $current_network = get_network(); 1903 1904 /** 1905 * Filters whether to bypass the welcome email after user activation. 1906 * 1907 * Returning false disables the welcome email. 1908 * 1909 * @since MU (3.0.0) 1910 * 1911 * @param int $user_id User ID. 1912 * @param string $password User password. 1913 * @param array $meta Signup meta data. Default empty array. 1914 */ 1915 if ( ! apply_filters( 'wpmu_welcome_user_notification', $user_id, $password, $meta ) ) { 1916 return false; 1917 } 1918 1919 $welcome_email = get_site_option( 'welcome_user_email' ); 1920 1921 $user = get_userdata( $user_id ); 1922 1923 $switched_locale = switch_to_user_locale( $user_id ); 1924 1925 /** 1926 * Filters the content of the welcome email after user activation. 1927 * 1928 * Content should be formatted for transmission via wp_mail(). 1929 * 1930 * @since MU (3.0.0) 1931 * 1932 * @param string $welcome_email The message body of the account activation success email. 1933 * @param int $user_id User ID. 1934 * @param string $password User password. 1935 * @param array $meta Signup meta data. Default empty array. 1936 */ 1937 $welcome_email = apply_filters( 'update_welcome_user_email', $welcome_email, $user_id, $password, $meta ); 1938 $welcome_email = str_replace( 'SITE_NAME', $current_network->site_name, $welcome_email ); 1939 $welcome_email = str_replace( 'USERNAME', $user->user_login, $welcome_email ); 1940 $welcome_email = str_replace( 'PASSWORD', $password, $welcome_email ); 1941 $welcome_email = str_replace( 'LOGINLINK', wp_login_url(), $welcome_email ); 1942 1943 $admin_email = get_site_option( 'admin_email' ); 1944 1945 if ( '' === $admin_email ) { 1946 $admin_email = 'support@' . wp_parse_url( network_home_url(), PHP_URL_HOST ); 1947 } 1948 1949 $from_name = ( '' !== get_site_option( 'site_name' ) ) ? esc_html( get_site_option( 'site_name' ) ) : 'WordPress'; 1950 $message_headers = "From: \"{$from_name}\" <{$admin_email}>\n" . 'Content-Type: text/plain; charset="' . get_option( 'blog_charset' ) . "\"\n"; 1951 $message = $welcome_email; 1952 1953 if ( empty( $current_network->site_name ) ) { 1954 $current_network->site_name = 'WordPress'; 1955 } 1956 1957 /* translators: New user notification email subject. 1: Network title, 2: New user login. */ 1958 $subject = __( 'New %1$s User: %2$s' ); 1959 1960 /** 1961 * Filters the subject of the welcome email after user activation. 1962 * 1963 * @since MU (3.0.0) 1964 * 1965 * @param string $subject Subject of the email. 1966 */ 1967 $subject = apply_filters( 'update_welcome_user_subject', sprintf( $subject, $current_network->site_name, $user->user_login ) ); 1968 1969 wp_mail( $user->user_email, wp_specialchars_decode( $subject ), $message, $message_headers ); 1970 1971 if ( $switched_locale ) { 1972 restore_previous_locale(); 1973 } 1974 1975 return true; 1976 } 1977 1978 /** 1979 * Gets the current network. 1980 * 1981 * Returns an object containing the 'id', 'domain', 'path', and 'site_name' 1982 * properties of the network being viewed. 1983 * 1984 * @see wpmu_current_site() 1985 * 1986 * @since MU (3.0.0) 1987 * 1988 * @global WP_Network $current_site The current network. 1989 * 1990 * @return WP_Network The current network. 1991 */ 1992 function get_current_site() { 1993 global $current_site; 1994 return $current_site; 1995 } 1996 1997 /** 1998 * Gets a user's most recent post. 1999 * 2000 * Walks through each of a user's blogs to find the post with 2001 * the most recent post_date_gmt. 2002 * 2003 * @since MU (3.0.0) 2004 * 2005 * @global wpdb $wpdb WordPress database abstraction object. 2006 * 2007 * @param int $user_id User ID. 2008 * @return array Contains the blog_id, post_id, post_date_gmt, and post_gmt_ts. 2009 */ 2010 function get_most_recent_post_of_user( $user_id ) { 2011 global $wpdb; 2012 2013 $user_blogs = get_blogs_of_user( (int) $user_id ); 2014 $most_recent_post = array(); 2015 2016 /* 2017 * Walk through each blog and get the most recent post 2018 * published by $user_id. 2019 */ 2020 foreach ( (array) $user_blogs as $blog ) { 2021 $prefix = $wpdb->get_blog_prefix( $blog->userblog_id ); 2022 $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 ); 2023 2024 // Make sure we found a post. 2025 if ( isset( $recent_post['ID'] ) ) { 2026 $post_gmt_ts = strtotime( $recent_post['post_date_gmt'] ); 2027 2028 /* 2029 * If this is the first post checked 2030 * or if this post is newer than the current recent post, 2031 * make it the new most recent post. 2032 */ 2033 if ( ! isset( $most_recent_post['post_gmt_ts'] ) || ( $post_gmt_ts > $most_recent_post['post_gmt_ts'] ) ) { 2034 $most_recent_post = array( 2035 'blog_id' => $blog->userblog_id, 2036 'post_id' => $recent_post['ID'], 2037 'post_date_gmt' => $recent_post['post_date_gmt'], 2038 'post_gmt_ts' => $post_gmt_ts, 2039 ); 2040 } 2041 } 2042 } 2043 2044 return $most_recent_post; 2045 } 2046 2047 // 2048 // Misc functions. 2049 // 2050 2051 /** 2052 * Checks an array of MIME types against a list of allowed types. 2053 * 2054 * WordPress ships with a set of allowed upload filetypes, 2055 * which is defined in wp-includes/functions.php in 2056 * get_allowed_mime_types(). This function is used to filter 2057 * that list against the filetypes allowed provided by Multisite 2058 * Super Admins at wp-admin/network/settings.php. 2059 * 2060 * @since MU (3.0.0) 2061 * 2062 * @param array $mimes 2063 * @return array 2064 */ 2065 function check_upload_mimes( $mimes ) { 2066 $site_exts = explode( ' ', get_site_option( 'upload_filetypes', 'jpg jpeg png gif' ) ); 2067 $site_mimes = array(); 2068 foreach ( $site_exts as $ext ) { 2069 foreach ( $mimes as $ext_pattern => $mime ) { 2070 if ( '' !== $ext && str_contains( $ext_pattern, $ext ) ) { 2071 $site_mimes[ $ext_pattern ] = $mime; 2072 } 2073 } 2074 } 2075 return $site_mimes; 2076 } 2077 2078 /** 2079 * Updates a blog's post count. 2080 * 2081 * WordPress MS stores a blog's post count as an option so as 2082 * to avoid extraneous COUNTs when a blog's details are fetched 2083 * with get_site(). This function is called when posts are published 2084 * or unpublished to make sure the count stays current. 2085 * 2086 * @since MU (3.0.0) 2087 * 2088 * @global wpdb $wpdb WordPress database abstraction object. 2089 * 2090 * @param string $deprecated Not used. 2091 */ 2092 function update_posts_count( $deprecated = '' ) { 2093 global $wpdb; 2094 update_option( 'post_count', (int) $wpdb->get_var( "SELECT COUNT(ID) FROM {$wpdb->posts} WHERE post_status = 'publish' and post_type = 'post'" ), true ); 2095 } 2096 2097 /** 2098 * Logs the user email, IP, and registration date of a new site. 2099 * 2100 * @since MU (3.0.0) 2101 * @since 5.1.0 Parameters now support input from the {@see 'wp_initialize_site'} action. 2102 * 2103 * @global wpdb $wpdb WordPress database abstraction object. 2104 * 2105 * @param WP_Site|int $blog_id The new site's object or ID. 2106 * @param int|array $user_id User ID, or array of arguments including 'user_id'. 2107 */ 2108 function wpmu_log_new_registrations( $blog_id, $user_id ) { 2109 global $wpdb; 2110 2111 if ( is_object( $blog_id ) ) { 2112 $blog_id = $blog_id->blog_id; 2113 } 2114 2115 if ( is_array( $user_id ) ) { 2116 $user_id = ! empty( $user_id['user_id'] ) ? $user_id['user_id'] : 0; 2117 } 2118 2119 $user = get_userdata( (int) $user_id ); 2120 if ( $user ) { 2121 $wpdb->insert( 2122 $wpdb->registration_log, 2123 array( 2124 'email' => $user->user_email, 2125 'IP' => preg_replace( '/[^0-9., ]/', '', wp_unslash( $_SERVER['REMOTE_ADDR'] ) ), 2126 'blog_id' => $blog_id, 2127 'date_registered' => current_time( 'mysql' ), 2128 ) 2129 ); 2130 } 2131 } 2132 2133 /** 2134 * Ensures that the current site's domain is listed in the allowed redirect host list. 2135 * 2136 * @see wp_validate_redirect() 2137 * @since MU (3.0.0) 2138 * 2139 * @param array|string $deprecated Not used. 2140 * @return string[] { 2141 * An array containing the current site's domain. 2142 * 2143 * @type string $0 The current site's domain. 2144 * } 2145 */ 2146 function redirect_this_site( $deprecated = '' ) { 2147 return array( get_network()->domain ); 2148 } 2149 2150 /** 2151 * Checks whether an upload is too big. 2152 * 2153 * @since MU (3.0.0) 2154 * 2155 * @param array $upload An array of information about the newly-uploaded file. 2156 * @return string|array If the upload is under the size limit, $upload is returned. Otherwise returns an error message. 2157 */ 2158 function upload_is_file_too_big( $upload ) { 2159 if ( ! is_array( $upload ) || defined( 'WP_IMPORTING' ) || get_site_option( 'upload_space_check_disabled' ) ) { 2160 return $upload; 2161 } 2162 2163 if ( strlen( $upload['bits'] ) > ( KB_IN_BYTES * get_site_option( 'fileupload_maxk', 1500 ) ) ) { 2164 /* translators: %s: Maximum allowed file size in kilobytes. */ 2165 return sprintf( __( 'This file is too big. Files must be less than %s KB in size.' ) . '<br />', get_site_option( 'fileupload_maxk', 1500 ) ); 2166 } 2167 2168 return $upload; 2169 } 2170 2171 /** 2172 * Adds a nonce field to the signup page. 2173 * 2174 * @since MU (3.0.0) 2175 */ 2176 function signup_nonce_fields() { 2177 $id = mt_rand(); 2178 echo "<input type='hidden' name='signup_form_id' value='{$id}' />"; 2179 wp_nonce_field( 'signup_form_' . $id, '_signup_form', false ); 2180 } 2181 2182 /** 2183 * Processes the signup nonce created in signup_nonce_fields(). 2184 * 2185 * @since MU (3.0.0) 2186 * 2187 * @param array $result 2188 * @return array 2189 */ 2190 function signup_nonce_check( $result ) { 2191 if ( ! strpos( $_SERVER['PHP_SELF'], 'wp-signup.php' ) ) { 2192 return $result; 2193 } 2194 2195 if ( ! wp_verify_nonce( $_POST['_signup_form'], 'signup_form_' . $_POST['signup_form_id'] ) ) { 2196 $result['errors']->add( 'invalid_nonce', __( 'Unable to submit this form, please try again.' ) ); 2197 } 2198 2199 return $result; 2200 } 2201 2202 /** 2203 * Corrects 404 redirects when NOBLOGREDIRECT is defined. 2204 * 2205 * @since MU (3.0.0) 2206 */ 2207 function maybe_redirect_404() { 2208 if ( is_main_site() && is_404() && defined( 'NOBLOGREDIRECT' ) ) { 2209 /** 2210 * Filters the redirect URL for 404s on the main site. 2211 * 2212 * The filter is only evaluated if the NOBLOGREDIRECT constant is defined. 2213 * 2214 * @since 3.0.0 2215 * 2216 * @param string $no_blog_redirect The redirect URL defined in NOBLOGREDIRECT. 2217 */ 2218 $destination = apply_filters( 'blog_redirect_404', NOBLOGREDIRECT ); 2219 2220 if ( $destination ) { 2221 if ( '%siteurl%' === $destination ) { 2222 $destination = network_home_url(); 2223 } 2224 2225 wp_redirect( $destination ); 2226 exit; 2227 } 2228 } 2229 } 2230 2231 /** 2232 * Adds a new user to a blog by visiting /newbloguser/{key}/. 2233 * 2234 * This will only work when the user's details are saved as an option 2235 * keyed as 'new_user_{key}', where '{key}' is a hash generated for the user to be 2236 * added, as when a user is invited through the regular WP Add User interface. 2237 * 2238 * @since MU (3.0.0) 2239 */ 2240 function maybe_add_existing_user_to_blog() { 2241 if ( ! str_contains( $_SERVER['REQUEST_URI'], '/newbloguser/' ) ) { 2242 return; 2243 } 2244 2245 $parts = explode( '/', $_SERVER['REQUEST_URI'] ); 2246 $key = array_pop( $parts ); 2247 2248 if ( '' === $key ) { 2249 $key = array_pop( $parts ); 2250 } 2251 2252 $details = get_option( 'new_user_' . $key ); 2253 if ( ! empty( $details ) ) { 2254 delete_option( 'new_user_' . $key ); 2255 } 2256 2257 if ( empty( $details ) || is_wp_error( add_existing_user_to_blog( $details ) ) ) { 2258 wp_die( 2259 sprintf( 2260 /* translators: %s: Home URL. */ 2261 __( 'An error occurred adding you to this site. Go to the <a href="%s">homepage</a>.' ), 2262 home_url() 2263 ) 2264 ); 2265 } 2266 2267 wp_die( 2268 sprintf( 2269 /* translators: 1: Home URL, 2: Admin URL. */ 2270 __( '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.' ), 2271 home_url(), 2272 admin_url() 2273 ), 2274 __( 'WordPress › Success' ), 2275 array( 'response' => 200 ) 2276 ); 2277 } 2278 2279 /** 2280 * Adds a user to a blog based on details from maybe_add_existing_user_to_blog(). 2281 * 2282 * @since MU (3.0.0) 2283 * 2284 * @param array|false $details { 2285 * User details. Must at least contain values for the keys listed below. 2286 * 2287 * @type int $user_id The ID of the user being added to the current blog. 2288 * @type string $role The role to be assigned to the user. 2289 * } 2290 * @return true|WP_Error|null True on success or a WP_Error object if the user doesn't exist 2291 * or could not be added. Null if $details array was not provided. 2292 */ 2293 function add_existing_user_to_blog( $details = false ) { 2294 if ( is_array( $details ) ) { 2295 $blog_id = get_current_blog_id(); 2296 $result = add_user_to_blog( $blog_id, $details['user_id'], $details['role'] ); 2297 2298 /** 2299 * Fires immediately after an existing user is added to a site. 2300 * 2301 * @since MU (3.0.0) 2302 * 2303 * @param int $user_id User ID. 2304 * @param true|WP_Error $result True on success or a WP_Error object if the user doesn't exist 2305 * or could not be added. 2306 */ 2307 do_action( 'added_existing_user', $details['user_id'], $result ); 2308 2309 return $result; 2310 } 2311 return null; 2312 } 2313 2314 /** 2315 * Adds a newly created user to the appropriate blog 2316 * 2317 * To add a user in general, use add_user_to_blog(). This function 2318 * is specifically hooked into the {@see 'wpmu_activate_user'} action. 2319 * 2320 * @since MU (3.0.0) 2321 * 2322 * @see add_user_to_blog() 2323 * 2324 * @param int $user_id User ID. 2325 * @param string $password User password. Ignored. 2326 * @param array $meta Signup meta data. 2327 */ 2328 function add_new_user_to_blog( 2329 $user_id, 2330 #[\SensitiveParameter] 2331 $password, 2332 $meta 2333 ) { 2334 if ( ! empty( $meta['add_to_blog'] ) ) { 2335 $blog_id = $meta['add_to_blog']; 2336 $role = $meta['new_role']; 2337 remove_user_from_blog( $user_id, get_network()->site_id ); // Remove user from main blog. 2338 2339 $result = add_user_to_blog( $blog_id, $user_id, $role ); 2340 2341 if ( ! is_wp_error( $result ) ) { 2342 update_user_meta( $user_id, 'primary_blog', $blog_id ); 2343 } 2344 } 2345 } 2346 2347 /** 2348 * Corrects From host on outgoing mail to match the site domain. 2349 * 2350 * @since MU (3.0.0) 2351 * 2352 * @param PHPMailer\PHPMailer\PHPMailer $phpmailer The PHPMailer instance (passed by reference). 2353 */ 2354 function fix_phpmailer_messageid( $phpmailer ) { 2355 $phpmailer->Hostname = get_network()->domain; 2356 } 2357 2358 /** 2359 * Determines whether a user is marked as a spammer, based on user login. 2360 * 2361 * @since MU (3.0.0) 2362 * 2363 * @param string|WP_User $user Optional. Defaults to current user. WP_User object, 2364 * or user login name as a string. 2365 * @return bool 2366 */ 2367 function is_user_spammy( $user = null ) { 2368 if ( ! ( $user instanceof WP_User ) ) { 2369 if ( $user ) { 2370 $user = get_user_by( 'login', $user ); 2371 } else { 2372 $user = wp_get_current_user(); 2373 } 2374 } 2375 2376 return $user && isset( $user->spam ) && '1' === $user->spam; 2377 } 2378 2379 /** 2380 * Updates this blog's 'public' setting in the global blogs table. 2381 * 2382 * Public blogs have a setting of 1, private blogs are 0. 2383 * 2384 * @since MU (3.0.0) 2385 * 2386 * @param int $old_value The old public value. 2387 * @param int $value The new public value. 2388 */ 2389 function update_blog_public( $old_value, $value ) { 2390 update_blog_status( get_current_blog_id(), 'public', (int) $value ); 2391 } 2392 2393 /** 2394 * Determines whether users can self-register, based on Network settings. 2395 * 2396 * @since MU (3.0.0) 2397 * 2398 * @return bool 2399 */ 2400 function users_can_register_signup_filter() { 2401 $registration = get_site_option( 'registration' ); 2402 return ( 'all' === $registration || 'user' === $registration ); 2403 } 2404 2405 /** 2406 * Ensures that the welcome message is not empty. Currently unused. 2407 * 2408 * @since MU (3.0.0) 2409 * 2410 * @param string $text 2411 * @return string 2412 */ 2413 function welcome_user_msg_filter( $text ) { 2414 if ( ! $text ) { 2415 remove_filter( 'site_option_welcome_user_email', 'welcome_user_msg_filter' ); 2416 2417 /* translators: Do not translate USERNAME, PASSWORD, LOGINLINK, SITE_NAME: those are placeholders. */ 2418 $text = __( 2419 'Howdy USERNAME, 2420 2421 Your new account is set up. 2422 2423 You can log in with the following information: 2424 Username: USERNAME 2425 Password: PASSWORD 2426 LOGINLINK 2427 2428 Thanks! 2429 2430 --The Team @ SITE_NAME' 2431 ); 2432 update_site_option( 'welcome_user_email', $text ); 2433 } 2434 return $text; 2435 } 2436 2437 /** 2438 * Determines whether to force SSL on content. 2439 * 2440 * @since 2.8.5 2441 * 2442 * @param bool|null $force Optional. Whether to force SSL in admin screens. Default null. 2443 * @return bool True if forced, false if not forced. 2444 */ 2445 function force_ssl_content( $force = null ) { 2446 static $forced_content = false; 2447 2448 if ( ! is_null( $force ) ) { 2449 $old_forced = $forced_content; 2450 $forced_content = (bool) $force; 2451 return $old_forced; 2452 } 2453 2454 return $forced_content; 2455 } 2456 2457 /** 2458 * Formats a URL to use https. 2459 * 2460 * Useful as a filter. 2461 * 2462 * @since 2.8.5 2463 * 2464 * @param string $url URL. 2465 * @return string URL with https as the scheme. 2466 */ 2467 function filter_SSL( $url ) { // phpcs:ignore WordPress.NamingConventions.ValidFunctionName.FunctionNameInvalid 2468 if ( ! is_string( $url ) ) { 2469 return get_bloginfo( 'url' ); // Return home site URL with proper scheme. 2470 } 2471 2472 if ( force_ssl_content() && is_ssl() ) { 2473 $url = set_url_scheme( $url, 'https' ); 2474 } 2475 2476 return $url; 2477 } 2478 2479 /** 2480 * Schedules update of the network-wide counts for the current network. 2481 * 2482 * @since 3.1.0 2483 */ 2484 function wp_schedule_update_network_counts() { 2485 if ( ! is_main_site() ) { 2486 return; 2487 } 2488 2489 if ( ! wp_next_scheduled( 'update_network_counts' ) && ! wp_installing() ) { 2490 wp_schedule_event( time(), 'twicedaily', 'update_network_counts' ); 2491 } 2492 } 2493 2494 /** 2495 * Updates the network-wide counts for the current network. 2496 * 2497 * @since 3.1.0 2498 * @since 4.8.0 The `$network_id` parameter has been added. 2499 * 2500 * @param int|null $network_id ID of the network. Default is the current network. 2501 */ 2502 function wp_update_network_counts( $network_id = null ) { 2503 wp_update_network_user_counts( $network_id ); 2504 wp_update_network_site_counts( $network_id ); 2505 } 2506 2507 /** 2508 * Updates the count of sites for the current network. 2509 * 2510 * If enabled through the {@see 'enable_live_network_counts'} filter, update the sites count 2511 * on a network when a site is created or its status is updated. 2512 * 2513 * @since 3.7.0 2514 * @since 4.8.0 The `$network_id` parameter has been added. 2515 * 2516 * @param int|null $network_id ID of the network. Default is the current network. 2517 */ 2518 function wp_maybe_update_network_site_counts( $network_id = null ) { 2519 $is_small_network = ! wp_is_large_network( 'sites', $network_id ); 2520 2521 /** 2522 * Filters whether to update network site or user counts when a new site is created. 2523 * 2524 * @since 3.7.0 2525 * 2526 * @see wp_is_large_network() 2527 * 2528 * @param bool $small_network Whether the network is considered small. 2529 * @param string $context Context. Either 'users' or 'sites'. 2530 */ 2531 if ( ! apply_filters( 'enable_live_network_counts', $is_small_network, 'sites' ) ) { 2532 return; 2533 } 2534 2535 wp_update_network_site_counts( $network_id ); 2536 } 2537 2538 /** 2539 * Updates the network-wide users count. 2540 * 2541 * If enabled through the {@see 'enable_live_network_counts'} filter, update the users count 2542 * on a network when a user is created or its status is updated. 2543 * 2544 * @since 3.7.0 2545 * @since 4.8.0 The `$network_id` parameter has been added. 2546 * 2547 * @param int|null $network_id ID of the network. Default is the current network. 2548 */ 2549 function wp_maybe_update_network_user_counts( $network_id = null ) { 2550 $is_small_network = ! wp_is_large_network( 'users', $network_id ); 2551 2552 /** This filter is documented in wp-includes/ms-functions.php */ 2553 if ( ! apply_filters( 'enable_live_network_counts', $is_small_network, 'users' ) ) { 2554 return; 2555 } 2556 2557 wp_update_network_user_counts( $network_id ); 2558 } 2559 2560 /** 2561 * Updates the network-wide site count. 2562 * 2563 * @since 3.7.0 2564 * @since 4.8.0 The `$network_id` parameter has been added. 2565 * 2566 * @param int|null $network_id ID of the network. Default is the current network. 2567 */ 2568 function wp_update_network_site_counts( $network_id = null ) { 2569 $network_id = (int) $network_id; 2570 if ( ! $network_id ) { 2571 $network_id = get_current_network_id(); 2572 } 2573 2574 $count = get_sites( 2575 array( 2576 'network_id' => $network_id, 2577 'spam' => 0, 2578 'deleted' => 0, 2579 'archived' => 0, 2580 'count' => true, 2581 'update_site_meta_cache' => false, 2582 ) 2583 ); 2584 2585 update_network_option( $network_id, 'blog_count', $count ); 2586 } 2587 2588 /** 2589 * Updates the network-wide user count. 2590 * 2591 * @since 3.7.0 2592 * @since 4.8.0 The `$network_id` parameter has been added. 2593 * @since 6.0.0 This function is now a wrapper for wp_update_user_counts(). 2594 * 2595 * @param int|null $network_id ID of the network. Default is the current network. 2596 */ 2597 function wp_update_network_user_counts( $network_id = null ) { 2598 wp_update_user_counts( $network_id ); 2599 } 2600 2601 /** 2602 * Returns the space used by the current site. 2603 * 2604 * @since 3.5.0 2605 * 2606 * @return int Used space in megabytes. 2607 */ 2608 function get_space_used() { 2609 /** 2610 * Filters the amount of storage space used by the current site, in megabytes. 2611 * 2612 * @since 3.5.0 2613 * 2614 * @param int|false $space_used The amount of used space, in megabytes. Default false. 2615 */ 2616 $space_used = apply_filters( 'pre_get_space_used', false ); 2617 2618 if ( false === $space_used ) { 2619 $upload_dir = wp_upload_dir(); 2620 $space_used = get_dirsize( $upload_dir['basedir'] ) / MB_IN_BYTES; 2621 } 2622 2623 return $space_used; 2624 } 2625 2626 /** 2627 * Returns the upload quota for the current blog. 2628 * 2629 * @since MU (3.0.0) 2630 * 2631 * @return int Quota in megabytes. 2632 */ 2633 function get_space_allowed() { 2634 $space_allowed = get_option( 'blog_upload_space' ); 2635 2636 if ( ! is_numeric( $space_allowed ) ) { 2637 $space_allowed = get_site_option( 'blog_upload_space' ); 2638 } 2639 2640 if ( ! is_numeric( $space_allowed ) ) { 2641 $space_allowed = 100; 2642 } 2643 2644 /** 2645 * Filters the upload quota for the current site. 2646 * 2647 * @since 3.7.0 2648 * 2649 * @param int $space_allowed Upload quota in megabytes for the current blog. 2650 */ 2651 return apply_filters( 'get_space_allowed', $space_allowed ); 2652 } 2653 2654 /** 2655 * Determines if there is any upload space left in the current blog's quota. 2656 * 2657 * @since 3.0.0 2658 * 2659 * @return int of upload space available in bytes. 2660 */ 2661 function get_upload_space_available() { 2662 $allowed = get_space_allowed(); 2663 if ( $allowed < 0 ) { 2664 $allowed = 0; 2665 } 2666 $space_allowed = $allowed * MB_IN_BYTES; 2667 if ( get_site_option( 'upload_space_check_disabled' ) ) { 2668 return $space_allowed; 2669 } 2670 2671 $space_used = get_space_used() * MB_IN_BYTES; 2672 2673 if ( ( $space_allowed - $space_used ) <= 0 ) { 2674 return 0; 2675 } 2676 2677 return $space_allowed - $space_used; 2678 } 2679 2680 /** 2681 * Determines if there is any upload space left in the current blog's quota. 2682 * 2683 * @since 3.0.0 2684 * @return bool True if space is available, false otherwise. 2685 */ 2686 function is_upload_space_available() { 2687 if ( get_site_option( 'upload_space_check_disabled' ) ) { 2688 return true; 2689 } 2690 2691 return (bool) get_upload_space_available(); 2692 } 2693 2694 /** 2695 * Filters the maximum upload file size allowed, in bytes. 2696 * 2697 * @since 3.0.0 2698 * 2699 * @param int $size Upload size limit in bytes. 2700 * @return int Upload size limit in bytes. 2701 */ 2702 function upload_size_limit_filter( $size ) { 2703 $fileupload_maxk = (int) get_site_option( 'fileupload_maxk', 1500 ); 2704 $max_fileupload_in_bytes = KB_IN_BYTES * $fileupload_maxk; 2705 2706 if ( get_site_option( 'upload_space_check_disabled' ) ) { 2707 return min( $size, $max_fileupload_in_bytes ); 2708 } 2709 2710 return min( $size, $max_fileupload_in_bytes, get_upload_space_available() ); 2711 } 2712 2713 /** 2714 * Determines whether or not we have a large network. 2715 * 2716 * The default criteria for a large network is either more than 10,000 users or more than 10,000 sites. 2717 * Plugins can alter this criteria using the {@see 'wp_is_large_network'} filter. 2718 * 2719 * @since 3.3.0 2720 * @since 4.8.0 The `$network_id` parameter has been added. 2721 * 2722 * @param string $using 'sites' or 'users'. Default is 'sites'. 2723 * @param int|null $network_id ID of the network. Default is the current network. 2724 * @return bool True if the network meets the criteria for large. False otherwise. 2725 */ 2726 function wp_is_large_network( $using = 'sites', $network_id = null ) { 2727 $network_id = (int) $network_id; 2728 if ( ! $network_id ) { 2729 $network_id = get_current_network_id(); 2730 } 2731 2732 if ( 'users' === $using ) { 2733 $count = get_user_count( $network_id ); 2734 2735 $is_large_network = wp_is_large_user_count( $network_id ); 2736 2737 /** 2738 * Filters whether the network is considered large. 2739 * 2740 * @since 3.3.0 2741 * @since 4.8.0 The `$network_id` parameter has been added. 2742 * 2743 * @param bool $is_large_network Whether the network has more than 10000 users or sites. 2744 * @param string $component The component to count. Accepts 'users', or 'sites'. 2745 * @param int $count The count of items for the component. 2746 * @param int $network_id The ID of the network being checked. 2747 */ 2748 return apply_filters( 'wp_is_large_network', $is_large_network, 'users', $count, $network_id ); 2749 } 2750 2751 $count = get_blog_count( $network_id ); 2752 2753 /** This filter is documented in wp-includes/ms-functions.php */ 2754 return apply_filters( 'wp_is_large_network', $count > 10000, 'sites', $count, $network_id ); 2755 } 2756 2757 /** 2758 * Retrieves a list of reserved site on a sub-directory Multisite installation. 2759 * 2760 * @since 4.4.0 2761 * 2762 * @return string[] Array of reserved names. 2763 */ 2764 function get_subdirectory_reserved_names() { 2765 $names = array( 2766 'page', 2767 'comments', 2768 'blog', 2769 'files', 2770 'feed', 2771 'wp-admin', 2772 'wp-content', 2773 'wp-includes', 2774 'wp-json', 2775 'embed', 2776 ); 2777 2778 /** 2779 * Filters reserved site names on a sub-directory Multisite installation. 2780 * 2781 * @since 3.0.0 2782 * @since 4.4.0 'wp-admin', 'wp-content', 'wp-includes', 'wp-json', and 'embed' were added 2783 * to the reserved names list. 2784 * 2785 * @param string[] $subdirectory_reserved_names Array of reserved names. 2786 */ 2787 return apply_filters( 'subdirectory_reserved_names', $names ); 2788 } 2789 2790 /** 2791 * Sends a confirmation request email when a change of network admin email address is attempted. 2792 * 2793 * The new network admin address will not become active until confirmed. 2794 * 2795 * @since 4.9.0 2796 * 2797 * @param string $old_value The old network admin email address. 2798 * @param string $value The proposed new network admin email address. 2799 */ 2800 function update_network_option_new_admin_email( $old_value, $value ) { 2801 if ( get_site_option( 'admin_email' ) === $value || ! is_email( $value ) ) { 2802 return; 2803 } 2804 2805 $hash = md5( $value . time() . mt_rand() ); 2806 $new_admin_email = array( 2807 'hash' => $hash, 2808 'newemail' => $value, 2809 ); 2810 update_site_option( 'network_admin_hash', $new_admin_email ); 2811 2812 $switched_locale = switch_to_user_locale( get_current_user_id() ); 2813 2814 /* translators: Do not translate USERNAME, ADMIN_URL, EMAIL, SITENAME, SITEURL: those are placeholders. */ 2815 $email_text = __( 2816 'Howdy ###USERNAME###, 2817 2818 You recently requested to have the network admin email address on 2819 your network changed. 2820 2821 If this is correct, please click on the following link to change it: 2822 ###ADMIN_URL### 2823 2824 You can safely ignore and delete this email if you do not want to 2825 take this action. 2826 2827 This email has been sent to ###EMAIL### 2828 2829 Regards, 2830 All at ###SITENAME### 2831 ###SITEURL###' 2832 ); 2833 2834 /** 2835 * Filters the text of the email sent when a change of network admin email address is attempted. 2836 * 2837 * The following strings have a special meaning and will get replaced dynamically: 2838 * 2839 * - `###USERNAME###` The current user's username. 2840 * - `###ADMIN_URL###` The link to click on to confirm the email change. 2841 * - `###EMAIL###` The proposed new network admin email address. 2842 * - `###SITENAME###` The name of the network. 2843 * - `###SITEURL###` The URL to the network. 2844 * 2845 * @since 4.9.0 2846 * 2847 * @param string $email_text Text in the email. 2848 * @param array $new_admin_email { 2849 * Data relating to the new network admin email address. 2850 * 2851 * @type string $hash The secure hash used in the confirmation link URL. 2852 * @type string $newemail The proposed new network admin email address. 2853 * } 2854 */ 2855 $content = apply_filters( 'new_network_admin_email_content', $email_text, $new_admin_email ); 2856 2857 $current_user = wp_get_current_user(); 2858 $content = str_replace( '###USERNAME###', $current_user->user_login, $content ); 2859 $content = str_replace( '###ADMIN_URL###', esc_url( network_admin_url( 'settings.php?network_admin_hash=' . $hash ) ), $content ); 2860 $content = str_replace( '###EMAIL###', $value, $content ); 2861 $content = str_replace( '###SITENAME###', wp_specialchars_decode( get_site_option( 'site_name' ), ENT_QUOTES ), $content ); 2862 $content = str_replace( '###SITEURL###', network_home_url(), $content ); 2863 2864 wp_mail( 2865 $value, 2866 sprintf( 2867 /* translators: Email change notification email subject. %s: Network title. */ 2868 __( '[%s] Network Admin Email Change Request' ), 2869 wp_specialchars_decode( get_site_option( 'site_name' ), ENT_QUOTES ) 2870 ), 2871 $content 2872 ); 2873 2874 if ( $switched_locale ) { 2875 restore_previous_locale(); 2876 } 2877 } 2878 2879 /** 2880 * Sends an email to the old network admin email address when the network admin email address changes. 2881 * 2882 * @since 4.9.0 2883 * 2884 * @param string $option_name The relevant database option name. 2885 * @param string $new_email The new network admin email address. 2886 * @param string $old_email The old network admin email address. 2887 * @param int $network_id ID of the network. 2888 */ 2889 function wp_network_admin_email_change_notification( $option_name, $new_email, $old_email, $network_id ) { 2890 $send = true; 2891 2892 // Don't send the notification for an empty email address or the default 'admin_email' value. 2893 if ( empty( $old_email ) || 'you@example.com' === $old_email ) { 2894 $send = false; 2895 } 2896 2897 /** 2898 * Filters whether to send the network admin email change notification email. 2899 * 2900 * @since 4.9.0 2901 * 2902 * @param bool $send Whether to send the email notification. 2903 * @param string $old_email The old network admin email address. 2904 * @param string $new_email The new network admin email address. 2905 * @param int $network_id ID of the network. 2906 */ 2907 $send = apply_filters( 'send_network_admin_email_change_email', $send, $old_email, $new_email, $network_id ); 2908 2909 if ( ! $send ) { 2910 return; 2911 } 2912 2913 /* translators: Do not translate OLD_EMAIL, NEW_EMAIL, SITENAME, SITEURL: those are placeholders. */ 2914 $email_change_text = __( 2915 'Hi, 2916 2917 This notice confirms that the network admin email address was changed on ###SITENAME###. 2918 2919 The new network admin email address is ###NEW_EMAIL###. 2920 2921 This email has been sent to ###OLD_EMAIL### 2922 2923 Regards, 2924 All at ###SITENAME### 2925 ###SITEURL###' 2926 ); 2927 2928 $email_change_email = array( 2929 'to' => $old_email, 2930 /* translators: Network admin email change notification email subject. %s: Network title. */ 2931 'subject' => __( '[%s] Network Admin Email Changed' ), 2932 'message' => $email_change_text, 2933 'headers' => '', 2934 ); 2935 // Get network name. 2936 $network_name = wp_specialchars_decode( get_site_option( 'site_name' ), ENT_QUOTES ); 2937 2938 /** 2939 * Filters the contents of the email notification sent when the network admin email address is changed. 2940 * 2941 * @since 4.9.0 2942 * 2943 * @param array $email_change_email { 2944 * Used to build wp_mail(). 2945 * 2946 * @type string $to The intended recipient. 2947 * @type string $subject The subject of the email. 2948 * @type string $message The content of the email. 2949 * The following strings have a special meaning and will get replaced dynamically: 2950 * - `###OLD_EMAIL###` The old network admin email address. 2951 * - `###NEW_EMAIL###` The new network admin email address. 2952 * - `###SITENAME###` The name of the network. 2953 * - `###SITEURL###` The URL to the site. 2954 * @type string $headers Headers. 2955 * } 2956 * @param string $old_email The old network admin email address. 2957 * @param string $new_email The new network admin email address. 2958 * @param int $network_id ID of the network. 2959 */ 2960 $email_change_email = apply_filters( 'network_admin_email_change_email', $email_change_email, $old_email, $new_email, $network_id ); 2961 2962 $email_change_email['message'] = str_replace( '###OLD_EMAIL###', $old_email, $email_change_email['message'] ); 2963 $email_change_email['message'] = str_replace( '###NEW_EMAIL###', $new_email, $email_change_email['message'] ); 2964 $email_change_email['message'] = str_replace( '###SITENAME###', $network_name, $email_change_email['message'] ); 2965 $email_change_email['message'] = str_replace( '###SITEURL###', home_url(), $email_change_email['message'] ); 2966 2967 wp_mail( 2968 $email_change_email['to'], 2969 sprintf( 2970 $email_change_email['subject'], 2971 $network_name 2972 ), 2973 $email_change_email['message'], 2974 $email_change_email['headers'] 2975 ); 2976 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
| Generated : Thu Jul 16 08:20:16 2026 | Cross-referenced by PHPXref |