[ Index ]

PHP Cross Reference of WordPress Trunk (Updated Daily)

Search

title

Body

[close]

/wp-includes/ -> ms-functions.php (source)

   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 &rsaquo; 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  }


Generated : Thu Jul 16 08:20:16 2026 Cross-referenced by PHPXref