ms-site.php (39587B)
1 <?php 2 /** 3 * Site API 4 * 5 * @package WordPress 6 * @subpackage Multisite 7 * @since 5.1.0 8 */ 9 10 /** 11 * Inserts a new site into the database. 12 * 13 * @since 5.1.0 14 * 15 * @global wpdb $wpdb WordPress database abstraction object. 16 * 17 * @param array $data { 18 * Data for the new site that should be inserted. 19 * 20 * @type string $domain Site domain. Default empty string. 21 * @type string $path Site path. Default '/'. 22 * @type int $network_id The site's network ID. Default is the current network ID. 23 * @type string $registered When the site was registered, in SQL datetime format. Default is 24 * the current time. 25 * @type string $last_updated When the site was last updated, in SQL datetime format. Default is 26 * the value of $registered. 27 * @type int $public Whether the site is public. Default 1. 28 * @type int $archived Whether the site is archived. Default 0. 29 * @type int $mature Whether the site is mature. Default 0. 30 * @type int $spam Whether the site is spam. Default 0. 31 * @type int $deleted Whether the site is deleted. Default 0. 32 * @type int $lang_id The site's language ID. Currently unused. Default 0. 33 * @type int $user_id User ID for the site administrator. Passed to the 34 * `wp_initialize_site` hook. 35 * @type string $title Site title. Default is 'Site %d' where %d is the site ID. Passed 36 * to the `wp_initialize_site` hook. 37 * @type array $options Custom option $key => $value pairs to use. Default empty array. Passed 38 * to the `wp_initialize_site` hook. 39 * @type array $meta Custom site metadata $key => $value pairs to use. Default empty array. 40 * Passed to the `wp_initialize_site` hook. 41 * } 42 * @return int|WP_Error The new site's ID on success, or error object on failure. 43 */ 44 function wp_insert_site( array $data ) { 45 global $wpdb; 46 47 $now = current_time( 'mysql', true ); 48 49 $defaults = array( 50 'domain' => '', 51 'path' => '/', 52 'network_id' => get_current_network_id(), 53 'registered' => $now, 54 'last_updated' => $now, 55 'public' => 1, 56 'archived' => 0, 57 'mature' => 0, 58 'spam' => 0, 59 'deleted' => 0, 60 'lang_id' => 0, 61 ); 62 63 $prepared_data = wp_prepare_site_data( $data, $defaults ); 64 if ( is_wp_error( $prepared_data ) ) { 65 return $prepared_data; 66 } 67 68 if ( false === $wpdb->insert( $wpdb->blogs, $prepared_data ) ) { 69 return new WP_Error( 'db_insert_error', __( 'Could not insert site into the database.' ), $wpdb->last_error ); 70 } 71 72 $site_id = (int) $wpdb->insert_id; 73 74 clean_blog_cache( $site_id ); 75 76 $new_site = get_site( $site_id ); 77 78 if ( ! $new_site ) { 79 return new WP_Error( 'get_site_error', __( 'Could not retrieve site data.' ) ); 80 } 81 82 /** 83 * Fires once a site has been inserted into the database. 84 * 85 * @since 5.1.0 86 * 87 * @param WP_Site $new_site New site object. 88 */ 89 do_action( 'wp_insert_site', $new_site ); 90 91 // Extract the passed arguments that may be relevant for site initialization. 92 $args = array_diff_key( $data, $defaults ); 93 if ( isset( $args['site_id'] ) ) { 94 unset( $args['site_id'] ); 95 } 96 97 /** 98 * Fires when a site's initialization routine should be executed. 99 * 100 * @since 5.1.0 101 * 102 * @param WP_Site $new_site New site object. 103 * @param array $args Arguments for the initialization. 104 */ 105 do_action( 'wp_initialize_site', $new_site, $args ); 106 107 // Only compute extra hook parameters if the deprecated hook is actually in use. 108 if ( has_action( 'wpmu_new_blog' ) ) { 109 $user_id = ! empty( $args['user_id'] ) ? $args['user_id'] : 0; 110 $meta = ! empty( $args['options'] ) ? $args['options'] : array(); 111 112 // WPLANG was passed with `$meta` to the `wpmu_new_blog` hook prior to 5.1.0. 113 if ( ! array_key_exists( 'WPLANG', $meta ) ) { 114 $meta['WPLANG'] = get_network_option( $new_site->network_id, 'WPLANG' ); 115 } 116 117 // Rebuild the data expected by the `wpmu_new_blog` hook prior to 5.1.0 using allowed keys. 118 // The `$allowed_data_fields` matches the one used in `wpmu_create_blog()`. 119 $allowed_data_fields = array( 'public', 'archived', 'mature', 'spam', 'deleted', 'lang_id' ); 120 $meta = array_merge( array_intersect_key( $data, array_flip( $allowed_data_fields ) ), $meta ); 121 122 /** 123 * Fires immediately after a new site is created. 124 * 125 * @since MU (3.0.0) 126 * @deprecated 5.1.0 Use {@see 'wp_insert_site'} instead. 127 * 128 * @param int $site_id Site ID. 129 * @param int $user_id User ID. 130 * @param string $domain Site domain. 131 * @param string $path Site path. 132 * @param int $network_id Network ID. Only relevant on multi-network installations. 133 * @param array $meta Meta data. Used to set initial site options. 134 */ 135 do_action_deprecated( 136 'wpmu_new_blog', 137 array( $new_site->id, $user_id, $new_site->domain, $new_site->path, $new_site->network_id, $meta ), 138 '5.1.0', 139 'wp_insert_site' 140 ); 141 } 142 143 return (int) $new_site->id; 144 } 145 146 /** 147 * Updates a site in the database. 148 * 149 * @since 5.1.0 150 * 151 * @global wpdb $wpdb WordPress database abstraction object. 152 * 153 * @param int $site_id ID of the site that should be updated. 154 * @param array $data Site data to update. See {@see wp_insert_site()} for the list of supported keys. 155 * @return int|WP_Error The updated site's ID on success, or error object on failure. 156 */ 157 function wp_update_site( $site_id, array $data ) { 158 global $wpdb; 159 160 if ( empty( $site_id ) ) { 161 return new WP_Error( 'site_empty_id', __( 'Site ID must not be empty.' ) ); 162 } 163 164 $old_site = get_site( $site_id ); 165 if ( ! $old_site ) { 166 return new WP_Error( 'site_not_exist', __( 'Site does not exist.' ) ); 167 } 168 169 $defaults = $old_site->to_array(); 170 $defaults['network_id'] = (int) $defaults['site_id']; 171 $defaults['last_updated'] = current_time( 'mysql', true ); 172 unset( $defaults['blog_id'], $defaults['site_id'] ); 173 174 $data = wp_prepare_site_data( $data, $defaults, $old_site ); 175 if ( is_wp_error( $data ) ) { 176 return $data; 177 } 178 179 if ( false === $wpdb->update( $wpdb->blogs, $data, array( 'blog_id' => $old_site->id ) ) ) { 180 return new WP_Error( 'db_update_error', __( 'Could not update site in the database.' ), $wpdb->last_error ); 181 } 182 183 clean_blog_cache( $old_site ); 184 185 $new_site = get_site( $old_site->id ); 186 187 /** 188 * Fires once a site has been updated in the database. 189 * 190 * @since 5.1.0 191 * 192 * @param WP_Site $new_site New site object. 193 * @param WP_Site $old_site Old site object. 194 */ 195 do_action( 'wp_update_site', $new_site, $old_site ); 196 197 return (int) $new_site->id; 198 } 199 200 /** 201 * Deletes a site from the database. 202 * 203 * @since 5.1.0 204 * 205 * @global wpdb $wpdb WordPress database abstraction object. 206 * 207 * @param int $site_id ID of the site that should be deleted. 208 * @return WP_Site|WP_Error The deleted site object on success, or error object on failure. 209 */ 210 function wp_delete_site( $site_id ) { 211 global $wpdb; 212 213 if ( empty( $site_id ) ) { 214 return new WP_Error( 'site_empty_id', __( 'Site ID must not be empty.' ) ); 215 } 216 217 $old_site = get_site( $site_id ); 218 if ( ! $old_site ) { 219 return new WP_Error( 'site_not_exist', __( 'Site does not exist.' ) ); 220 } 221 222 $errors = new WP_Error(); 223 224 /** 225 * Fires before a site should be deleted from the database. 226 * 227 * Plugins should amend the `$errors` object via its `WP_Error::add()` method. If any errors 228 * are present, the site will not be deleted. 229 * 230 * @since 5.1.0 231 * 232 * @param WP_Error $errors Error object to add validation errors to. 233 * @param WP_Site $old_site The site object to be deleted. 234 */ 235 do_action( 'wp_validate_site_deletion', $errors, $old_site ); 236 237 if ( ! empty( $errors->errors ) ) { 238 return $errors; 239 } 240 241 /** 242 * Fires before a site is deleted. 243 * 244 * @since MU (3.0.0) 245 * @deprecated 5.1.0 246 * 247 * @param int $site_id The site ID. 248 * @param bool $drop True if site's table should be dropped. Default false. 249 */ 250 do_action_deprecated( 'delete_blog', array( $old_site->id, true ), '5.1.0' ); 251 252 /** 253 * Fires when a site's uninitialization routine should be executed. 254 * 255 * @since 5.1.0 256 * 257 * @param WP_Site $old_site Deleted site object. 258 */ 259 do_action( 'wp_uninitialize_site', $old_site ); 260 261 if ( is_site_meta_supported() ) { 262 $blog_meta_ids = $wpdb->get_col( $wpdb->prepare( "SELECT meta_id FROM $wpdb->blogmeta WHERE blog_id = %d ", $old_site->id ) ); 263 foreach ( $blog_meta_ids as $mid ) { 264 delete_metadata_by_mid( 'blog', $mid ); 265 } 266 } 267 268 if ( false === $wpdb->delete( $wpdb->blogs, array( 'blog_id' => $old_site->id ) ) ) { 269 return new WP_Error( 'db_delete_error', __( 'Could not delete site from the database.' ), $wpdb->last_error ); 270 } 271 272 clean_blog_cache( $old_site ); 273 274 /** 275 * Fires once a site has been deleted from the database. 276 * 277 * @since 5.1.0 278 * 279 * @param WP_Site $old_site Deleted site object. 280 */ 281 do_action( 'wp_delete_site', $old_site ); 282 283 /** 284 * Fires after the site is deleted from the network. 285 * 286 * @since 4.8.0 287 * @deprecated 5.1.0 288 * 289 * @param int $site_id The site ID. 290 * @param bool $drop True if site's tables should be dropped. Default false. 291 */ 292 do_action_deprecated( 'deleted_blog', array( $old_site->id, true ), '5.1.0' ); 293 294 return $old_site; 295 } 296 297 /** 298 * Retrieves site data given a site ID or site object. 299 * 300 * Site data will be cached and returned after being passed through a filter. 301 * If the provided site is empty, the current site global will be used. 302 * 303 * @since 4.6.0 304 * 305 * @param WP_Site|int|null $site Optional. Site to retrieve. Default is the current site. 306 * @return WP_Site|null The site object or null if not found. 307 */ 308 function get_site( $site = null ) { 309 if ( empty( $site ) ) { 310 $site = get_current_blog_id(); 311 } 312 313 if ( $site instanceof WP_Site ) { 314 $_site = $site; 315 } elseif ( is_object( $site ) ) { 316 $_site = new WP_Site( $site ); 317 } else { 318 $_site = WP_Site::get_instance( $site ); 319 } 320 321 if ( ! $_site ) { 322 return null; 323 } 324 325 /** 326 * Fires after a site is retrieved. 327 * 328 * @since 4.6.0 329 * 330 * @param WP_Site $_site Site data. 331 */ 332 $_site = apply_filters( 'get_site', $_site ); 333 334 return $_site; 335 } 336 337 /** 338 * Adds any sites from the given IDs to the cache that do not already exist in cache. 339 * 340 * @since 4.6.0 341 * @since 5.1.0 Introduced the `$update_meta_cache` parameter. 342 * @access private 343 * 344 * @see update_site_cache() 345 * @global wpdb $wpdb WordPress database abstraction object. 346 * 347 * @param array $ids ID list. 348 * @param bool $update_meta_cache Optional. Whether to update the meta cache. Default true. 349 */ 350 function _prime_site_caches( $ids, $update_meta_cache = true ) { 351 global $wpdb; 352 353 $non_cached_ids = _get_non_cached_ids( $ids, 'sites' ); 354 if ( ! empty( $non_cached_ids ) ) { 355 $fresh_sites = $wpdb->get_results( sprintf( "SELECT * FROM $wpdb->blogs WHERE blog_id IN (%s)", implode( ',', array_map( 'intval', $non_cached_ids ) ) ) ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared 356 357 update_site_cache( $fresh_sites, $update_meta_cache ); 358 } 359 } 360 361 /** 362 * Updates sites in cache. 363 * 364 * @since 4.6.0 365 * @since 5.1.0 Introduced the `$update_meta_cache` parameter. 366 * 367 * @param array $sites Array of site objects. 368 * @param bool $update_meta_cache Whether to update site meta cache. Default true. 369 */ 370 function update_site_cache( $sites, $update_meta_cache = true ) { 371 if ( ! $sites ) { 372 return; 373 } 374 $site_ids = array(); 375 foreach ( $sites as $site ) { 376 $site_ids[] = $site->blog_id; 377 wp_cache_add( $site->blog_id, $site, 'sites' ); 378 wp_cache_add( $site->blog_id . 'short', $site, 'blog-details' ); 379 } 380 381 if ( $update_meta_cache ) { 382 update_sitemeta_cache( $site_ids ); 383 } 384 } 385 386 /** 387 * Updates metadata cache for list of site IDs. 388 * 389 * Performs SQL query to retrieve all metadata for the sites matching `$site_ids` and stores them in the cache. 390 * Subsequent calls to `get_site_meta()` will not need to query the database. 391 * 392 * @since 5.1.0 393 * 394 * @param array $site_ids List of site IDs. 395 * @return array|false An array of metadata on success, false if there is nothing to update. 396 */ 397 function update_sitemeta_cache( $site_ids ) { 398 // Ensure this filter is hooked in even if the function is called early. 399 if ( ! has_filter( 'update_blog_metadata_cache', 'wp_check_site_meta_support_prefilter' ) ) { 400 add_filter( 'update_blog_metadata_cache', 'wp_check_site_meta_support_prefilter' ); 401 } 402 return update_meta_cache( 'blog', $site_ids ); 403 } 404 405 /** 406 * Retrieves a list of sites matching requested arguments. 407 * 408 * @since 4.6.0 409 * @since 4.8.0 Introduced the 'lang_id', 'lang__in', and 'lang__not_in' parameters. 410 * 411 * @see WP_Site_Query::parse_query() 412 * 413 * @param string|array $args Optional. Array or string of arguments. See WP_Site_Query::__construct() 414 * for information on accepted arguments. Default empty array. 415 * @return array|int List of WP_Site objects, a list of site IDs when 'fields' is set to 'ids', 416 * or the number of sites when 'count' is passed as a query var. 417 */ 418 function get_sites( $args = array() ) { 419 $query = new WP_Site_Query(); 420 421 return $query->query( $args ); 422 } 423 424 /** 425 * Prepares site data for insertion or update in the database. 426 * 427 * @since 5.1.0 428 * 429 * @param array $data Associative array of site data passed to the respective function. 430 * See {@see wp_insert_site()} for the possibly included data. 431 * @param array $defaults Site data defaults to parse $data against. 432 * @param WP_Site|null $old_site Optional. Old site object if an update, or null if an insertion. 433 * Default null. 434 * @return array|WP_Error Site data ready for a database transaction, or WP_Error in case a validation 435 * error occurred. 436 */ 437 function wp_prepare_site_data( $data, $defaults, $old_site = null ) { 438 439 // Maintain backward-compatibility with `$site_id` as network ID. 440 if ( isset( $data['site_id'] ) ) { 441 if ( ! empty( $data['site_id'] ) && empty( $data['network_id'] ) ) { 442 $data['network_id'] = $data['site_id']; 443 } 444 unset( $data['site_id'] ); 445 } 446 447 /** 448 * Filters passed site data in order to normalize it. 449 * 450 * @since 5.1.0 451 * 452 * @param array $data Associative array of site data passed to the respective function. 453 * See {@see wp_insert_site()} for the possibly included data. 454 */ 455 $data = apply_filters( 'wp_normalize_site_data', $data ); 456 457 $allowed_data_fields = array( 'domain', 'path', 'network_id', 'registered', 'last_updated', 'public', 'archived', 'mature', 'spam', 'deleted', 'lang_id' ); 458 $data = array_intersect_key( wp_parse_args( $data, $defaults ), array_flip( $allowed_data_fields ) ); 459 460 $errors = new WP_Error(); 461 462 /** 463 * Fires when data should be validated for a site prior to inserting or updating in the database. 464 * 465 * Plugins should amend the `$errors` object via its `WP_Error::add()` method. 466 * 467 * @since 5.1.0 468 * 469 * @param WP_Error $errors Error object to add validation errors to. 470 * @param array $data Associative array of complete site data. See {@see wp_insert_site()} 471 * for the included data. 472 * @param WP_Site|null $old_site The old site object if the data belongs to a site being updated, 473 * or null if it is a new site being inserted. 474 */ 475 do_action( 'wp_validate_site_data', $errors, $data, $old_site ); 476 477 if ( ! empty( $errors->errors ) ) { 478 return $errors; 479 } 480 481 // Prepare for database. 482 $data['site_id'] = $data['network_id']; 483 unset( $data['network_id'] ); 484 485 return $data; 486 } 487 488 /** 489 * Normalizes data for a site prior to inserting or updating in the database. 490 * 491 * @since 5.1.0 492 * 493 * @param array $data Associative array of site data passed to the respective function. 494 * See {@see wp_insert_site()} for the possibly included data. 495 * @return array Normalized site data. 496 */ 497 function wp_normalize_site_data( $data ) { 498 // Sanitize domain if passed. 499 if ( array_key_exists( 'domain', $data ) ) { 500 $data['domain'] = trim( $data['domain'] ); 501 $data['domain'] = preg_replace( '/\s+/', '', sanitize_user( $data['domain'], true ) ); 502 if ( is_subdomain_install() ) { 503 $data['domain'] = str_replace( '@', '', $data['domain'] ); 504 } 505 } 506 507 // Sanitize path if passed. 508 if ( array_key_exists( 'path', $data ) ) { 509 $data['path'] = trailingslashit( '/' . trim( $data['path'], '/' ) ); 510 } 511 512 // Sanitize network ID if passed. 513 if ( array_key_exists( 'network_id', $data ) ) { 514 $data['network_id'] = (int) $data['network_id']; 515 } 516 517 // Sanitize status fields if passed. 518 $status_fields = array( 'public', 'archived', 'mature', 'spam', 'deleted' ); 519 foreach ( $status_fields as $status_field ) { 520 if ( array_key_exists( $status_field, $data ) ) { 521 $data[ $status_field ] = (int) $data[ $status_field ]; 522 } 523 } 524 525 // Strip date fields if empty. 526 $date_fields = array( 'registered', 'last_updated' ); 527 foreach ( $date_fields as $date_field ) { 528 if ( ! array_key_exists( $date_field, $data ) ) { 529 continue; 530 } 531 532 if ( empty( $data[ $date_field ] ) || '0000-00-00 00:00:00' === $data[ $date_field ] ) { 533 unset( $data[ $date_field ] ); 534 } 535 } 536 537 return $data; 538 } 539 540 /** 541 * Validates data for a site prior to inserting or updating in the database. 542 * 543 * @since 5.1.0 544 * 545 * @param WP_Error $errors Error object, passed by reference. Will contain validation errors if 546 * any occurred. 547 * @param array $data Associative array of complete site data. See {@see wp_insert_site()} 548 * for the included data. 549 * @param WP_Site|null $old_site The old site object if the data belongs to a site being updated, 550 * or null if it is a new site being inserted. 551 */ 552 function wp_validate_site_data( $errors, $data, $old_site = null ) { 553 // A domain must always be present. 554 if ( empty( $data['domain'] ) ) { 555 $errors->add( 'site_empty_domain', __( 'Site domain must not be empty.' ) ); 556 } 557 558 // A path must always be present. 559 if ( empty( $data['path'] ) ) { 560 $errors->add( 'site_empty_path', __( 'Site path must not be empty.' ) ); 561 } 562 563 // A network ID must always be present. 564 if ( empty( $data['network_id'] ) ) { 565 $errors->add( 'site_empty_network_id', __( 'Site network ID must be provided.' ) ); 566 } 567 568 // Both registration and last updated dates must always be present and valid. 569 $date_fields = array( 'registered', 'last_updated' ); 570 foreach ( $date_fields as $date_field ) { 571 if ( empty( $data[ $date_field ] ) ) { 572 $errors->add( 'site_empty_' . $date_field, __( 'Both registration and last updated dates must be provided.' ) ); 573 break; 574 } 575 576 // Allow '0000-00-00 00:00:00', although it be stripped out at this point. 577 if ( '0000-00-00 00:00:00' !== $data[ $date_field ] ) { 578 $month = substr( $data[ $date_field ], 5, 2 ); 579 $day = substr( $data[ $date_field ], 8, 2 ); 580 $year = substr( $data[ $date_field ], 0, 4 ); 581 $valid_date = wp_checkdate( $month, $day, $year, $data[ $date_field ] ); 582 if ( ! $valid_date ) { 583 $errors->add( 'site_invalid_' . $date_field, __( 'Both registration and last updated dates must be valid dates.' ) ); 584 break; 585 } 586 } 587 } 588 589 if ( ! empty( $errors->errors ) ) { 590 return; 591 } 592 593 // If a new site, or domain/path/network ID have changed, ensure uniqueness. 594 if ( ! $old_site 595 || $data['domain'] !== $old_site->domain 596 || $data['path'] !== $old_site->path 597 || $data['network_id'] !== $old_site->network_id 598 ) { 599 if ( domain_exists( $data['domain'], $data['path'], $data['network_id'] ) ) { 600 $errors->add( 'site_taken', __( 'Sorry, that site already exists!' ) ); 601 } 602 } 603 } 604 605 /** 606 * Runs the initialization routine for a given site. 607 * 608 * This process includes creating the site's database tables and 609 * populating them with defaults. 610 * 611 * @since 5.1.0 612 * 613 * @global wpdb $wpdb WordPress database abstraction object. 614 * @global WP_Roles $wp_roles WordPress role management object. 615 * 616 * @param int|WP_Site $site_id Site ID or object. 617 * @param array $args { 618 * Optional. Arguments to modify the initialization behavior. 619 * 620 * @type int $user_id Required. User ID for the site administrator. 621 * @type string $title Site title. Default is 'Site %d' where %d is the 622 * site ID. 623 * @type array $options Custom option $key => $value pairs to use. Default 624 * empty array. 625 * @type array $meta Custom site metadata $key => $value pairs to use. 626 * Default empty array. 627 * } 628 * @return true|WP_Error True on success, or error object on failure. 629 */ 630 function wp_initialize_site( $site_id, array $args = array() ) { 631 global $wpdb, $wp_roles; 632 633 if ( empty( $site_id ) ) { 634 return new WP_Error( 'site_empty_id', __( 'Site ID must not be empty.' ) ); 635 } 636 637 $site = get_site( $site_id ); 638 if ( ! $site ) { 639 return new WP_Error( 'site_invalid_id', __( 'Site with the ID does not exist.' ) ); 640 } 641 642 if ( wp_is_site_initialized( $site ) ) { 643 return new WP_Error( 'site_already_initialized', __( 'The site appears to be already initialized.' ) ); 644 } 645 646 $network = get_network( $site->network_id ); 647 if ( ! $network ) { 648 $network = get_network(); 649 } 650 651 $args = wp_parse_args( 652 $args, 653 array( 654 'user_id' => 0, 655 /* translators: %d: Site ID. */ 656 'title' => sprintf( __( 'Site %d' ), $site->id ), 657 'options' => array(), 658 'meta' => array(), 659 ) 660 ); 661 662 /** 663 * Filters the arguments for initializing a site. 664 * 665 * @since 5.1.0 666 * 667 * @param array $args Arguments to modify the initialization behavior. 668 * @param WP_Site $site Site that is being initialized. 669 * @param WP_Network $network Network that the site belongs to. 670 */ 671 $args = apply_filters( 'wp_initialize_site_args', $args, $site, $network ); 672 673 $orig_installing = wp_installing(); 674 if ( ! $orig_installing ) { 675 wp_installing( true ); 676 } 677 678 $switch = false; 679 if ( get_current_blog_id() !== $site->id ) { 680 $switch = true; 681 switch_to_blog( $site->id ); 682 } 683 684 require_once ABSPATH . 'wp-admin/includes/upgrade.php'; 685 686 // Set up the database tables. 687 make_db_current_silent( 'blog' ); 688 689 $home_scheme = 'http'; 690 $siteurl_scheme = 'http'; 691 if ( ! is_subdomain_install() ) { 692 if ( 'https' === parse_url( get_home_url( $network->site_id ), PHP_URL_SCHEME ) ) { 693 $home_scheme = 'https'; 694 } 695 if ( 'https' === parse_url( get_network_option( $network->id, 'siteurl' ), PHP_URL_SCHEME ) ) { 696 $siteurl_scheme = 'https'; 697 } 698 } 699 700 // Populate the site's options. 701 populate_options( 702 array_merge( 703 array( 704 'home' => untrailingslashit( $home_scheme . '://' . $site->domain . $site->path ), 705 'siteurl' => untrailingslashit( $siteurl_scheme . '://' . $site->domain . $site->path ), 706 'blogname' => wp_unslash( $args['title'] ), 707 'admin_email' => '', 708 'upload_path' => get_network_option( $network->id, 'ms_files_rewriting' ) ? UPLOADBLOGSDIR . "/{$site->id}/files" : get_blog_option( $network->site_id, 'upload_path' ), 709 'blog_public' => (int) $site->public, 710 'WPLANG' => get_network_option( $network->id, 'WPLANG' ), 711 ), 712 $args['options'] 713 ) 714 ); 715 716 // Clean blog cache after populating options. 717 clean_blog_cache( $site ); 718 719 // Populate the site's roles. 720 populate_roles(); 721 $wp_roles = new WP_Roles(); 722 723 // Populate metadata for the site. 724 populate_site_meta( $site->id, $args['meta'] ); 725 726 // Remove all permissions that may exist for the site. 727 $table_prefix = $wpdb->get_blog_prefix(); 728 delete_metadata( 'user', 0, $table_prefix . 'user_level', null, true ); // Delete all. 729 delete_metadata( 'user', 0, $table_prefix . 'capabilities', null, true ); // Delete all. 730 731 // Install default site content. 732 wp_install_defaults( $args['user_id'] ); 733 734 // Set the site administrator. 735 add_user_to_blog( $site->id, $args['user_id'], 'administrator' ); 736 if ( ! user_can( $args['user_id'], 'manage_network' ) && ! get_user_meta( $args['user_id'], 'primary_blog', true ) ) { 737 update_user_meta( $args['user_id'], 'primary_blog', $site->id ); 738 } 739 740 if ( $switch ) { 741 restore_current_blog(); 742 } 743 744 wp_installing( $orig_installing ); 745 746 return true; 747 } 748 749 /** 750 * Runs the uninitialization routine for a given site. 751 * 752 * This process includes dropping the site's database tables and deleting its uploads directory. 753 * 754 * @since 5.1.0 755 * 756 * @global wpdb $wpdb WordPress database abstraction object. 757 * 758 * @param int|WP_Site $site_id Site ID or object. 759 * @return true|WP_Error True on success, or error object on failure. 760 */ 761 function wp_uninitialize_site( $site_id ) { 762 global $wpdb; 763 764 if ( empty( $site_id ) ) { 765 return new WP_Error( 'site_empty_id', __( 'Site ID must not be empty.' ) ); 766 } 767 768 $site = get_site( $site_id ); 769 if ( ! $site ) { 770 return new WP_Error( 'site_invalid_id', __( 'Site with the ID does not exist.' ) ); 771 } 772 773 if ( ! wp_is_site_initialized( $site ) ) { 774 return new WP_Error( 'site_already_uninitialized', __( 'The site appears to be already uninitialized.' ) ); 775 } 776 777 $users = get_users( 778 array( 779 'blog_id' => $site->id, 780 'fields' => 'ids', 781 ) 782 ); 783 784 // Remove users from the site. 785 if ( ! empty( $users ) ) { 786 foreach ( $users as $user_id ) { 787 remove_user_from_blog( $user_id, $site->id ); 788 } 789 } 790 791 $switch = false; 792 if ( get_current_blog_id() !== $site->id ) { 793 $switch = true; 794 switch_to_blog( $site->id ); 795 } 796 797 $uploads = wp_get_upload_dir(); 798 799 $tables = $wpdb->tables( 'blog' ); 800 801 /** 802 * Filters the tables to drop when the site is deleted. 803 * 804 * @since MU (3.0.0) 805 * 806 * @param string[] $tables Array of names of the site tables to be dropped. 807 * @param int $site_id The ID of the site to drop tables for. 808 */ 809 $drop_tables = apply_filters( 'wpmu_drop_tables', $tables, $site->id ); 810 811 foreach ( (array) $drop_tables as $table ) { 812 $wpdb->query( "DROP TABLE IF EXISTS `$table`" ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared 813 } 814 815 /** 816 * Filters the upload base directory to delete when the site is deleted. 817 * 818 * @since MU (3.0.0) 819 * 820 * @param string $basedir Uploads path without subdirectory. @see wp_upload_dir() 821 * @param int $site_id The site ID. 822 */ 823 $dir = apply_filters( 'wpmu_delete_blog_upload_dir', $uploads['basedir'], $site->id ); 824 $dir = rtrim( $dir, DIRECTORY_SEPARATOR ); 825 $top_dir = $dir; 826 $stack = array( $dir ); 827 $index = 0; 828 829 while ( $index < count( $stack ) ) { 830 // Get indexed directory from stack. 831 $dir = $stack[ $index ]; 832 833 // phpcs:disable WordPress.PHP.NoSilencedErrors.Discouraged 834 $dh = @opendir( $dir ); 835 if ( $dh ) { 836 $file = @readdir( $dh ); 837 while ( false !== $file ) { 838 if ( '.' === $file || '..' === $file ) { 839 $file = @readdir( $dh ); 840 continue; 841 } 842 843 if ( @is_dir( $dir . DIRECTORY_SEPARATOR . $file ) ) { 844 $stack[] = $dir . DIRECTORY_SEPARATOR . $file; 845 } elseif ( @is_file( $dir . DIRECTORY_SEPARATOR . $file ) ) { 846 @unlink( $dir . DIRECTORY_SEPARATOR . $file ); 847 } 848 849 $file = @readdir( $dh ); 850 } 851 @closedir( $dh ); 852 } 853 $index++; 854 } 855 856 $stack = array_reverse( $stack ); // Last added directories are deepest. 857 foreach ( (array) $stack as $dir ) { 858 if ( $dir != $top_dir ) { 859 @rmdir( $dir ); 860 } 861 } 862 863 // phpcs:enable WordPress.PHP.NoSilencedErrors.Discouraged 864 if ( $switch ) { 865 restore_current_blog(); 866 } 867 868 return true; 869 } 870 871 /** 872 * Checks whether a site is initialized. 873 * 874 * A site is considered initialized when its database tables are present. 875 * 876 * @since 5.1.0 877 * 878 * @global wpdb $wpdb WordPress database abstraction object. 879 * 880 * @param int|WP_Site $site_id Site ID or object. 881 * @return bool True if the site is initialized, false otherwise. 882 */ 883 function wp_is_site_initialized( $site_id ) { 884 global $wpdb; 885 886 if ( is_object( $site_id ) ) { 887 $site_id = $site_id->blog_id; 888 } 889 $site_id = (int) $site_id; 890 891 /** 892 * Filters the check for whether a site is initialized before the database is accessed. 893 * 894 * Returning a non-null value will effectively short-circuit the function, returning 895 * that value instead. 896 * 897 * @since 5.1.0 898 * 899 * @param bool|null $pre The value to return instead. Default null 900 * to continue with the check. 901 * @param int $site_id The site ID that is being checked. 902 */ 903 $pre = apply_filters( 'pre_wp_is_site_initialized', null, $site_id ); 904 if ( null !== $pre ) { 905 return (bool) $pre; 906 } 907 908 $switch = false; 909 if ( get_current_blog_id() !== $site_id ) { 910 $switch = true; 911 remove_action( 'switch_blog', 'wp_switch_roles_and_user', 1 ); 912 switch_to_blog( $site_id ); 913 } 914 915 $suppress = $wpdb->suppress_errors(); 916 $result = (bool) $wpdb->get_results( "DESCRIBE {$wpdb->posts}" ); 917 $wpdb->suppress_errors( $suppress ); 918 919 if ( $switch ) { 920 restore_current_blog(); 921 add_action( 'switch_blog', 'wp_switch_roles_and_user', 1, 2 ); 922 } 923 924 return $result; 925 } 926 927 /** 928 * Clean the blog cache 929 * 930 * @since 3.5.0 931 * 932 * @global bool $_wp_suspend_cache_invalidation 933 * 934 * @param WP_Site|int $blog The site object or ID to be cleared from cache. 935 */ 936 function clean_blog_cache( $blog ) { 937 global $_wp_suspend_cache_invalidation; 938 939 if ( ! empty( $_wp_suspend_cache_invalidation ) ) { 940 return; 941 } 942 943 if ( empty( $blog ) ) { 944 return; 945 } 946 947 $blog_id = $blog; 948 $blog = get_site( $blog_id ); 949 if ( ! $blog ) { 950 if ( ! is_numeric( $blog_id ) ) { 951 return; 952 } 953 954 // Make sure a WP_Site object exists even when the site has been deleted. 955 $blog = new WP_Site( 956 (object) array( 957 'blog_id' => $blog_id, 958 'domain' => null, 959 'path' => null, 960 ) 961 ); 962 } 963 964 $blog_id = $blog->blog_id; 965 $domain_path_key = md5( $blog->domain . $blog->path ); 966 967 wp_cache_delete( $blog_id, 'sites' ); 968 wp_cache_delete( $blog_id, 'site-details' ); 969 wp_cache_delete( $blog_id, 'blog-details' ); 970 wp_cache_delete( $blog_id . 'short', 'blog-details' ); 971 wp_cache_delete( $domain_path_key, 'blog-lookup' ); 972 wp_cache_delete( $domain_path_key, 'blog-id-cache' ); 973 wp_cache_delete( $blog_id, 'blog_meta' ); 974 975 /** 976 * Fires immediately after a site has been removed from the object cache. 977 * 978 * @since 4.6.0 979 * 980 * @param int $id Blog ID. 981 * @param WP_Site $blog Site object. 982 * @param string $domain_path_key md5 hash of domain and path. 983 */ 984 do_action( 'clean_site_cache', $blog_id, $blog, $domain_path_key ); 985 986 wp_cache_set( 'last_changed', microtime(), 'sites' ); 987 988 /** 989 * Fires after the blog details cache is cleared. 990 * 991 * @since 3.4.0 992 * @deprecated 4.9.0 Use {@see 'clean_site_cache'} instead. 993 * 994 * @param int $blog_id Blog ID. 995 */ 996 do_action_deprecated( 'refresh_blog_details', array( $blog_id ), '4.9.0', 'clean_site_cache' ); 997 } 998 999 /** 1000 * Adds metadata to a site. 1001 * 1002 * @since 5.1.0 1003 * 1004 * @param int $site_id Site ID. 1005 * @param string $meta_key Metadata name. 1006 * @param mixed $meta_value Metadata value. Must be serializable if non-scalar. 1007 * @param bool $unique Optional. Whether the same key should not be added. 1008 * Default false. 1009 * @return int|false Meta ID on success, false on failure. 1010 */ 1011 function add_site_meta( $site_id, $meta_key, $meta_value, $unique = false ) { 1012 return add_metadata( 'blog', $site_id, $meta_key, $meta_value, $unique ); 1013 } 1014 1015 /** 1016 * Removes metadata matching criteria from a site. 1017 * 1018 * You can match based on the key, or key and value. Removing based on key and 1019 * value, will keep from removing duplicate metadata with the same key. It also 1020 * allows removing all metadata matching key, if needed. 1021 * 1022 * @since 5.1.0 1023 * 1024 * @param int $site_id Site ID. 1025 * @param string $meta_key Metadata name. 1026 * @param mixed $meta_value Optional. Metadata value. If provided, 1027 * rows will only be removed that match the value. 1028 * Must be serializable if non-scalar. Default empty. 1029 * @return bool True on success, false on failure. 1030 */ 1031 function delete_site_meta( $site_id, $meta_key, $meta_value = '' ) { 1032 return delete_metadata( 'blog', $site_id, $meta_key, $meta_value ); 1033 } 1034 1035 /** 1036 * Retrieves metadata for a site. 1037 * 1038 * @since 5.1.0 1039 * 1040 * @param int $site_id Site ID. 1041 * @param string $key Optional. The meta key to retrieve. By default, 1042 * returns data for all keys. Default empty. 1043 * @param bool $single Optional. Whether to return a single value. 1044 * This parameter has no effect if `$key` is not specified. 1045 * Default false. 1046 * @return mixed An array of values if `$single` is false. 1047 * The value of meta data field if `$single` is true. 1048 * False for an invalid `$site_id` (non-numeric, zero, or negative value). 1049 * An empty string if a valid but non-existing site ID is passed. 1050 */ 1051 function get_site_meta( $site_id, $key = '', $single = false ) { 1052 return get_metadata( 'blog', $site_id, $key, $single ); 1053 } 1054 1055 /** 1056 * Updates metadata for a site. 1057 * 1058 * Use the $prev_value parameter to differentiate between meta fields with the 1059 * same key and site ID. 1060 * 1061 * If the meta field for the site does not exist, it will be added. 1062 * 1063 * @since 5.1.0 1064 * 1065 * @param int $site_id Site ID. 1066 * @param string $meta_key Metadata key. 1067 * @param mixed $meta_value Metadata value. Must be serializable if non-scalar. 1068 * @param mixed $prev_value Optional. Previous value to check before updating. 1069 * If specified, only update existing metadata entries with 1070 * this value. Otherwise, update all entries. Default empty. 1071 * @return int|bool Meta ID if the key didn't exist, true on successful update, 1072 * false on failure or if the value passed to the function 1073 * is the same as the one that is already in the database. 1074 */ 1075 function update_site_meta( $site_id, $meta_key, $meta_value, $prev_value = '' ) { 1076 return update_metadata( 'blog', $site_id, $meta_key, $meta_value, $prev_value ); 1077 } 1078 1079 /** 1080 * Deletes everything from site meta matching meta key. 1081 * 1082 * @since 5.1.0 1083 * 1084 * @param string $meta_key Metadata key to search for when deleting. 1085 * @return bool Whether the site meta key was deleted from the database. 1086 */ 1087 function delete_site_meta_by_key( $meta_key ) { 1088 return delete_metadata( 'blog', null, $meta_key, '', true ); 1089 } 1090 1091 /** 1092 * Updates the count of sites for a network based on a changed site. 1093 * 1094 * @since 5.1.0 1095 * 1096 * @param WP_Site $new_site The site object that has been inserted, updated or deleted. 1097 * @param WP_Site|null $old_site Optional. If $new_site has been updated, this must be the previous 1098 * state of that site. Default null. 1099 */ 1100 function wp_maybe_update_network_site_counts_on_update( $new_site, $old_site = null ) { 1101 if ( null === $old_site ) { 1102 wp_maybe_update_network_site_counts( $new_site->network_id ); 1103 return; 1104 } 1105 1106 if ( $new_site->network_id != $old_site->network_id ) { 1107 wp_maybe_update_network_site_counts( $new_site->network_id ); 1108 wp_maybe_update_network_site_counts( $old_site->network_id ); 1109 } 1110 } 1111 1112 /** 1113 * Triggers actions on site status updates. 1114 * 1115 * @since 5.1.0 1116 * 1117 * @param WP_Site $new_site The site object after the update. 1118 * @param WP_Site|null $old_site Optional. If $new_site has been updated, this must be the previous 1119 * state of that site. Default null. 1120 */ 1121 function wp_maybe_transition_site_statuses_on_update( $new_site, $old_site = null ) { 1122 $site_id = $new_site->id; 1123 1124 // Use the default values for a site if no previous state is given. 1125 if ( ! $old_site ) { 1126 $old_site = new WP_Site( new stdClass() ); 1127 } 1128 1129 if ( $new_site->spam != $old_site->spam ) { 1130 if ( 1 == $new_site->spam ) { 1131 1132 /** 1133 * Fires when the 'spam' status is added to a site. 1134 * 1135 * @since MU (3.0.0) 1136 * 1137 * @param int $site_id Site ID. 1138 */ 1139 do_action( 'make_spam_blog', $site_id ); 1140 } else { 1141 1142 /** 1143 * Fires when the 'spam' status is removed from a site. 1144 * 1145 * @since MU (3.0.0) 1146 * 1147 * @param int $site_id Site ID. 1148 */ 1149 do_action( 'make_ham_blog', $site_id ); 1150 } 1151 } 1152 1153 if ( $new_site->mature != $old_site->mature ) { 1154 if ( 1 == $new_site->mature ) { 1155 1156 /** 1157 * Fires when the 'mature' status is added to a site. 1158 * 1159 * @since 3.1.0 1160 * 1161 * @param int $site_id Site ID. 1162 */ 1163 do_action( 'mature_blog', $site_id ); 1164 } else { 1165 1166 /** 1167 * Fires when the 'mature' status is removed from a site. 1168 * 1169 * @since 3.1.0 1170 * 1171 * @param int $site_id Site ID. 1172 */ 1173 do_action( 'unmature_blog', $site_id ); 1174 } 1175 } 1176 1177 if ( $new_site->archived != $old_site->archived ) { 1178 if ( 1 == $new_site->archived ) { 1179 1180 /** 1181 * Fires when the 'archived' status is added to a site. 1182 * 1183 * @since MU (3.0.0) 1184 * 1185 * @param int $site_id Site ID. 1186 */ 1187 do_action( 'archive_blog', $site_id ); 1188 } else { 1189 1190 /** 1191 * Fires when the 'archived' status is removed from a site. 1192 * 1193 * @since MU (3.0.0) 1194 * 1195 * @param int $site_id Site ID. 1196 */ 1197 do_action( 'unarchive_blog', $site_id ); 1198 } 1199 } 1200 1201 if ( $new_site->deleted != $old_site->deleted ) { 1202 if ( 1 == $new_site->deleted ) { 1203 1204 /** 1205 * Fires when the 'deleted' status is added to a site. 1206 * 1207 * @since 3.5.0 1208 * 1209 * @param int $site_id Site ID. 1210 */ 1211 do_action( 'make_delete_blog', $site_id ); 1212 } else { 1213 1214 /** 1215 * Fires when the 'deleted' status is removed from a site. 1216 * 1217 * @since 3.5.0 1218 * 1219 * @param int $site_id Site ID. 1220 */ 1221 do_action( 'make_undelete_blog', $site_id ); 1222 } 1223 } 1224 1225 if ( $new_site->public != $old_site->public ) { 1226 1227 /** 1228 * Fires after the current blog's 'public' setting is updated. 1229 * 1230 * @since MU (3.0.0) 1231 * 1232 * @param int $site_id Site ID. 1233 * @param string $value The value of the site status. 1234 */ 1235 do_action( 'update_blog_public', $site_id, $new_site->public ); 1236 } 1237 } 1238 1239 /** 1240 * Cleans the necessary caches after specific site data has been updated. 1241 * 1242 * @since 5.1.0 1243 * 1244 * @param WP_Site $new_site The site object after the update. 1245 * @param WP_Site $old_site The site obejct prior to the update. 1246 */ 1247 function wp_maybe_clean_new_site_cache_on_update( $new_site, $old_site ) { 1248 if ( $old_site->domain !== $new_site->domain || $old_site->path !== $new_site->path ) { 1249 clean_blog_cache( $new_site ); 1250 } 1251 } 1252 1253 /** 1254 * Updates the `blog_public` option for a given site ID. 1255 * 1256 * @since 5.1.0 1257 * 1258 * @param int $site_id Site ID. 1259 * @param string $public The value of the site status. 1260 */ 1261 function wp_update_blog_public_option_on_site_update( $site_id, $public ) { 1262 1263 // Bail if the site's database tables do not exist (yet). 1264 if ( ! wp_is_site_initialized( $site_id ) ) { 1265 return; 1266 } 1267 1268 update_blog_option( $site_id, 'blog_public', $public ); 1269 } 1270 1271 /** 1272 * Sets the last changed time for the 'sites' cache group. 1273 * 1274 * @since 5.1.0 1275 */ 1276 function wp_cache_set_sites_last_changed() { 1277 wp_cache_set( 'last_changed', microtime(), 'sites' ); 1278 } 1279 1280 /** 1281 * Aborts calls to site meta if it is not supported. 1282 * 1283 * @since 5.1.0 1284 * 1285 * @global wpdb $wpdb WordPress database abstraction object. 1286 * 1287 * @param mixed $check Skip-value for whether to proceed site meta function execution. 1288 * @return mixed Original value of $check, or false if site meta is not supported. 1289 */ 1290 function wp_check_site_meta_support_prefilter( $check ) { 1291 if ( ! is_site_meta_supported() ) { 1292 /* translators: %s: Database table name. */ 1293 _doing_it_wrong( __FUNCTION__, sprintf( __( 'The %s table is not installed. Please run the network database upgrade.' ), $GLOBALS['wpdb']->blogmeta ), '5.1.0' ); 1294 return false; 1295 } 1296 1297 return $check; 1298 }