Wordpress 4.6
[autoinstalls/wordpress.git] / wp-includes / ms-blogs.php
1 <?php
2
3 /**
4  * Site/blog functions that work with the blogs table and related data.
5  *
6  * @package WordPress
7  * @subpackage Multisite
8  * @since MU
9  */
10
11 /**
12  * Update the last_updated field for the current site.
13  *
14  * @since MU
15  *
16  * @global wpdb $wpdb WordPress database abstraction object.
17  */
18 function wpmu_update_blogs_date() {
19         global $wpdb;
20
21         update_blog_details( $wpdb->blogid, array('last_updated' => current_time('mysql', true)) );
22         /**
23          * Fires after the blog details are updated.
24          *
25          * @since MU
26          *
27          * @param int $blog_id Site ID.
28          */
29         do_action( 'wpmu_blog_updated', $wpdb->blogid );
30 }
31
32 /**
33  * Get a full blog URL, given a blog id.
34  *
35  * @since MU
36  *
37  * @param int $blog_id Blog ID
38  * @return string Full URL of the blog if found. Empty string if not.
39  */
40 function get_blogaddress_by_id( $blog_id ) {
41         $bloginfo = get_blog_details( (int) $blog_id );
42
43         if ( empty( $bloginfo ) ) {
44                 return '';
45         }
46
47         $scheme = parse_url( $bloginfo->home, PHP_URL_SCHEME );
48         $scheme = empty( $scheme ) ? 'http' : $scheme;
49
50         return esc_url( $scheme . '://' . $bloginfo->domain . $bloginfo->path );
51 }
52
53 /**
54  * Get a full blog URL, given a blog name.
55  *
56  * @since MU
57  *
58  * @param string $blogname The (subdomain or directory) name
59  * @return string
60  */
61 function get_blogaddress_by_name( $blogname ) {
62         if ( is_subdomain_install() ) {
63                 if ( $blogname == 'main' )
64                         $blogname = 'www';
65                 $url = rtrim( network_home_url(), '/' );
66                 if ( !empty( $blogname ) )
67                         $url = preg_replace( '|^([^\.]+://)|', "\${1}" . $blogname . '.', $url );
68         } else {
69                 $url = network_home_url( $blogname );
70         }
71         return esc_url( $url . '/' );
72 }
73
74 /**
75  * Given a blog's (subdomain or directory) slug, retrieve its id.
76  *
77  * @since MU
78  *
79  * @global wpdb $wpdb WordPress database abstraction object.
80  *
81  * @param string $slug
82  * @return int A blog id
83  */
84 function get_id_from_blogname( $slug ) {
85         global $wpdb;
86
87         $current_site = get_current_site();
88         $slug = trim( $slug, '/' );
89
90         $blog_id = wp_cache_get( 'get_id_from_blogname_' . $slug, 'blog-details' );
91         if ( $blog_id )
92                 return $blog_id;
93
94         if ( is_subdomain_install() ) {
95                 $domain = $slug . '.' . $current_site->domain;
96                 $path = $current_site->path;
97         } else {
98                 $domain = $current_site->domain;
99                 $path = $current_site->path . $slug . '/';
100         }
101
102         $blog_id = $wpdb->get_var( $wpdb->prepare("SELECT blog_id FROM {$wpdb->blogs} WHERE domain = %s AND path = %s", $domain, $path) );
103         wp_cache_set( 'get_id_from_blogname_' . $slug, $blog_id, 'blog-details' );
104         return $blog_id;
105 }
106
107 /**
108  * Retrieve the details for a blog from the blogs table and blog options.
109  *
110  * @since MU
111  *
112  * @global wpdb $wpdb WordPress database abstraction object.
113  *
114  * @param int|string|array $fields  Optional. A blog ID, a blog slug, or an array of fields to query against.
115  *                                  If not specified the current blog ID is used.
116  * @param bool             $get_all Whether to retrieve all details or only the details in the blogs table.
117  *                                  Default is true.
118  * @return WP_Site|false Blog details on success. False on failure.
119  */
120 function get_blog_details( $fields = null, $get_all = true ) {
121         global $wpdb;
122
123         if ( is_array($fields ) ) {
124                 if ( isset($fields['blog_id']) ) {
125                         $blog_id = $fields['blog_id'];
126                 } elseif ( isset($fields['domain']) && isset($fields['path']) ) {
127                         $key = md5( $fields['domain'] . $fields['path'] );
128                         $blog = wp_cache_get($key, 'blog-lookup');
129                         if ( false !== $blog )
130                                 return $blog;
131                         if ( substr( $fields['domain'], 0, 4 ) == 'www.' ) {
132                                 $nowww = substr( $fields['domain'], 4 );
133                                 $blog = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM $wpdb->blogs WHERE domain IN (%s,%s) AND path = %s ORDER BY CHAR_LENGTH(domain) DESC", $nowww, $fields['domain'], $fields['path'] ) );
134                         } else {
135                                 $blog = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM $wpdb->blogs WHERE domain = %s AND path = %s", $fields['domain'], $fields['path'] ) );
136                         }
137                         if ( $blog ) {
138                                 wp_cache_set($blog->blog_id . 'short', $blog, 'blog-details');
139                                 $blog_id = $blog->blog_id;
140                         } else {
141                                 return false;
142                         }
143                 } elseif ( isset($fields['domain']) && is_subdomain_install() ) {
144                         $key = md5( $fields['domain'] );
145                         $blog = wp_cache_get($key, 'blog-lookup');
146                         if ( false !== $blog )
147                                 return $blog;
148                         if ( substr( $fields['domain'], 0, 4 ) == 'www.' ) {
149                                 $nowww = substr( $fields['domain'], 4 );
150                                 $blog = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM $wpdb->blogs WHERE domain IN (%s,%s) ORDER BY CHAR_LENGTH(domain) DESC", $nowww, $fields['domain'] ) );
151                         } else {
152                                 $blog = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM $wpdb->blogs WHERE domain = %s", $fields['domain'] ) );
153                         }
154                         if ( $blog ) {
155                                 wp_cache_set($blog->blog_id . 'short', $blog, 'blog-details');
156                                 $blog_id = $blog->blog_id;
157                         } else {
158                                 return false;
159                         }
160                 } else {
161                         return false;
162                 }
163         } else {
164                 if ( ! $fields )
165                         $blog_id = get_current_blog_id();
166                 elseif ( ! is_numeric( $fields ) )
167                         $blog_id = get_id_from_blogname( $fields );
168                 else
169                         $blog_id = $fields;
170         }
171
172         $blog_id = (int) $blog_id;
173
174         $all = $get_all == true ? '' : 'short';
175         $details = wp_cache_get( $blog_id . $all, 'blog-details' );
176
177         if ( $details ) {
178                 if ( ! is_object( $details ) ) {
179                         if ( $details == -1 ) {
180                                 return false;
181                         } else {
182                                 // Clear old pre-serialized objects. Cache clients do better with that.
183                                 wp_cache_delete( $blog_id . $all, 'blog-details' );
184                                 unset($details);
185                         }
186                 } else {
187                         return $details;
188                 }
189         }
190
191         // Try the other cache.
192         if ( $get_all ) {
193                 $details = wp_cache_get( $blog_id . 'short', 'blog-details' );
194         } else {
195                 $details = wp_cache_get( $blog_id, 'blog-details' );
196                 // If short was requested and full cache is set, we can return.
197                 if ( $details ) {
198                         if ( ! is_object( $details ) ) {
199                                 if ( $details == -1 ) {
200                                         return false;
201                                 } else {
202                                         // Clear old pre-serialized objects. Cache clients do better with that.
203                                         wp_cache_delete( $blog_id, 'blog-details' );
204                                         unset($details);
205                                 }
206                         } else {
207                                 return $details;
208                         }
209                 }
210         }
211
212         if ( empty($details) ) {
213                 $details = WP_Site::get_instance( $blog_id );
214                 if ( ! $details ) {
215                         // Set the full cache.
216                         wp_cache_set( $blog_id, -1, 'blog-details' );
217                         return false;
218                 }
219         }
220
221         if ( ! $details instanceof WP_Site ) {
222                 $details = new WP_Site( $details );
223         }
224
225         if ( ! $get_all ) {
226                 wp_cache_set( $blog_id . $all, $details, 'blog-details' );
227                 return $details;
228         }
229
230         switch_to_blog( $blog_id );
231         $details->blogname   = get_option( 'blogname' );
232         $details->siteurl    = get_option( 'siteurl' );
233         $details->post_count = get_option( 'post_count' );
234         $details->home       = get_option( 'home' );
235         restore_current_blog();
236
237         /**
238          * Filters a blog's details.
239          *
240          * @since MU
241          *
242          * @param object $details The blog details.
243          */
244         $details = apply_filters( 'blog_details', $details );
245
246         wp_cache_set( $blog_id . $all, $details, 'blog-details' );
247
248         $key = md5( $details->domain . $details->path );
249         wp_cache_set( $key, $details, 'blog-lookup' );
250
251         return $details;
252 }
253
254 /**
255  * Clear the blog details cache.
256  *
257  * @since MU
258  *
259  * @param int $blog_id Optional. Blog ID. Defaults to current blog.
260  */
261 function refresh_blog_details( $blog_id = 0 ) {
262         $blog_id = (int) $blog_id;
263         if ( ! $blog_id ) {
264                 $blog_id = get_current_blog_id();
265         }
266
267         $details = get_blog_details( $blog_id, false );
268         if ( ! $details ) {
269                 // Make sure clean_blog_cache() gets the blog ID
270                 // when the blog has been previously cached as
271                 // non-existent.
272                 $details = (object) array(
273                         'blog_id' => $blog_id,
274                         'domain' => null,
275                         'path' => null
276                 );
277         }
278
279         clean_blog_cache( $details );
280
281         /**
282          * Fires after the blog details cache is cleared.
283          *
284          * @since 3.4.0
285          *
286          * @param int $blog_id Blog ID.
287          */
288         do_action( 'refresh_blog_details', $blog_id );
289 }
290
291 /**
292  * Update the details for a blog. Updates the blogs table for a given blog id.
293  *
294  * @since MU
295  *
296  * @global wpdb $wpdb WordPress database abstraction object.
297  *
298  * @param int   $blog_id Blog ID
299  * @param array $details Array of details keyed by blogs table field names.
300  * @return bool True if update succeeds, false otherwise.
301  */
302 function update_blog_details( $blog_id, $details = array() ) {
303         global $wpdb;
304
305         if ( empty($details) )
306                 return false;
307
308         if ( is_object($details) )
309                 $details = get_object_vars($details);
310
311         $current_details = get_blog_details($blog_id, false);
312         if ( empty($current_details) )
313                 return false;
314
315         $current_details = get_object_vars($current_details);
316
317         $details = array_merge($current_details, $details);
318         $details['last_updated'] = current_time('mysql', true);
319
320         $update_details = array();
321         $fields = array( 'site_id', 'domain', 'path', 'registered', 'last_updated', 'public', 'archived', 'mature', 'spam', 'deleted', 'lang_id');
322         foreach ( array_intersect( array_keys( $details ), $fields ) as $field ) {
323                 if ( 'path' === $field ) {
324                         $details[ $field ] = trailingslashit( '/' . trim( $details[ $field ], '/' ) );
325                 }
326
327                 $update_details[ $field ] = $details[ $field ];
328         }
329
330         $result = $wpdb->update( $wpdb->blogs, $update_details, array('blog_id' => $blog_id) );
331
332         if ( false === $result )
333                 return false;
334
335         // If spam status changed, issue actions.
336         if ( $details['spam'] != $current_details['spam'] ) {
337                 if ( $details['spam'] == 1 ) {
338                         /**
339                          * Fires when the blog status is changed to 'spam'.
340                          *
341                          * @since MU
342                          *
343                          * @param int $blog_id Blog ID.
344                          */
345                         do_action( 'make_spam_blog', $blog_id );
346                 } else {
347                         /**
348                          * Fires when the blog status is changed to 'ham'.
349                          *
350                          * @since MU
351                          *
352                          * @param int $blog_id Blog ID.
353                          */
354                         do_action( 'make_ham_blog', $blog_id );
355                 }
356         }
357
358         // If mature status changed, issue actions.
359         if ( $details['mature'] != $current_details['mature'] ) {
360                 if ( $details['mature'] == 1 ) {
361                         /**
362                          * Fires when the blog status is changed to 'mature'.
363                          *
364                          * @since 3.1.0
365                          *
366                          * @param int $blog_id Blog ID.
367                          */
368                         do_action( 'mature_blog', $blog_id );
369                 } else {
370                         /**
371                          * Fires when the blog status is changed to 'unmature'.
372                          *
373                          * @since 3.1.0
374                          *
375                          * @param int $blog_id Blog ID.
376                          */
377                         do_action( 'unmature_blog', $blog_id );
378                 }
379         }
380
381         // If archived status changed, issue actions.
382         if ( $details['archived'] != $current_details['archived'] ) {
383                 if ( $details['archived'] == 1 ) {
384                         /**
385                          * Fires when the blog status is changed to 'archived'.
386                          *
387                          * @since MU
388                          *
389                          * @param int $blog_id Blog ID.
390                          */
391                         do_action( 'archive_blog', $blog_id );
392                 } else {
393                         /**
394                          * Fires when the blog status is changed to 'unarchived'.
395                          *
396                          * @since MU
397                          *
398                          * @param int $blog_id Blog ID.
399                          */
400                         do_action( 'unarchive_blog', $blog_id );
401                 }
402         }
403
404         // If deleted status changed, issue actions.
405         if ( $details['deleted'] != $current_details['deleted'] ) {
406                 if ( $details['deleted'] == 1 ) {
407                         /**
408                          * Fires when the blog status is changed to 'deleted'.
409                          *
410                          * @since 3.5.0
411                          *
412                          * @param int $blog_id Blog ID.
413                          */
414                         do_action( 'make_delete_blog', $blog_id );
415                 } else {
416                         /**
417                          * Fires when the blog status is changed to 'undeleted'.
418                          *
419                          * @since 3.5.0
420                          *
421                          * @param int $blog_id Blog ID.
422                          */
423                         do_action( 'make_undelete_blog', $blog_id );
424                 }
425         }
426
427         if ( isset( $details['public'] ) ) {
428                 switch_to_blog( $blog_id );
429                 update_option( 'blog_public', $details['public'] );
430                 restore_current_blog();
431         }
432
433         refresh_blog_details($blog_id);
434
435         return true;
436 }
437
438 /**
439  * Clean the blog cache
440  *
441  * @since 3.5.0
442  *
443  * @param WP_Site $blog The blog details as returned from get_blog_details()
444  */
445 function clean_blog_cache( $blog ) {
446         $blog_id = $blog->blog_id;
447         $domain_path_key = md5( $blog->domain . $blog->path );
448
449         wp_cache_delete( $blog_id, 'sites' );
450         wp_cache_delete( $blog_id, 'site-details' );
451         wp_cache_delete( $blog_id , 'blog-details' );
452         wp_cache_delete( $blog_id . 'short' , 'blog-details' );
453         wp_cache_delete(  $domain_path_key, 'blog-lookup' );
454         wp_cache_delete( 'current_blog_' . $blog->domain, 'site-options' );
455         wp_cache_delete( 'current_blog_' . $blog->domain . $blog->path, 'site-options' );
456         wp_cache_delete( 'get_id_from_blogname_' . trim( $blog->path, '/' ), 'blog-details' );
457         wp_cache_delete( $domain_path_key, 'blog-id-cache' );
458
459         /**
460          * Fires immediately after a site has been removed from the object cache.
461          *
462          * @since 4.6.0
463          *
464          * @param int     $id              Blog ID.
465          * @param WP_Site $blog            Site object.
466          * @param string  $domain_path_key md5 hash of domain and path.
467          */
468         do_action( 'clean_site_cache', $blog_id, $blog, $domain_path_key );
469
470         wp_cache_set( 'last_changed', microtime(), 'sites' );
471 }
472
473 /**
474  * Retrieves site data given a site ID or site object.
475  *
476  * Site data will be cached and returned after being passed through a filter.
477  * If the provided site is empty, the current site global will be used.
478  *
479  * @since 4.6.0
480  *
481  * @param WP_Site|int|null $site Optional. Site to retrieve. Default is the current site.
482  * @return WP_Site|null The site object or null if not found.
483  */
484 function get_site( $site = null ) {
485         if ( empty( $site ) ) {
486                 $site = get_current_blog_id();
487         }
488
489         if ( $site instanceof WP_Site ) {
490                 $_site = $site;
491         } elseif ( is_object( $site ) ) {
492                 $_site = new WP_Site( $site );
493         } else {
494                 $_site = WP_Site::get_instance( $site );
495         }
496
497         if ( ! $_site ) {
498                 return null;
499         }
500
501         /**
502          * Fires after a site is retrieved.
503          *
504          * @since 4.6.0
505          *
506          * @param WP_Site $_site Site data.
507          */
508         $_site = apply_filters( 'get_site', $_site );
509
510         return $_site;
511 }
512
513 /**
514  * Adds any sites from the given ids to the cache that do not already exist in cache.
515  *
516  * @since 4.6.0
517  * @access private
518  *
519  * @see update_site_cache()
520  * @global wpdb $wpdb WordPress database abstraction object.
521  *
522  * @param array $ids ID list.
523  */
524 function _prime_site_caches( $ids ) {
525         global $wpdb;
526
527         $non_cached_ids = _get_non_cached_ids( $ids, 'sites' );
528         if ( ! empty( $non_cached_ids ) ) {
529                 $fresh_sites = $wpdb->get_results( sprintf( "SELECT * FROM $wpdb->blogs WHERE blog_id IN (%s)", join( ",", array_map( 'intval', $non_cached_ids ) ) ) );
530
531                 update_site_cache( $fresh_sites );
532         }
533 }
534
535 /**
536  * Updates sites in cache.
537  *
538  * @since 4.6.0
539  *
540  * @param array $sites Array of site objects.
541  */
542 function update_site_cache( $sites ) {
543         if ( ! $sites ) {
544                 return;
545         }
546
547         foreach ( $sites as $site ) {
548                 wp_cache_add( $site->blog_id, $site, 'sites' );
549                 wp_cache_add( $site->blog_id . 'short', $site, 'blog-details' );
550         }
551 }
552
553 /**
554  * Retrieves a list of sites matching requested arguments.
555  *
556  * @since 4.6.0
557  *
558  * @see WP_Site_Query::parse_query()
559  *
560  * @param string|array $args {
561  *     Optional. Array or query string of site query parameters. Default empty.
562  *
563  *     @type array        $site__in          Array of site IDs to include. Default empty.
564  *     @type array        $site__not_in      Array of site IDs to exclude. Default empty.
565  *     @type bool         $count             Whether to return a site count (true) or array of site objects.
566  *                                           Default false.
567  *     @type array        $date_query        Date query clauses to limit sites by. See WP_Date_Query.
568  *                                           Default null.
569  *     @type string       $fields            Site fields to return. Accepts 'ids' for site IDs only or empty
570  *                                           for all fields. Default empty.
571  *     @type int          $ID                A site ID to only return that site. Default empty.
572  *     @type int          $number            Maximum number of sites to retrieve. Default null (no limit).
573  *     @type int          $offset            Number of sites to offset the query. Used to build LIMIT clause.
574  *                                           Default 0.
575  *     @type bool         $no_found_rows     Whether to disable the `SQL_CALC_FOUND_ROWS` query. Default true.
576  *     @type string|array $orderby           Site status or array of statuses. Accepts 'id', 'domain', 'path',
577  *                                           'network_id', 'last_updated', 'registered', 'domain_length',
578  *                                           'path_length', 'site__in' and 'network__in'. Also accepts false,
579  *                                           an empty array, or 'none' to disable `ORDER BY` clause.
580  *                                           Default 'id'.
581  *     @type string       $order             How to order retrieved sites. Accepts 'ASC', 'DESC'. Default 'ASC'.
582  *     @type int          $network_id        Limit results to those affiliated with a given network ID.
583  *                                           Default current network ID.
584  *     @type array        $network__in       Array of network IDs to include affiliated sites for. Default empty.
585  *     @type array        $network__not_in   Array of network IDs to exclude affiliated sites for. Default empty.
586  *     @type string       $domain            Limit results to those affiliated with a given domain.
587  *                                           Default empty.
588  *     @type array        $domain__in        Array of domains to include affiliated sites for. Default empty.
589  *     @type array        $domain__not_in    Array of domains to exclude affiliated sites for. Default empty.
590  *     @type string       $path              Limit results to those affiliated with a given path.
591  *                                           Default empty.
592  *     @type array        $path__in          Array of paths to include affiliated sites for. Default empty.
593  *     @type array        $path__not_in      Array of paths to exclude affiliated sites for. Default empty.
594  *     @type int          $public            Limit results to public sites. Accepts '1' or '0'. Default empty.
595  *     @type int          $archived          Limit results to archived sites. Accepts '1' or '0'. Default empty.
596  *     @type int          $mature            Limit results to mature sites. Accepts '1' or '0'. Default empty.
597  *     @type int          $spam              Limit results to spam sites. Accepts '1' or '0'. Default empty.
598  *     @type int          $deleted           Limit results to deleted sites. Accepts '1' or '0'. Default empty.
599  *     @type string       $search            Search term(s) to retrieve matching sites for. Default empty.
600  *     @type bool         $update_site_cache Whether to prime the cache for found sites. Default false.
601  * }
602  * @return array List of sites.
603  */
604 function get_sites( $args = array() ) {
605         $query = new WP_Site_Query();
606
607         return $query->query( $args );
608 }
609
610 /**
611  * Retrieve option value for a given blog id based on name of option.
612  *
613  * If the option does not exist or does not have a value, then the return value
614  * will be false. This is useful to check whether you need to install an option
615  * and is commonly used during installation of plugin options and to test
616  * whether upgrading is required.
617  *
618  * If the option was serialized then it will be unserialized when it is returned.
619  *
620  * @since MU
621  *
622  * @param int    $id      A blog ID. Can be null to refer to the current blog.
623  * @param string $option  Name of option to retrieve. Expected to not be SQL-escaped.
624  * @param mixed  $default Optional. Default value to return if the option does not exist.
625  * @return mixed Value set for the option.
626  */
627 function get_blog_option( $id, $option, $default = false ) {
628         $id = (int) $id;
629
630         if ( empty( $id ) )
631                 $id = get_current_blog_id();
632
633         if ( get_current_blog_id() == $id )
634                 return get_option( $option, $default );
635
636         switch_to_blog( $id );
637         $value = get_option( $option, $default );
638         restore_current_blog();
639
640         /**
641          * Filters a blog option value.
642          *
643          * The dynamic portion of the hook name, `$option`, refers to the blog option name.
644          *
645          * @since 3.5.0
646          *
647          * @param string  $value The option value.
648          * @param int     $id    Blog ID.
649          */
650         return apply_filters( "blog_option_{$option}", $value, $id );
651 }
652
653 /**
654  * Add a new option for a given blog id.
655  *
656  * You do not need to serialize values. If the value needs to be serialized, then
657  * it will be serialized before it is inserted into the database. Remember,
658  * resources can not be serialized or added as an option.
659  *
660  * You can create options without values and then update the values later.
661  * Existing options will not be updated and checks are performed to ensure that you
662  * aren't adding a protected WordPress option. Care should be taken to not name
663  * options the same as the ones which are protected.
664  *
665  * @since MU
666  *
667  * @param int    $id     A blog ID. Can be null to refer to the current blog.
668  * @param string $option Name of option to add. Expected to not be SQL-escaped.
669  * @param mixed  $value  Optional. Option value, can be anything. Expected to not be SQL-escaped.
670  * @return bool False if option was not added and true if option was added.
671  */
672 function add_blog_option( $id, $option, $value ) {
673         $id = (int) $id;
674
675         if ( empty( $id ) )
676                 $id = get_current_blog_id();
677
678         if ( get_current_blog_id() == $id )
679                 return add_option( $option, $value );
680
681         switch_to_blog( $id );
682         $return = add_option( $option, $value );
683         restore_current_blog();
684
685         return $return;
686 }
687
688 /**
689  * Removes option by name for a given blog id. Prevents removal of protected WordPress options.
690  *
691  * @since MU
692  *
693  * @param int    $id     A blog ID. Can be null to refer to the current blog.
694  * @param string $option Name of option to remove. Expected to not be SQL-escaped.
695  * @return bool True, if option is successfully deleted. False on failure.
696  */
697 function delete_blog_option( $id, $option ) {
698         $id = (int) $id;
699
700         if ( empty( $id ) )
701                 $id = get_current_blog_id();
702
703         if ( get_current_blog_id() == $id )
704                 return delete_option( $option );
705
706         switch_to_blog( $id );
707         $return = delete_option( $option );
708         restore_current_blog();
709
710         return $return;
711 }
712
713 /**
714  * Update an option for a particular blog.
715  *
716  * @since MU
717  *
718  * @param int    $id         The blog id.
719  * @param string $option     The option key.
720  * @param mixed  $value      The option value.
721  * @param mixed  $deprecated Not used.
722  * @return bool True on success, false on failure.
723  */
724 function update_blog_option( $id, $option, $value, $deprecated = null ) {
725         $id = (int) $id;
726
727         if ( null !== $deprecated  )
728                 _deprecated_argument( __FUNCTION__, '3.1.0' );
729
730         if ( get_current_blog_id() == $id )
731                 return update_option( $option, $value );
732
733         switch_to_blog( $id );
734         $return = update_option( $option, $value );
735         restore_current_blog();
736
737         refresh_blog_details( $id );
738
739         return $return;
740 }
741
742 /**
743  * Switch the current blog.
744  *
745  * This function is useful if you need to pull posts, or other information,
746  * from other blogs. You can switch back afterwards using restore_current_blog().
747  *
748  * Things that aren't switched:
749  *  - autoloaded options. See #14992
750  *  - plugins. See #14941
751  *
752  * @see restore_current_blog()
753  * @since MU
754  *
755  * @global wpdb            $wpdb
756  * @global int             $blog_id
757  * @global array           $_wp_switched_stack
758  * @global bool            $switched
759  * @global string          $table_prefix
760  * @global WP_Object_Cache $wp_object_cache
761  *
762  * @param int  $new_blog   The id of the blog you want to switch to. Default: current blog
763  * @param bool $deprecated Deprecated argument
764  * @return true Always returns True.
765  */
766 function switch_to_blog( $new_blog, $deprecated = null ) {
767         global $wpdb;
768
769         if ( empty( $new_blog ) )
770                 $new_blog = $GLOBALS['blog_id'];
771
772         $GLOBALS['_wp_switched_stack'][] = $GLOBALS['blog_id'];
773
774         /*
775          * If we're switching to the same blog id that we're on,
776          * set the right vars, do the associated actions, but skip
777          * the extra unnecessary work
778          */
779         if ( $new_blog == $GLOBALS['blog_id'] ) {
780                 /**
781                  * Fires when the blog is switched.
782                  *
783                  * @since MU
784                  *
785                  * @param int $new_blog New blog ID.
786                  * @param int $new_blog Blog ID.
787                  */
788                 do_action( 'switch_blog', $new_blog, $new_blog );
789                 $GLOBALS['switched'] = true;
790                 return true;
791         }
792
793         $wpdb->set_blog_id( $new_blog );
794         $GLOBALS['table_prefix'] = $wpdb->get_blog_prefix();
795         $prev_blog_id = $GLOBALS['blog_id'];
796         $GLOBALS['blog_id'] = $new_blog;
797
798         if ( function_exists( 'wp_cache_switch_to_blog' ) ) {
799                 wp_cache_switch_to_blog( $new_blog );
800         } else {
801                 global $wp_object_cache;
802
803                 if ( is_object( $wp_object_cache ) && isset( $wp_object_cache->global_groups ) )
804                         $global_groups = $wp_object_cache->global_groups;
805                 else
806                         $global_groups = false;
807
808                 wp_cache_init();
809
810                 if ( function_exists( 'wp_cache_add_global_groups' ) ) {
811                         if ( is_array( $global_groups ) ) {
812                                 wp_cache_add_global_groups( $global_groups );
813                         } else {
814                                 wp_cache_add_global_groups( array( 'users', 'userlogins', 'usermeta', 'user_meta', 'useremail', 'userslugs', 'site-transient', 'site-options', 'site-lookup', 'blog-lookup', 'blog-details', 'rss', 'global-posts', 'blog-id-cache', 'networks', 'sites', 'site-details' ) );
815                         }
816                         wp_cache_add_non_persistent_groups( array( 'counts', 'plugins' ) );
817                 }
818         }
819
820         if ( did_action( 'init' ) ) {
821                 wp_roles()->reinit();
822                 $current_user = wp_get_current_user();
823                 $current_user->for_blog( $new_blog );
824         }
825
826         /** This filter is documented in wp-includes/ms-blogs.php */
827         do_action( 'switch_blog', $new_blog, $prev_blog_id );
828         $GLOBALS['switched'] = true;
829
830         return true;
831 }
832
833 /**
834  * Restore the current blog, after calling switch_to_blog()
835  *
836  * @see switch_to_blog()
837  * @since MU
838  *
839  * @global wpdb            $wpdb
840  * @global array           $_wp_switched_stack
841  * @global int             $blog_id
842  * @global bool            $switched
843  * @global string          $table_prefix
844  * @global WP_Object_Cache $wp_object_cache
845  *
846  * @return bool True on success, false if we're already on the current blog
847  */
848 function restore_current_blog() {
849         global $wpdb;
850
851         if ( empty( $GLOBALS['_wp_switched_stack'] ) )
852                 return false;
853
854         $blog = array_pop( $GLOBALS['_wp_switched_stack'] );
855
856         if ( $GLOBALS['blog_id'] == $blog ) {
857                 /** This filter is documented in wp-includes/ms-blogs.php */
858                 do_action( 'switch_blog', $blog, $blog );
859                 // If we still have items in the switched stack, consider ourselves still 'switched'
860                 $GLOBALS['switched'] = ! empty( $GLOBALS['_wp_switched_stack'] );
861                 return true;
862         }
863
864         $wpdb->set_blog_id( $blog );
865         $prev_blog_id = $GLOBALS['blog_id'];
866         $GLOBALS['blog_id'] = $blog;
867         $GLOBALS['table_prefix'] = $wpdb->get_blog_prefix();
868
869         if ( function_exists( 'wp_cache_switch_to_blog' ) ) {
870                 wp_cache_switch_to_blog( $blog );
871         } else {
872                 global $wp_object_cache;
873
874                 if ( is_object( $wp_object_cache ) && isset( $wp_object_cache->global_groups ) )
875                         $global_groups = $wp_object_cache->global_groups;
876                 else
877                         $global_groups = false;
878
879                 wp_cache_init();
880
881                 if ( function_exists( 'wp_cache_add_global_groups' ) ) {
882                         if ( is_array( $global_groups ) ) {
883                                 wp_cache_add_global_groups( $global_groups );
884                         } else {
885                                 wp_cache_add_global_groups( array( 'users', 'userlogins', 'usermeta', 'user_meta', 'useremail', 'userslugs', 'site-transient', 'site-options', 'site-lookup', 'blog-lookup', 'blog-details', 'rss', 'global-posts', 'blog-id-cache', 'networks', 'sites', 'site-details' ) );
886                         }
887                         wp_cache_add_non_persistent_groups( array( 'counts', 'plugins' ) );
888                 }
889         }
890
891         if ( did_action( 'init' ) ) {
892                 wp_roles()->reinit();
893                 $current_user = wp_get_current_user();
894                 $current_user->for_blog( $blog );
895         }
896
897         /** This filter is documented in wp-includes/ms-blogs.php */
898         do_action( 'switch_blog', $blog, $prev_blog_id );
899
900         // If we still have items in the switched stack, consider ourselves still 'switched'
901         $GLOBALS['switched'] = ! empty( $GLOBALS['_wp_switched_stack'] );
902
903         return true;
904 }
905
906 /**
907  * Determines if switch_to_blog() is in effect
908  *
909  * @since 3.5.0
910  *
911  * @global array $_wp_switched_stack
912  *
913  * @return bool True if switched, false otherwise.
914  */
915 function ms_is_switched() {
916         return ! empty( $GLOBALS['_wp_switched_stack'] );
917 }
918
919 /**
920  * Check if a particular blog is archived.
921  *
922  * @since MU
923  *
924  * @param int $id The blog id
925  * @return string Whether the blog is archived or not
926  */
927 function is_archived( $id ) {
928         return get_blog_status($id, 'archived');
929 }
930
931 /**
932  * Update the 'archived' status of a particular blog.
933  *
934  * @since MU
935  *
936  * @param int    $id       The blog id
937  * @param string $archived The new status
938  * @return string $archived
939  */
940 function update_archived( $id, $archived ) {
941         update_blog_status($id, 'archived', $archived);
942         return $archived;
943 }
944
945 /**
946  * Update a blog details field.
947  *
948  * @since MU
949  *
950  * @global wpdb $wpdb WordPress database abstraction object.
951  *
952  * @param int    $blog_id BLog ID
953  * @param string $pref    A field name
954  * @param string $value   Value for $pref
955  * @param null   $deprecated
956  * @return string|false $value
957  */
958 function update_blog_status( $blog_id, $pref, $value, $deprecated = null ) {
959         global $wpdb;
960
961         if ( null !== $deprecated  )
962                 _deprecated_argument( __FUNCTION__, '3.1.0' );
963
964         if ( ! in_array( $pref, array( 'site_id', 'domain', 'path', 'registered', 'last_updated', 'public', 'archived', 'mature', 'spam', 'deleted', 'lang_id') ) )
965                 return $value;
966
967         $result = $wpdb->update( $wpdb->blogs, array($pref => $value, 'last_updated' => current_time('mysql', true)), array('blog_id' => $blog_id) );
968
969         if ( false === $result )
970                 return false;
971
972         refresh_blog_details( $blog_id );
973
974         if ( 'spam' == $pref ) {
975                 if ( $value == 1 ) {
976                         /** This filter is documented in wp-includes/ms-blogs.php */
977                         do_action( 'make_spam_blog', $blog_id );
978                 } else {
979                         /** This filter is documented in wp-includes/ms-blogs.php */
980                         do_action( 'make_ham_blog', $blog_id );
981                 }
982         } elseif ( 'mature' == $pref ) {
983                 if ( $value == 1 ) {
984                         /** This filter is documented in wp-includes/ms-blogs.php */
985                         do_action( 'mature_blog', $blog_id );
986                 } else {
987                         /** This filter is documented in wp-includes/ms-blogs.php */
988                         do_action( 'unmature_blog', $blog_id );
989                 }
990         } elseif ( 'archived' == $pref ) {
991                 if ( $value == 1 ) {
992                         /** This filter is documented in wp-includes/ms-blogs.php */
993                         do_action( 'archive_blog', $blog_id );
994                 } else {
995                         /** This filter is documented in wp-includes/ms-blogs.php */
996                         do_action( 'unarchive_blog', $blog_id );
997                 }
998         } elseif ( 'deleted' == $pref ) {
999                 if ( $value == 1 ) {
1000                         /** This filter is documented in wp-includes/ms-blogs.php */
1001                         do_action( 'make_delete_blog', $blog_id );
1002                 } else {
1003                         /** This filter is documented in wp-includes/ms-blogs.php */
1004                         do_action( 'make_undelete_blog', $blog_id );
1005                 }
1006         } elseif ( 'public' == $pref ) {
1007                 /**
1008                  * Fires after the current blog's 'public' setting is updated.
1009                  *
1010                  * @since MU
1011                  *
1012                  * @param int    $blog_id Blog ID.
1013                  * @param string $value   The value of blog status.
1014                  */
1015                 do_action( 'update_blog_public', $blog_id, $value ); // Moved here from update_blog_public().
1016         }
1017
1018         return $value;
1019 }
1020
1021 /**
1022  * Get a blog details field.
1023  *
1024  * @since MU
1025  *
1026  * @global wpdb $wpdb WordPress database abstraction object.
1027  *
1028  * @param int    $id   The blog id
1029  * @param string $pref A field name
1030  * @return bool|string|null $value
1031  */
1032 function get_blog_status( $id, $pref ) {
1033         global $wpdb;
1034
1035         $details = get_blog_details( $id, false );
1036         if ( $details )
1037                 return $details->$pref;
1038
1039         return $wpdb->get_var( $wpdb->prepare("SELECT %s FROM {$wpdb->blogs} WHERE blog_id = %d", $pref, $id) );
1040 }
1041
1042 /**
1043  * Get a list of most recently updated blogs.
1044  *
1045  * @since MU
1046  *
1047  * @global wpdb $wpdb WordPress database abstraction object.
1048  *
1049  * @param mixed $deprecated Not used
1050  * @param int   $start      The offset
1051  * @param int   $quantity   The maximum number of blogs to retrieve. Default is 40.
1052  * @return array The list of blogs
1053  */
1054 function get_last_updated( $deprecated = '', $start = 0, $quantity = 40 ) {
1055         global $wpdb;
1056
1057         if ( ! empty( $deprecated ) )
1058                 _deprecated_argument( __FUNCTION__, 'MU' ); // never used
1059
1060         return $wpdb->get_results( $wpdb->prepare("SELECT blog_id, domain, path FROM $wpdb->blogs WHERE site_id = %d AND public = '1' AND archived = '0' AND mature = '0' AND spam = '0' AND deleted = '0' AND last_updated != '0000-00-00 00:00:00' ORDER BY last_updated DESC limit %d, %d", $wpdb->siteid, $start, $quantity ) , ARRAY_A );
1061 }
1062
1063 /**
1064  * Retrieves a list of networks.
1065  *
1066  * @since 4.6.0
1067  *
1068  * @param string|array $args Optional. Array or string of arguments. See WP_Network_Query::parse_query()
1069  *                           for information on accepted arguments. Default empty array.
1070  * @return int|array List of networks or number of found networks if `$count` argument is true.
1071  */
1072 function get_networks( $args = array() ) {
1073         $query = new WP_Network_Query();
1074
1075         return $query->query( $args );
1076 }
1077
1078 /**
1079  * Retrieves network data given a network ID or network object.
1080  *
1081  * Network data will be cached and returned after being passed through a filter.
1082  * If the provided network is empty, the current network global will be used.
1083  *
1084  * @since 4.6.0
1085  *
1086  * @global WP_Network $current_site
1087  *
1088  * @param WP_Network|int|null $network Optional. Network to retrieve. Default is the current network.
1089  * @return WP_Network|null The network object or null if not found.
1090  */
1091 function get_network( $network = null ) {
1092         global $current_site;
1093         if ( empty( $network ) && isset( $current_site ) ) {
1094                 $network = $current_site;
1095         }
1096
1097         if ( $network instanceof WP_Network ) {
1098                 $_network = $network;
1099         } elseif ( is_object( $network ) ) {
1100                 $_network = new WP_Network( $network );
1101         } else {
1102                 $_network = WP_Network::get_instance( $network );
1103         }
1104
1105         if ( ! $_network ) {
1106                 return null;
1107         }
1108
1109         /**
1110          * Fires after a network is retrieved.
1111          *
1112          * @since 4.6.0
1113          *
1114          * @param WP_Network $_network Network data.
1115          */
1116         $_network = apply_filters( 'get_network', $_network );
1117
1118         return $_network;
1119 }
1120
1121 /**
1122  * Removes a network from the object cache.
1123  *
1124  * @since 4.6.0
1125  *
1126  * @param int|array $ids Network ID or an array of network IDs to remove from cache.
1127  */
1128 function clean_network_cache( $ids ) {
1129         foreach ( (array) $ids as $id ) {
1130                 wp_cache_delete( $id, 'networks' );
1131
1132                 /**
1133                  * Fires immediately after a network has been removed from the object cache.
1134                  *
1135                  * @since 4.6.0
1136                  *
1137                  * @param int $id Network ID.
1138                  */
1139                 do_action( 'clean_network_cache', $id );
1140         }
1141
1142         wp_cache_set( 'last_changed', microtime(), 'networks' );
1143 }
1144
1145 /**
1146  * Updates the network cache of given networks.
1147  *
1148  * Will add the networks in $networks to the cache. If network ID already exists
1149  * in the network cache then it will not be updated. The network is added to the
1150  * cache using the network group with the key using the ID of the networks.
1151  *
1152  * @since 4.6.0
1153  *
1154  * @param array $networks Array of network row objects.
1155  */
1156 function update_network_cache( $networks ) {
1157         foreach ( (array) $networks as $network ) {
1158                 wp_cache_add( $network->id, $network, 'networks' );
1159         }
1160 }
1161
1162 /**
1163  * Adds any networks from the given IDs to the cache that do not already exist in cache.
1164  *
1165  * @since 4.6.0
1166  * @access private
1167  *
1168  * @see update_network_cache()
1169  * @global wpdb $wpdb WordPress database abstraction object.
1170  *
1171  * @param array $network_ids Array of network IDs.
1172  */
1173 function _prime_network_caches( $network_ids ) {
1174         global $wpdb;
1175
1176         $non_cached_ids = _get_non_cached_ids( $network_ids, 'networks' );
1177         if ( !empty( $non_cached_ids ) ) {
1178                 $fresh_networks = $wpdb->get_results( sprintf( "SELECT $wpdb->site.* FROM $wpdb->site WHERE id IN (%s)", join( ",", array_map( 'intval', $non_cached_ids ) ) ) );
1179
1180                 update_network_cache( $fresh_networks );
1181         }
1182 }
1183
1184 /**
1185  * Handler for updating the blog date when a post is published or an already published post is changed.
1186  *
1187  * @since 3.3.0
1188  *
1189  * @param string $new_status The new post status
1190  * @param string $old_status The old post status
1191  * @param object $post       Post object
1192  */
1193 function _update_blog_date_on_post_publish( $new_status, $old_status, $post ) {
1194         $post_type_obj = get_post_type_object( $post->post_type );
1195         if ( ! $post_type_obj || ! $post_type_obj->public ) {
1196                 return;
1197         }
1198
1199         if ( 'publish' != $new_status && 'publish' != $old_status ) {
1200                 return;
1201         }
1202
1203         // Post was freshly published, published post was saved, or published post was unpublished.
1204
1205         wpmu_update_blogs_date();
1206 }
1207
1208 /**
1209  * Handler for updating the blog date when a published post is deleted.
1210  *
1211  * @since 3.4.0
1212  *
1213  * @param int $post_id Post ID
1214  */
1215 function _update_blog_date_on_post_delete( $post_id ) {
1216         $post = get_post( $post_id );
1217
1218         $post_type_obj = get_post_type_object( $post->post_type );
1219         if ( ! $post_type_obj || ! $post_type_obj->public ) {
1220                 return;
1221         }
1222
1223         if ( 'publish' != $post->post_status ) {
1224                 return;
1225         }
1226
1227         wpmu_update_blogs_date();
1228 }
1229
1230 /**
1231  * Handler for updating the blog posts count date when a post is deleted.
1232  *
1233  * @since 4.0.0
1234  *
1235  * @param int $post_id Post ID.
1236  */
1237 function _update_posts_count_on_delete( $post_id ) {
1238         $post = get_post( $post_id );
1239
1240         if ( ! $post || 'publish' !== $post->post_status ) {
1241                 return;
1242         }
1243
1244         update_posts_count();
1245 }
1246
1247 /**
1248  * Handler for updating the blog posts count date when a post status changes.
1249  *
1250  * @since 4.0.0
1251  *
1252  * @param string $new_status The status the post is changing to.
1253  * @param string $old_status The status the post is changing from.
1254  */
1255 function _update_posts_count_on_transition_post_status( $new_status, $old_status ) {
1256         if ( $new_status === $old_status ) {
1257                 return;
1258         }
1259
1260         if ( 'publish' !== $new_status && 'publish' !== $old_status ) {
1261                 return;
1262         }
1263
1264         update_posts_count();
1265 }