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


Generated : Fri Feb 21 08:20:01 2025 Cross-referenced by PHPXref