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


Generated : Tue Jan 21 08:20:01 2025 Cross-referenced by PHPXref