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