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