[ Index ]

PHP Cross Reference of WordPress Trunk (Updated Daily)

Search

title

Body

[close]

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

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


Generated : Wed Oct 14 08:20:33 2026 Cross-referenced by PHPXref