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


Generated : Wed Jun 24 08:20:11 2026 Cross-referenced by PHPXref