WordPress 4.7
[autoinstalls/wordpress.git] / wp-includes / ms-functions.php
1 <?php
2 /**
3  * Multisite WordPress API
4  *
5  * @package WordPress
6  * @subpackage Multisite
7  * @since 3.0.0
8  */
9
10 /**
11  * Gets the network's site and user counts.
12  *
13  * @since MU 1.0
14  *
15  * @return array Site and user count for the network.
16  */
17 function get_sitestats() {
18         $stats = array(
19                 'blogs' => get_blog_count(),
20                 'users' => get_user_count(),
21         );
22
23         return $stats;
24 }
25
26 /**
27  * Get one of a user's active blogs
28  *
29  * Returns the user's primary blog, if they have one and
30  * it is active. If it's inactive, function returns another
31  * active blog of the user. If none are found, the user
32  * is added as a Subscriber to the Dashboard Blog and that blog
33  * is returned.
34  *
35  * @since MU 1.0
36  *
37  * @global wpdb $wpdb WordPress database abstraction object.
38  *
39  * @param int $user_id The unique ID of the user
40  * @return WP_Site|void The blog object
41  */
42 function get_active_blog_for_user( $user_id ) {
43         global $wpdb;
44         $blogs = get_blogs_of_user( $user_id );
45         if ( empty( $blogs ) )
46                 return;
47
48         if ( !is_multisite() )
49                 return $blogs[$wpdb->blogid];
50
51         $primary_blog = get_user_meta( $user_id, 'primary_blog', true );
52         $first_blog = current($blogs);
53         if ( false !== $primary_blog ) {
54                 if ( ! isset( $blogs[ $primary_blog ] ) ) {
55                         update_user_meta( $user_id, 'primary_blog', $first_blog->userblog_id );
56                         $primary = get_site( $first_blog->userblog_id );
57                 } else {
58                         $primary = get_site( $primary_blog );
59                 }
60         } else {
61                 //TODO Review this call to add_user_to_blog too - to get here the user must have a role on this blog?
62                 add_user_to_blog( $first_blog->userblog_id, $user_id, 'subscriber' );
63                 update_user_meta( $user_id, 'primary_blog', $first_blog->userblog_id );
64                 $primary = $first_blog;
65         }
66
67         if ( ( ! is_object( $primary ) ) || ( $primary->archived == 1 || $primary->spam == 1 || $primary->deleted == 1 ) ) {
68                 $blogs = get_blogs_of_user( $user_id, true ); // if a user's primary blog is shut down, check their other blogs.
69                 $ret = false;
70                 if ( is_array( $blogs ) && count( $blogs ) > 0 ) {
71                         foreach ( (array) $blogs as $blog_id => $blog ) {
72                                 if ( $blog->site_id != $wpdb->siteid )
73                                         continue;
74                                 $details = get_site( $blog_id );
75                                 if ( is_object( $details ) && $details->archived == 0 && $details->spam == 0 && $details->deleted == 0 ) {
76                                         $ret = $blog;
77                                         if ( get_user_meta( $user_id , 'primary_blog', true ) != $blog_id )
78                                                 update_user_meta( $user_id, 'primary_blog', $blog_id );
79                                         if ( !get_user_meta($user_id , 'source_domain', true) )
80                                                 update_user_meta( $user_id, 'source_domain', $blog->domain );
81                                         break;
82                                 }
83                         }
84                 } else {
85                         return;
86                 }
87                 return $ret;
88         } else {
89                 return $primary;
90         }
91 }
92
93 /**
94  * The number of active users in your installation.
95  *
96  * The count is cached and updated twice daily. This is not a live count.
97  *
98  * @since MU 2.7
99  *
100  * @return int
101  */
102 function get_user_count() {
103         return get_site_option( 'user_count' );
104 }
105
106 /**
107  * The number of active sites on your installation.
108  *
109  * The count is cached and updated twice daily. This is not a live count.
110  *
111  * @since MU 1.0
112  *
113  * @param int $network_id Deprecated, not supported.
114  * @return int
115  */
116 function get_blog_count( $network_id = 0 ) {
117         if ( func_num_args() )
118                 _deprecated_argument( __FUNCTION__, '3.1.0' );
119
120         return get_site_option( 'blog_count' );
121 }
122
123 /**
124  * Get a blog post from any site on the network.
125  *
126  * @since MU 1.0
127  *
128  * @param int $blog_id ID of the blog.
129  * @param int $post_id ID of the post you're looking for.
130  * @return WP_Post|null WP_Post on success or null on failure
131  */
132 function get_blog_post( $blog_id, $post_id ) {
133         switch_to_blog( $blog_id );
134         $post = get_post( $post_id );
135         restore_current_blog();
136
137         return $post;
138 }
139
140 /**
141  * Adds a user to a blog.
142  *
143  * Use the {@see 'add_user_to_blog'} action to fire an event when users are added to a blog.
144  *
145  * @since MU 1.0
146  *
147  * @param int    $blog_id ID of the blog you're adding the user to.
148  * @param int    $user_id ID of the user you're adding.
149  * @param string $role    The role you want the user to have
150  * @return true|WP_Error
151  */
152 function add_user_to_blog( $blog_id, $user_id, $role ) {
153         switch_to_blog($blog_id);
154
155         $user = get_userdata( $user_id );
156
157         if ( ! $user ) {
158                 restore_current_blog();
159                 return new WP_Error( 'user_does_not_exist', __( 'The requested user does not exist.' ) );
160         }
161
162         if ( !get_user_meta($user_id, 'primary_blog', true) ) {
163                 update_user_meta($user_id, 'primary_blog', $blog_id);
164                 $site = get_site( $blog_id );
165                 update_user_meta( $user_id, 'source_domain', $site->domain );
166         }
167
168         $user->set_role($role);
169
170         /**
171          * Fires immediately after a user is added to a site.
172          *
173          * @since MU
174          *
175          * @param int    $user_id User ID.
176          * @param string $role    User role.
177          * @param int    $blog_id Blog ID.
178          */
179         do_action( 'add_user_to_blog', $user_id, $role, $blog_id );
180         wp_cache_delete( $user_id, 'users' );
181         wp_cache_delete( $blog_id . '_user_count', 'blog-details' );
182         restore_current_blog();
183         return true;
184 }
185
186 /**
187  * Remove a user from a blog.
188  *
189  * Use the {@see 'remove_user_from_blog'} action to fire an event when
190  * users are removed from a blog.
191  *
192  * Accepts an optional `$reassign` parameter, if you want to
193  * reassign the user's blog posts to another user upon removal.
194  *
195  * @since MU 1.0
196  *
197  * @global wpdb $wpdb WordPress database abstraction object.
198  *
199  * @param int    $user_id  ID of the user you're removing.
200  * @param int    $blog_id  ID of the blog you're removing the user from.
201  * @param string $reassign Optional. A user to whom to reassign posts.
202  * @return true|WP_Error
203  */
204 function remove_user_from_blog($user_id, $blog_id = '', $reassign = '') {
205         global $wpdb;
206         switch_to_blog($blog_id);
207         $user_id = (int) $user_id;
208         /**
209          * Fires before a user is removed from a site.
210          *
211          * @since MU
212          *
213          * @param int $user_id User ID.
214          * @param int $blog_id Blog ID.
215          */
216         do_action( 'remove_user_from_blog', $user_id, $blog_id );
217
218         // If being removed from the primary blog, set a new primary if the user is assigned
219         // to multiple blogs.
220         $primary_blog = get_user_meta($user_id, 'primary_blog', true);
221         if ( $primary_blog == $blog_id ) {
222                 $new_id = '';
223                 $new_domain = '';
224                 $blogs = get_blogs_of_user($user_id);
225                 foreach ( (array) $blogs as $blog ) {
226                         if ( $blog->userblog_id == $blog_id )
227                                 continue;
228                         $new_id = $blog->userblog_id;
229                         $new_domain = $blog->domain;
230                         break;
231                 }
232
233                 update_user_meta($user_id, 'primary_blog', $new_id);
234                 update_user_meta($user_id, 'source_domain', $new_domain);
235         }
236
237         // wp_revoke_user($user_id);
238         $user = get_userdata( $user_id );
239         if ( ! $user ) {
240                 restore_current_blog();
241                 return new WP_Error('user_does_not_exist', __('That user does not exist.'));
242         }
243
244         $user->remove_all_caps();
245
246         $blogs = get_blogs_of_user($user_id);
247         if ( count($blogs) == 0 ) {
248                 update_user_meta($user_id, 'primary_blog', '');
249                 update_user_meta($user_id, 'source_domain', '');
250         }
251
252         if ( $reassign != '' ) {
253                 $reassign = (int) $reassign;
254                 $post_ids = $wpdb->get_col( $wpdb->prepare( "SELECT ID FROM $wpdb->posts WHERE post_author = %d", $user_id ) );
255                 $link_ids = $wpdb->get_col( $wpdb->prepare( "SELECT link_id FROM $wpdb->links WHERE link_owner = %d", $user_id ) );
256
257                 if ( ! empty( $post_ids ) ) {
258                         $wpdb->query( $wpdb->prepare( "UPDATE $wpdb->posts SET post_author = %d WHERE post_author = %d", $reassign, $user_id ) );
259                         array_walk( $post_ids, 'clean_post_cache' );
260                 }
261
262                 if ( ! empty( $link_ids ) ) {
263                         $wpdb->query( $wpdb->prepare( "UPDATE $wpdb->links SET link_owner = %d WHERE link_owner = %d", $reassign, $user_id ) );
264                         array_walk( $link_ids, 'clean_bookmark_cache' );
265                 }
266         }
267
268         restore_current_blog();
269
270         return true;
271 }
272
273 /**
274  * Get the permalink for a post on another blog.
275  *
276  * @since MU 1.0
277  *
278  * @param int $blog_id ID of the source blog.
279  * @param int $post_id ID of the desired post.
280  * @return string The post's permalink
281  */
282 function get_blog_permalink( $blog_id, $post_id ) {
283         switch_to_blog( $blog_id );
284         $link = get_permalink( $post_id );
285         restore_current_blog();
286
287         return $link;
288 }
289
290 /**
291  * Get a blog's numeric ID from its URL.
292  *
293  * On a subdirectory installation like example.com/blog1/,
294  * $domain will be the root 'example.com' and $path the
295  * subdirectory '/blog1/'. With subdomains like blog1.example.com,
296  * $domain is 'blog1.example.com' and $path is '/'.
297  *
298  * @since MU 2.6.5
299  *
300  * @global wpdb $wpdb WordPress database abstraction object.
301  *
302  * @param string $domain
303  * @param string $path   Optional. Not required for subdomain installations.
304  * @return int 0 if no blog found, otherwise the ID of the matching blog
305  */
306 function get_blog_id_from_url( $domain, $path = '/' ) {
307         $domain = strtolower( $domain );
308         $path = strtolower( $path );
309         $id = wp_cache_get( md5( $domain . $path ), 'blog-id-cache' );
310
311         if ( $id == -1 ) // blog does not exist
312                 return 0;
313         elseif ( $id )
314                 return (int) $id;
315
316         $args = array(
317                 'domain' => $domain,
318                 'path' => $path,
319                 'fields' => 'ids',
320         );
321         $result = get_sites( $args );
322         $id = array_shift( $result );
323
324         if ( ! $id ) {
325                 wp_cache_set( md5( $domain . $path ), -1, 'blog-id-cache' );
326                 return 0;
327         }
328
329         wp_cache_set( md5( $domain . $path ), $id, 'blog-id-cache' );
330
331         return $id;
332 }
333
334 // Admin functions
335
336 /**
337  * Checks an email address against a list of banned domains.
338  *
339  * This function checks against the Banned Email Domains list
340  * at wp-admin/network/settings.php. The check is only run on
341  * self-registrations; user creation at wp-admin/network/users.php
342  * bypasses this check.
343  *
344  * @since MU
345  *
346  * @param string $user_email The email provided by the user at registration.
347  * @return bool Returns true when the email address is banned.
348  */
349 function is_email_address_unsafe( $user_email ) {
350         $banned_names = get_site_option( 'banned_email_domains' );
351         if ( $banned_names && ! is_array( $banned_names ) )
352                 $banned_names = explode( "\n", $banned_names );
353
354         $is_email_address_unsafe = false;
355
356         if ( $banned_names && is_array( $banned_names ) ) {
357                 $banned_names = array_map( 'strtolower', $banned_names );
358                 $normalized_email = strtolower( $user_email );
359
360                 list( $email_local_part, $email_domain ) = explode( '@', $normalized_email );
361
362                 foreach ( $banned_names as $banned_domain ) {
363                         if ( ! $banned_domain )
364                                 continue;
365
366                         if ( $email_domain == $banned_domain ) {
367                                 $is_email_address_unsafe = true;
368                                 break;
369                         }
370
371                         $dotted_domain = ".$banned_domain";
372                         if ( $dotted_domain === substr( $normalized_email, -strlen( $dotted_domain ) ) ) {
373                                 $is_email_address_unsafe = true;
374                                 break;
375                         }
376                 }
377         }
378
379         /**
380          * Filters whether an email address is unsafe.
381          *
382          * @since 3.5.0
383          *
384          * @param bool   $is_email_address_unsafe Whether the email address is "unsafe". Default false.
385          * @param string $user_email              User email address.
386          */
387         return apply_filters( 'is_email_address_unsafe', $is_email_address_unsafe, $user_email );
388 }
389
390 /**
391  * Sanitize and validate data required for a user sign-up.
392  *
393  * Verifies the validity and uniqueness of user names and user email addresses,
394  * and checks email addresses against admin-provided domain whitelists and blacklists.
395  *
396  * The {@see 'wpmu_validate_user_signup'} hook provides an easy way to modify the sign-up
397  * process. The value $result, which is passed to the hook, contains both the user-provided
398  * info and the error messages created by the function. {@see 'wpmu_validate_user_signup'}
399  * allows you to process the data in any way you'd like, and unset the relevant errors if
400  * necessary.
401  *
402  * @since MU
403  *
404  * @global wpdb $wpdb WordPress database abstraction object.
405  *
406  * @param string $user_name  The login name provided by the user.
407  * @param string $user_email The email provided by the user.
408  * @return array Contains username, email, and error messages.
409  */
410 function wpmu_validate_user_signup($user_name, $user_email) {
411         global $wpdb;
412
413         $errors = new WP_Error();
414
415         $orig_username = $user_name;
416         $user_name = preg_replace( '/\s+/', '', sanitize_user( $user_name, true ) );
417
418         if ( $user_name != $orig_username || preg_match( '/[^a-z0-9]/', $user_name ) ) {
419                 $errors->add( 'user_name', __( 'Usernames can only contain lowercase letters (a-z) and numbers.' ) );
420                 $user_name = $orig_username;
421         }
422
423         $user_email = sanitize_email( $user_email );
424
425         if ( empty( $user_name ) )
426                 $errors->add('user_name', __( 'Please enter a username.' ) );
427
428         $illegal_names = get_site_option( 'illegal_names' );
429         if ( ! is_array( $illegal_names ) ) {
430                 $illegal_names = array(  'www', 'web', 'root', 'admin', 'main', 'invite', 'administrator' );
431                 add_site_option( 'illegal_names', $illegal_names );
432         }
433         if ( in_array( $user_name, $illegal_names ) ) {
434                 $errors->add( 'user_name',  __( 'Sorry, that username is not allowed.' ) );
435         }
436
437         /** This filter is documented in wp-includes/user.php */
438         $illegal_logins = (array) apply_filters( 'illegal_user_logins', array() );
439
440         if ( in_array( strtolower( $user_name ), array_map( 'strtolower', $illegal_logins ) ) ) {
441                 $errors->add( 'user_name',  __( 'Sorry, that username is not allowed.' ) );
442         }
443
444         if ( is_email_address_unsafe( $user_email ) )
445                 $errors->add('user_email',  __('You cannot use that email address to signup. We are having problems with them blocking some of our email. Please use another email provider.'));
446
447         if ( strlen( $user_name ) < 4 )
448                 $errors->add('user_name',  __( 'Username must be at least 4 characters.' ) );
449
450         if ( strlen( $user_name ) > 60 ) {
451                 $errors->add( 'user_name', __( 'Username may not be longer than 60 characters.' ) );
452         }
453
454         // all numeric?
455         if ( preg_match( '/^[0-9]*$/', $user_name ) )
456                 $errors->add('user_name', __('Sorry, usernames must have letters too!'));
457
458         if ( !is_email( $user_email ) )
459                 $errors->add('user_email', __( 'Please enter a valid email address.' ) );
460
461         $limited_email_domains = get_site_option( 'limited_email_domains' );
462         if ( is_array( $limited_email_domains ) && ! empty( $limited_email_domains ) ) {
463                 $emaildomain = substr( $user_email, 1 + strpos( $user_email, '@' ) );
464                 if ( ! in_array( $emaildomain, $limited_email_domains ) ) {
465                         $errors->add('user_email', __('Sorry, that email address is not allowed!'));
466                 }
467         }
468
469         // Check if the username has been used already.
470         if ( username_exists($user_name) )
471                 $errors->add( 'user_name', __( 'Sorry, that username already exists!' ) );
472
473         // Check if the email address has been used already.
474         if ( email_exists($user_email) )
475                 $errors->add( 'user_email', __( 'Sorry, that email address is already used!' ) );
476
477         // Has someone already signed up for this username?
478         $signup = $wpdb->get_row( $wpdb->prepare("SELECT * FROM $wpdb->signups WHERE user_login = %s", $user_name) );
479         if ( $signup != null ) {
480                 $registered_at =  mysql2date('U', $signup->registered);
481                 $now = current_time( 'timestamp', true );
482                 $diff = $now - $registered_at;
483                 // If registered more than two days ago, cancel registration and let this signup go through.
484                 if ( $diff > 2 * DAY_IN_SECONDS )
485                         $wpdb->delete( $wpdb->signups, array( 'user_login' => $user_name ) );
486                 else
487                         $errors->add('user_name', __('That username is currently reserved but may be available in a couple of days.'));
488         }
489
490         $signup = $wpdb->get_row( $wpdb->prepare("SELECT * FROM $wpdb->signups WHERE user_email = %s", $user_email) );
491         if ( $signup != null ) {
492                 $diff = current_time( 'timestamp', true ) - mysql2date('U', $signup->registered);
493                 // If registered more than two days ago, cancel registration and let this signup go through.
494                 if ( $diff > 2 * DAY_IN_SECONDS )
495                         $wpdb->delete( $wpdb->signups, array( 'user_email' => $user_email ) );
496                 else
497                         $errors->add('user_email', __('That email address has already been used. Please check your inbox for an activation email. It will become available in a couple of days if you do nothing.'));
498         }
499
500         $result = array('user_name' => $user_name, 'orig_username' => $orig_username, 'user_email' => $user_email, 'errors' => $errors);
501
502         /**
503          * Filters the validated user registration details.
504          *
505          * This does not allow you to override the username or email of the user during
506          * registration. The values are solely used for validation and error handling.
507          *
508          * @since MU
509          *
510          * @param array $result {
511          *     The array of user name, email and the error messages.
512          *
513          *     @type string   $user_name     Sanitized and unique username.
514          *     @type string   $orig_username Original username.
515          *     @type string   $user_email    User email address.
516          *     @type WP_Error $errors        WP_Error object containing any errors found.
517          * }
518          */
519         return apply_filters( 'wpmu_validate_user_signup', $result );
520 }
521
522 /**
523  * Processes new site registrations.
524  *
525  * Checks the data provided by the user during blog signup. Verifies
526  * the validity and uniqueness of blog paths and domains.
527  *
528  * This function prevents the current user from registering a new site
529  * with a blogname equivalent to another user's login name. Passing the
530  * $user parameter to the function, where $user is the other user, is
531  * effectively an override of this limitation.
532  *
533  * Filter {@see 'wpmu_validate_blog_signup'} if you want to modify
534  * the way that WordPress validates new site signups.
535  *
536  * @since MU
537  *
538  * @global wpdb   $wpdb
539  * @global string $domain
540  *
541  * @param string         $blogname   The blog name provided by the user. Must be unique.
542  * @param string         $blog_title The blog title provided by the user.
543  * @param WP_User|string $user       Optional. The user object to check against the new site name.
544  * @return array Contains the new site data and error messages.
545  */
546 function wpmu_validate_blog_signup( $blogname, $blog_title, $user = '' ) {
547         global $wpdb, $domain;
548
549         $current_network = get_network();
550         $base = $current_network->path;
551
552         $blog_title = strip_tags( $blog_title );
553
554         $errors = new WP_Error();
555         $illegal_names = get_site_option( 'illegal_names' );
556         if ( $illegal_names == false ) {
557                 $illegal_names = array( 'www', 'web', 'root', 'admin', 'main', 'invite', 'administrator' );
558                 add_site_option( 'illegal_names', $illegal_names );
559         }
560
561         /*
562          * On sub dir installs, some names are so illegal, only a filter can
563          * spring them from jail.
564          */
565         if ( ! is_subdomain_install() ) {
566                 $illegal_names = array_merge( $illegal_names, get_subdirectory_reserved_names() );
567         }
568
569         if ( empty( $blogname ) )
570                 $errors->add('blogname', __( 'Please enter a site name.' ) );
571
572         if ( preg_match( '/[^a-z0-9]+/', $blogname ) ) {
573                 $errors->add( 'blogname', __( 'Site names can only contain lowercase letters (a-z) and numbers.' ) );
574         }
575
576         if ( in_array( $blogname, $illegal_names ) )
577                 $errors->add('blogname',  __( 'That name is not allowed.' ) );
578
579         if ( strlen( $blogname ) < 4 && !is_super_admin() )
580                 $errors->add('blogname',  __( 'Site name must be at least 4 characters.' ) );
581
582         // do not allow users to create a blog that conflicts with a page on the main blog.
583         if ( !is_subdomain_install() && $wpdb->get_var( $wpdb->prepare( "SELECT post_name FROM " . $wpdb->get_blog_prefix( $current_network->site_id ) . "posts WHERE post_type = 'page' AND post_name = %s", $blogname ) ) )
584                 $errors->add( 'blogname', __( 'Sorry, you may not use that site name.' ) );
585
586         // all numeric?
587         if ( preg_match( '/^[0-9]*$/', $blogname ) )
588                 $errors->add('blogname', __('Sorry, site names must have letters too!'));
589
590         /**
591          * Filters the new site name during registration.
592          *
593          * The name is the site's subdomain or the site's subdirectory
594          * path depending on the network settings.
595          *
596          * @since MU
597          *
598          * @param string $blogname Site name.
599          */
600         $blogname = apply_filters( 'newblogname', $blogname );
601
602         $blog_title = wp_unslash(  $blog_title );
603
604         if ( empty( $blog_title ) )
605                 $errors->add('blog_title', __( 'Please enter a site title.' ) );
606
607         // Check if the domain/path has been used already.
608         if ( is_subdomain_install() ) {
609                 $mydomain = $blogname . '.' . preg_replace( '|^www\.|', '', $domain );
610                 $path = $base;
611         } else {
612                 $mydomain = "$domain";
613                 $path = $base.$blogname.'/';
614         }
615         if ( domain_exists($mydomain, $path, $current_network->id) )
616                 $errors->add( 'blogname', __( 'Sorry, that site already exists!' ) );
617
618         if ( username_exists( $blogname ) ) {
619                 if ( ! is_object( $user ) || ( is_object($user) && ( $user->user_login != $blogname ) ) )
620                         $errors->add( 'blogname', __( 'Sorry, that site is reserved!' ) );
621         }
622
623         // Has someone already signed up for this domain?
624         $signup = $wpdb->get_row( $wpdb->prepare("SELECT * FROM $wpdb->signups WHERE domain = %s AND path = %s", $mydomain, $path) ); // TODO: Check email too?
625         if ( ! empty($signup) ) {
626                 $diff = current_time( 'timestamp', true ) - mysql2date('U', $signup->registered);
627                 // If registered more than two days ago, cancel registration and let this signup go through.
628                 if ( $diff > 2 * DAY_IN_SECONDS )
629                         $wpdb->delete( $wpdb->signups, array( 'domain' => $mydomain , 'path' => $path ) );
630                 else
631                         $errors->add('blogname', __('That site is currently reserved but may be available in a couple days.'));
632         }
633
634         $result = array('domain' => $mydomain, 'path' => $path, 'blogname' => $blogname, 'blog_title' => $blog_title, 'user' => $user, 'errors' => $errors);
635
636         /**
637          * Filters site details and error messages following registration.
638          *
639          * @since MU
640          *
641          * @param array $result {
642          *     Array of domain, path, blog name, blog title, user and error messages.
643          *
644          *     @type string         $domain     Domain for the site.
645          *     @type string         $path       Path for the site. Used in subdirectory installs.
646          *     @type string         $blogname   The unique site name (slug).
647          *     @type string         $blog_title Blog title.
648          *     @type string|WP_User $user       By default, an empty string. A user object if provided.
649          *     @type WP_Error       $errors     WP_Error containing any errors found.
650          * }
651          */
652         return apply_filters( 'wpmu_validate_blog_signup', $result );
653 }
654
655 /**
656  * Record site signup information for future activation.
657  *
658  * @since MU
659  *
660  * @global wpdb $wpdb WordPress database abstraction object.
661  *
662  * @param string $domain     The requested domain.
663  * @param string $path       The requested path.
664  * @param string $title      The requested site title.
665  * @param string $user       The user's requested login name.
666  * @param string $user_email The user's email address.
667  * @param array  $meta       By default, contains the requested privacy setting and lang_id.
668  */
669 function wpmu_signup_blog( $domain, $path, $title, $user, $user_email, $meta = array() )  {
670         global $wpdb;
671
672         $key = substr( md5( time() . rand() . $domain ), 0, 16 );
673         $meta = serialize($meta);
674
675         $wpdb->insert( $wpdb->signups, array(
676                 'domain' => $domain,
677                 'path' => $path,
678                 'title' => $title,
679                 'user_login' => $user,
680                 'user_email' => $user_email,
681                 'registered' => current_time('mysql', true),
682                 'activation_key' => $key,
683                 'meta' => $meta
684         ) );
685
686         /**
687          * Fires after site signup information has been written to the database.
688          *
689          * @since 4.4.0
690          *
691          * @param string $domain     The requested domain.
692          * @param string $path       The requested path.
693          * @param string $title      The requested site title.
694          * @param string $user       The user's requested login name.
695          * @param string $user_email The user's email address.
696          * @param string $key        The user's activation key
697          * @param array  $meta       By default, contains the requested privacy setting and lang_id.
698          */
699         do_action( 'after_signup_site', $domain, $path, $title, $user, $user_email, $key, $meta );
700 }
701
702 /**
703  * Record user signup information for future activation.
704  *
705  * This function is used when user registration is open but
706  * new site registration is not.
707  *
708  * @since MU
709  *
710  * @global wpdb $wpdb WordPress database abstraction object.
711  *
712  * @param string $user       The user's requested login name.
713  * @param string $user_email The user's email address.
714  * @param array  $meta       By default, this is an empty array.
715  */
716 function wpmu_signup_user( $user, $user_email, $meta = array() ) {
717         global $wpdb;
718
719         // Format data
720         $user = preg_replace( '/\s+/', '', sanitize_user( $user, true ) );
721         $user_email = sanitize_email( $user_email );
722         $key = substr( md5( time() . rand() . $user_email ), 0, 16 );
723         $meta = serialize($meta);
724
725         $wpdb->insert( $wpdb->signups, array(
726                 'domain' => '',
727                 'path' => '',
728                 'title' => '',
729                 'user_login' => $user,
730                 'user_email' => $user_email,
731                 'registered' => current_time('mysql', true),
732                 'activation_key' => $key,
733                 'meta' => $meta
734         ) );
735
736         /**
737          * Fires after a user's signup information has been written to the database.
738          *
739          * @since 4.4.0
740          *
741          * @param string $user       The user's requested login name.
742          * @param string $user_email The user's email address.
743          * @param string $key        The user's activation key
744          * @param array  $meta       Additional signup meta. By default, this is an empty array.
745          */
746         do_action( 'after_signup_user', $user, $user_email, $key, $meta );
747 }
748
749 /**
750  * Notify user of signup success.
751  *
752  * This is the notification function used when site registration
753  * is enabled.
754  *
755  * Filter {@see 'wpmu_signup_blog_notification'} to bypass this function or
756  * replace it with your own notification behavior.
757  *
758  * Filter {@see 'wpmu_signup_blog_notification_email'} and
759  * {@see 'wpmu_signup_blog_notification_subject'} to change the content
760  * and subject line of the email sent to newly registered users.
761  *
762  * @since MU
763  *
764  * @param string $domain     The new blog domain.
765  * @param string $path       The new blog path.
766  * @param string $title      The site title.
767  * @param string $user_login The user's login name.
768  * @param string $user_email The user's email address.
769  * @param string $key        The activation key created in wpmu_signup_blog()
770  * @param array  $meta       By default, contains the requested privacy setting and lang_id.
771  * @return bool
772  */
773 function wpmu_signup_blog_notification( $domain, $path, $title, $user_login, $user_email, $key, $meta = array() ) {
774         /**
775          * Filters whether to bypass the new site email notification.
776          *
777          * @since MU
778          *
779          * @param string|bool $domain     Site domain.
780          * @param string      $path       Site path.
781          * @param string      $title      Site title.
782          * @param string      $user_login User login name.
783          * @param string      $user_email User email address.
784          * @param string      $key        Activation key created in wpmu_signup_blog().
785          * @param array       $meta       By default, contains the requested privacy setting and lang_id.
786          */
787         if ( ! apply_filters( 'wpmu_signup_blog_notification', $domain, $path, $title, $user_login, $user_email, $key, $meta ) ) {
788                 return false;
789         }
790
791         // Send email with activation link.
792         if ( !is_subdomain_install() || get_current_network_id() != 1 )
793                 $activate_url = network_site_url("wp-activate.php?key=$key");
794         else
795                 $activate_url = "http://{$domain}{$path}wp-activate.php?key=$key"; // @todo use *_url() API
796
797         $activate_url = esc_url($activate_url);
798         $admin_email = get_site_option( 'admin_email' );
799         if ( $admin_email == '' )
800                 $admin_email = 'support@' . $_SERVER['SERVER_NAME'];
801         $from_name = get_site_option( 'site_name' ) == '' ? 'WordPress' : esc_html( get_site_option( 'site_name' ) );
802         $message_headers = "From: \"{$from_name}\" <{$admin_email}>\n" . "Content-Type: text/plain; charset=\"" . get_option('blog_charset') . "\"\n";
803
804         $user = get_user_by( 'login', $user_login );
805         $switched_locale = switch_to_locale( get_user_locale( $user ) );
806
807         $message = sprintf(
808                 /**
809                  * Filters the message content of the new blog notification email.
810                  *
811                  * Content should be formatted for transmission via wp_mail().
812                  *
813                  * @since MU
814                  *
815                  * @param string $content    Content of the notification email.
816                  * @param string $domain     Site domain.
817                  * @param string $path       Site path.
818                  * @param string $title      Site title.
819                  * @param string $user_login User login name.
820                  * @param string $user_email User email address.
821                  * @param string $key        Activation key created in wpmu_signup_blog().
822                  * @param array  $meta       By default, contains the requested privacy setting and lang_id.
823                  */
824                 apply_filters( 'wpmu_signup_blog_notification_email',
825                         __( "To activate your blog, please click the following link:\n\n%s\n\nAfter you activate, you will receive *another email* with your login.\n\nAfter you activate, you can visit your site here:\n\n%s" ),
826                         $domain, $path, $title, $user_login, $user_email, $key, $meta
827                 ),
828                 $activate_url,
829                 esc_url( "http://{$domain}{$path}" ),
830                 $key
831         );
832         // TODO: Don't hard code activation link.
833         $subject = sprintf(
834                 /**
835                  * Filters the subject of the new blog notification email.
836                  *
837                  * @since MU
838                  *
839                  * @param string $subject    Subject of the notification email.
840                  * @param string $domain     Site domain.
841                  * @param string $path       Site path.
842                  * @param string $title      Site title.
843                  * @param string $user_login User login name.
844                  * @param string $user_email User email address.
845                  * @param string $key        Activation key created in wpmu_signup_blog().
846                  * @param array  $meta       By default, contains the requested privacy setting and lang_id.
847                  */
848                 apply_filters( 'wpmu_signup_blog_notification_subject',
849                         /* translators: New site notification email subject. 1: Network name, 2: New site URL */
850                         _x( '[%1$s] Activate %2$s', 'New site notification email subject' ),
851                         $domain, $path, $title, $user_login, $user_email, $key, $meta
852                 ),
853                 $from_name,
854                 esc_url( 'http://' . $domain . $path )
855         );
856         wp_mail( $user_email, wp_specialchars_decode( $subject ), $message, $message_headers );
857
858         if ( $switched_locale ) {
859                 restore_previous_locale();
860         }
861
862         return true;
863 }
864
865 /**
866  * Notify user of signup success.
867  *
868  * This is the notification function used when no new site has
869  * been requested.
870  *
871  * Filter {@see 'wpmu_signup_user_notification'} to bypass this function or
872  * replace it with your own notification behavior.
873  *
874  * Filter {@see 'wpmu_signup_user_notification_email'} and
875  * {@see 'wpmu_signup_user_notification_subject'} to change the content
876  * and subject line of the email sent to newly registered users.
877  *
878  * @since MU
879  *
880  * @param string $user_login The user's login name.
881  * @param string $user_email The user's email address.
882  * @param string $key        The activation key created in wpmu_signup_user()
883  * @param array  $meta       By default, an empty array.
884  * @return bool
885  */
886 function wpmu_signup_user_notification( $user_login, $user_email, $key, $meta = array() ) {
887         /**
888          * Filters whether to bypass the email notification for new user sign-up.
889          *
890          * @since MU
891          *
892          * @param string $user_login User login name.
893          * @param string $user_email User email address.
894          * @param string $key        Activation key created in wpmu_signup_user().
895          * @param array  $meta       Signup meta data.
896          */
897         if ( ! apply_filters( 'wpmu_signup_user_notification', $user_login, $user_email, $key, $meta ) )
898                 return false;
899
900         $user = get_user_by( 'login', $user_login );
901         $switched_locale = switch_to_locale( get_user_locale( $user ) );
902
903         // Send email with activation link.
904         $admin_email = get_site_option( 'admin_email' );
905         if ( $admin_email == '' )
906                 $admin_email = 'support@' . $_SERVER['SERVER_NAME'];
907         $from_name = get_site_option( 'site_name' ) == '' ? 'WordPress' : esc_html( get_site_option( 'site_name' ) );
908         $message_headers = "From: \"{$from_name}\" <{$admin_email}>\n" . "Content-Type: text/plain; charset=\"" . get_option('blog_charset') . "\"\n";
909         $message = sprintf(
910                 /**
911                  * Filters the content of the notification email for new user sign-up.
912                  *
913                  * Content should be formatted for transmission via wp_mail().
914                  *
915                  * @since MU
916                  *
917                  * @param string $content    Content of the notification email.
918                  * @param string $user_login User login name.
919                  * @param string $user_email User email address.
920                  * @param string $key        Activation key created in wpmu_signup_user().
921                  * @param array  $meta       Signup meta data.
922                  */
923                 apply_filters( 'wpmu_signup_user_notification_email',
924                         __( "To activate your user, please click the following link:\n\n%s\n\nAfter you activate, you will receive *another email* with your login." ),
925                         $user_login, $user_email, $key, $meta
926                 ),
927                 site_url( "wp-activate.php?key=$key" )
928         );
929         // TODO: Don't hard code activation link.
930         $subject = sprintf(
931                 /**
932                  * Filters the subject of the notification email of new user signup.
933                  *
934                  * @since MU
935                  *
936                  * @param string $subject    Subject of the notification email.
937                  * @param string $user_login User login name.
938                  * @param string $user_email User email address.
939                  * @param string $key        Activation key created in wpmu_signup_user().
940                  * @param array  $meta       Signup meta data.
941                  */
942                 apply_filters( 'wpmu_signup_user_notification_subject',
943                         /* translators: New user notification email subject. 1: Network name, 2: New user login */
944                         _x( '[%1$s] Activate %2$s', 'New user notification email subject' ),
945                         $user_login, $user_email, $key, $meta
946                 ),
947                 $from_name,
948                 $user_login
949         );
950         wp_mail( $user_email, wp_specialchars_decode( $subject ), $message, $message_headers );
951
952         if ( $switched_locale ) {
953                 restore_previous_locale();
954         }
955
956         return true;
957 }
958
959 /**
960  * Activate a signup.
961  *
962  * Hook to {@see 'wpmu_activate_user'} or {@see 'wpmu_activate_blog'} for events
963  * that should happen only when users or sites are self-created (since
964  * those actions are not called when users and sites are created
965  * by a Super Admin).
966  *
967  * @since MU
968  *
969  * @global wpdb $wpdb WordPress database abstraction object.
970  *
971  * @param string $key The activation key provided to the user.
972  * @return array|WP_Error An array containing information about the activated user and/or blog
973  */
974 function wpmu_activate_signup($key) {
975         global $wpdb;
976
977         $signup = $wpdb->get_row( $wpdb->prepare("SELECT * FROM $wpdb->signups WHERE activation_key = %s", $key) );
978
979         if ( empty( $signup ) )
980                 return new WP_Error( 'invalid_key', __( 'Invalid activation key.' ) );
981
982         if ( $signup->active ) {
983                 if ( empty( $signup->domain ) )
984                         return new WP_Error( 'already_active', __( 'The user is already active.' ), $signup );
985                 else
986                         return new WP_Error( 'already_active', __( 'The site is already active.' ), $signup );
987         }
988
989         $meta = maybe_unserialize($signup->meta);
990         $password = wp_generate_password( 12, false );
991
992         $user_id = username_exists($signup->user_login);
993
994         if ( ! $user_id )
995                 $user_id = wpmu_create_user($signup->user_login, $password, $signup->user_email);
996         else
997                 $user_already_exists = true;
998
999         if ( ! $user_id )
1000                 return new WP_Error('create_user', __('Could not create user'), $signup);
1001
1002         $now = current_time('mysql', true);
1003
1004         if ( empty($signup->domain) ) {
1005                 $wpdb->update( $wpdb->signups, array('active' => 1, 'activated' => $now), array('activation_key' => $key) );
1006
1007                 if ( isset( $user_already_exists ) )
1008                         return new WP_Error( 'user_already_exists', __( 'That username is already activated.' ), $signup);
1009
1010                 /**
1011                  * Fires immediately after a new user is activated.
1012                  *
1013                  * @since MU
1014                  *
1015                  * @param int   $user_id  User ID.
1016                  * @param int   $password User password.
1017                  * @param array $meta     Signup meta data.
1018                  */
1019                 do_action( 'wpmu_activate_user', $user_id, $password, $meta );
1020                 return array( 'user_id' => $user_id, 'password' => $password, 'meta' => $meta );
1021         }
1022
1023         $blog_id = wpmu_create_blog( $signup->domain, $signup->path, $signup->title, $user_id, $meta, $wpdb->siteid );
1024
1025         // TODO: What to do if we create a user but cannot create a blog?
1026         if ( is_wp_error($blog_id) ) {
1027                 // If blog is taken, that means a previous attempt to activate this blog failed in between creating the blog and
1028                 // setting the activation flag. Let's just set the active flag and instruct the user to reset their password.
1029                 if ( 'blog_taken' == $blog_id->get_error_code() ) {
1030                         $blog_id->add_data( $signup );
1031                         $wpdb->update( $wpdb->signups, array( 'active' => 1, 'activated' => $now ), array( 'activation_key' => $key ) );
1032                 }
1033                 return $blog_id;
1034         }
1035
1036         $wpdb->update( $wpdb->signups, array('active' => 1, 'activated' => $now), array('activation_key' => $key) );
1037         /**
1038          * Fires immediately after a site is activated.
1039          *
1040          * @since MU
1041          *
1042          * @param int    $blog_id       Blog ID.
1043          * @param int    $user_id       User ID.
1044          * @param int    $password      User password.
1045          * @param string $signup_title  Site title.
1046          * @param array  $meta          Signup meta data.
1047          */
1048         do_action( 'wpmu_activate_blog', $blog_id, $user_id, $password, $signup->title, $meta );
1049
1050         return array('blog_id' => $blog_id, 'user_id' => $user_id, 'password' => $password, 'title' => $signup->title, 'meta' => $meta);
1051 }
1052
1053 /**
1054  * Create a user.
1055  *
1056  * This function runs when a user self-registers as well as when
1057  * a Super Admin creates a new user. Hook to {@see 'wpmu_new_user'} for events
1058  * that should affect all new users, but only on Multisite (otherwise
1059  * use {@see'user_register'}).
1060  *
1061  * @since MU
1062  *
1063  * @param string $user_name The new user's login name.
1064  * @param string $password  The new user's password.
1065  * @param string $email     The new user's email address.
1066  * @return int|false Returns false on failure, or int $user_id on success
1067  */
1068 function wpmu_create_user( $user_name, $password, $email ) {
1069         $user_name = preg_replace( '/\s+/', '', sanitize_user( $user_name, true ) );
1070
1071         $user_id = wp_create_user( $user_name, $password, $email );
1072         if ( is_wp_error( $user_id ) )
1073                 return false;
1074
1075         // Newly created users have no roles or caps until they are added to a blog.
1076         delete_user_option( $user_id, 'capabilities' );
1077         delete_user_option( $user_id, 'user_level' );
1078
1079         /**
1080          * Fires immediately after a new user is created.
1081          *
1082          * @since MU
1083          *
1084          * @param int $user_id User ID.
1085          */
1086         do_action( 'wpmu_new_user', $user_id );
1087
1088         return $user_id;
1089 }
1090
1091 /**
1092  * Create a site.
1093  *
1094  * This function runs when a user self-registers a new site as well
1095  * as when a Super Admin creates a new site. Hook to {@see 'wpmu_new_blog'}
1096  * for events that should affect all new sites.
1097  *
1098  * On subdirectory installs, $domain is the same as the main site's
1099  * domain, and the path is the subdirectory name (eg 'example.com'
1100  * and '/blog1/'). On subdomain installs, $domain is the new subdomain +
1101  * root domain (eg 'blog1.example.com'), and $path is '/'.
1102  *
1103  * @since MU
1104  *
1105  * @param string $domain  The new site's domain.
1106  * @param string $path    The new site's path.
1107  * @param string $title   The new site's title.
1108  * @param int    $user_id The user ID of the new site's admin.
1109  * @param array  $meta    Optional. Used to set initial site options.
1110  * @param int    $site_id Optional. Only relevant on multi-network installs.
1111  * @return int|WP_Error Returns WP_Error object on failure, int $blog_id on success
1112  */
1113 function wpmu_create_blog( $domain, $path, $title, $user_id, $meta = array(), $site_id = 1 ) {
1114         $defaults = array(
1115                 'public' => 0,
1116                 'WPLANG' => get_site_option( 'WPLANG' ),
1117         );
1118         $meta = wp_parse_args( $meta, $defaults );
1119
1120         $domain = preg_replace( '/\s+/', '', sanitize_user( $domain, true ) );
1121
1122         if ( is_subdomain_install() )
1123                 $domain = str_replace( '@', '', $domain );
1124
1125         $title = strip_tags( $title );
1126         $user_id = (int) $user_id;
1127
1128         if ( empty($path) )
1129                 $path = '/';
1130
1131         // Check if the domain has been used already. We should return an error message.
1132         if ( domain_exists($domain, $path, $site_id) )
1133                 return new WP_Error( 'blog_taken', __( 'Sorry, that site already exists!' ) );
1134
1135         if ( ! wp_installing() ) {
1136                 wp_installing( true );
1137         }
1138
1139         if ( ! $blog_id = insert_blog($domain, $path, $site_id) )
1140                 return new WP_Error('insert_blog', __('Could not create site.'));
1141
1142         switch_to_blog($blog_id);
1143         install_blog($blog_id, $title);
1144         wp_install_defaults($user_id);
1145
1146         add_user_to_blog($blog_id, $user_id, 'administrator');
1147
1148         foreach ( $meta as $key => $value ) {
1149                 if ( in_array( $key, array( 'public', 'archived', 'mature', 'spam', 'deleted', 'lang_id' ) ) )
1150                         update_blog_status( $blog_id, $key, $value );
1151                 else
1152                         update_option( $key, $value );
1153         }
1154
1155         update_option( 'blog_public', (int) $meta['public'] );
1156
1157         if ( ! is_super_admin( $user_id ) && ! get_user_meta( $user_id, 'primary_blog', true ) )
1158                 update_user_meta( $user_id, 'primary_blog', $blog_id );
1159
1160         restore_current_blog();
1161         /**
1162          * Fires immediately after a new site is created.
1163          *
1164          * @since MU
1165          *
1166          * @param int    $blog_id Blog ID.
1167          * @param int    $user_id User ID.
1168          * @param string $domain  Site domain.
1169          * @param string $path    Site path.
1170          * @param int    $site_id Site ID. Only relevant on multi-network installs.
1171          * @param array  $meta    Meta data. Used to set initial site options.
1172          */
1173         do_action( 'wpmu_new_blog', $blog_id, $user_id, $domain, $path, $site_id, $meta );
1174
1175         wp_cache_set( 'last_changed', microtime(), 'sites' );
1176
1177         return $blog_id;
1178 }
1179
1180 /**
1181  * Notifies the network admin that a new site has been activated.
1182  *
1183  * Filter {@see 'newblog_notify_siteadmin'} to change the content of
1184  * the notification email.
1185  *
1186  * @since MU
1187  *
1188  * @param int    $blog_id    The new site's ID.
1189  * @param string $deprecated Not used.
1190  * @return bool
1191  */
1192 function newblog_notify_siteadmin( $blog_id, $deprecated = '' ) {
1193         if ( get_site_option( 'registrationnotification' ) != 'yes' )
1194                 return false;
1195
1196         $email = get_site_option( 'admin_email' );
1197         if ( is_email($email) == false )
1198                 return false;
1199
1200         $options_site_url = esc_url(network_admin_url('settings.php'));
1201
1202         switch_to_blog( $blog_id );
1203         $blogname = get_option( 'blogname' );
1204         $siteurl = site_url();
1205         restore_current_blog();
1206
1207         /* translators: New site notification email. 1: Site URL, 2: User IP address, 3: Settings screen URL */
1208         $msg = sprintf( __( 'New Site: %1$s
1209 URL: %2$s
1210 Remote IP: %3$s
1211
1212 Disable these notifications: %4$s' ), $blogname, $siteurl, wp_unslash( $_SERVER['REMOTE_ADDR'] ), $options_site_url);
1213         /**
1214          * Filters the message body of the new site activation email sent
1215          * to the network administrator.
1216          *
1217          * @since MU
1218          *
1219          * @param string $msg Email body.
1220          */
1221         $msg = apply_filters( 'newblog_notify_siteadmin', $msg );
1222
1223         wp_mail( $email, sprintf( __( 'New Site Registration: %s' ), $siteurl ), $msg );
1224         return true;
1225 }
1226
1227 /**
1228  * Notifies the network admin that a new user has been activated.
1229  *
1230  * Filter {@see 'newuser_notify_siteadmin'} to change the content of
1231  * the notification email.
1232  *
1233  * @since MU
1234  *
1235  * @param int $user_id The new user's ID.
1236  * @return bool
1237  */
1238 function newuser_notify_siteadmin( $user_id ) {
1239         if ( get_site_option( 'registrationnotification' ) != 'yes' )
1240                 return false;
1241
1242         $email = get_site_option( 'admin_email' );
1243
1244         if ( is_email($email) == false )
1245                 return false;
1246
1247         $user = get_userdata( $user_id );
1248
1249         $options_site_url = esc_url(network_admin_url('settings.php'));
1250         /* translators: New user notification email. 1: User login, 2: User IP address, 3: Settings screen URL */
1251         $msg = sprintf(__('New User: %1$s
1252 Remote IP: %2$s
1253
1254 Disable these notifications: %3$s'), $user->user_login, wp_unslash( $_SERVER['REMOTE_ADDR'] ), $options_site_url);
1255
1256         /**
1257          * Filters the message body of the new user activation email sent
1258          * to the network administrator.
1259          *
1260          * @since MU
1261          *
1262          * @param string  $msg  Email body.
1263          * @param WP_User $user WP_User instance of the new user.
1264          */
1265         $msg = apply_filters( 'newuser_notify_siteadmin', $msg, $user );
1266         wp_mail( $email, sprintf(__('New User Registration: %s'), $user->user_login), $msg );
1267         return true;
1268 }
1269
1270 /**
1271  * Check whether a blogname is already taken.
1272  *
1273  * Used during the new site registration process to ensure
1274  * that each blogname is unique.
1275  *
1276  * @since MU
1277  *
1278  * @global wpdb $wpdb WordPress database abstraction object.
1279  *
1280  * @param string $domain  The domain to be checked.
1281  * @param string $path    The path to be checked.
1282  * @param int    $site_id Optional. Relevant only on multi-network installs.
1283  * @return int
1284  */
1285 function domain_exists($domain, $path, $site_id = 1) {
1286         $path = trailingslashit( $path );
1287         $args = array(
1288                 'network_id' => $site_id,
1289                 'domain' => $domain,
1290                 'path' => $path,
1291                 'fields' => 'ids',
1292         );
1293         $result = get_sites( $args );
1294         $result = array_shift( $result );
1295
1296         /**
1297          * Filters whether a blogname is taken.
1298          *
1299          * @since 3.5.0
1300          *
1301          * @param int|null $result  The blog_id if the blogname exists, null otherwise.
1302          * @param string   $domain  Domain to be checked.
1303          * @param string   $path    Path to be checked.
1304          * @param int      $site_id Site ID. Relevant only on multi-network installs.
1305          */
1306         return apply_filters( 'domain_exists', $result, $domain, $path, $site_id );
1307 }
1308
1309 /**
1310  * Store basic site info in the blogs table.
1311  *
1312  * This function creates a row in the wp_blogs table and returns
1313  * the new blog's ID. It is the first step in creating a new blog.
1314  *
1315  * @since MU
1316  *
1317  * @global wpdb $wpdb WordPress database abstraction object.
1318  *
1319  * @param string $domain  The domain of the new site.
1320  * @param string $path    The path of the new site.
1321  * @param int    $site_id Unless you're running a multi-network install, be sure to set this value to 1.
1322  * @return int|false The ID of the new row
1323  */
1324 function insert_blog($domain, $path, $site_id) {
1325         global $wpdb;
1326
1327         $path = trailingslashit($path);
1328         $site_id = (int) $site_id;
1329
1330         $result = $wpdb->insert( $wpdb->blogs, array('site_id' => $site_id, 'domain' => $domain, 'path' => $path, 'registered' => current_time('mysql')) );
1331         if ( ! $result )
1332                 return false;
1333
1334         $blog_id = $wpdb->insert_id;
1335         refresh_blog_details( $blog_id );
1336
1337         wp_maybe_update_network_site_counts();
1338
1339         return $blog_id;
1340 }
1341
1342 /**
1343  * Install an empty blog.
1344  *
1345  * Creates the new blog tables and options. If calling this function
1346  * directly, be sure to use switch_to_blog() first, so that $wpdb
1347  * points to the new blog.
1348  *
1349  * @since MU
1350  *
1351  * @global wpdb     $wpdb
1352  * @global WP_Roles $wp_roles
1353  *
1354  * @param int    $blog_id    The value returned by insert_blog().
1355  * @param string $blog_title The title of the new site.
1356  */
1357 function install_blog( $blog_id, $blog_title = '' ) {
1358         global $wpdb, $wp_roles;
1359
1360         // Cast for security
1361         $blog_id = (int) $blog_id;
1362
1363         require_once( ABSPATH . 'wp-admin/includes/upgrade.php' );
1364
1365         $suppress = $wpdb->suppress_errors();
1366         if ( $wpdb->get_results( "DESCRIBE {$wpdb->posts}" ) )
1367                 die( '<h1>' . __( 'Already Installed' ) . '</h1><p>' . __( 'You appear to have already installed WordPress. To reinstall please clear your old database tables first.' ) . '</p></body></html>' );
1368         $wpdb->suppress_errors( $suppress );
1369
1370         $url = get_blogaddress_by_id( $blog_id );
1371
1372         // Set everything up
1373         make_db_current_silent( 'blog' );
1374         populate_options();
1375         populate_roles();
1376
1377         // populate_roles() clears previous role definitions so we start over.
1378         $wp_roles = new WP_Roles();
1379
1380         $siteurl = $home = untrailingslashit( $url );
1381
1382         if ( ! is_subdomain_install() ) {
1383
1384                 if ( 'https' === parse_url( get_site_option( 'siteurl' ), PHP_URL_SCHEME ) ) {
1385                         $siteurl = set_url_scheme( $siteurl, 'https' );
1386                 }
1387                 if ( 'https' === parse_url( get_home_url( get_network()->site_id ), PHP_URL_SCHEME ) ) {
1388                         $home = set_url_scheme( $home, 'https' );
1389                 }
1390
1391         }
1392
1393         update_option( 'siteurl', $siteurl );
1394         update_option( 'home', $home );
1395
1396         if ( get_site_option( 'ms_files_rewriting' ) )
1397                 update_option( 'upload_path', UPLOADBLOGSDIR . "/$blog_id/files" );
1398         else
1399                 update_option( 'upload_path', get_blog_option( get_network()->site_id, 'upload_path' ) );
1400
1401         update_option( 'blogname', wp_unslash( $blog_title ) );
1402         update_option( 'admin_email', '' );
1403
1404         // remove all perms
1405         $table_prefix = $wpdb->get_blog_prefix();
1406         delete_metadata( 'user', 0, $table_prefix . 'user_level',   null, true ); // delete all
1407         delete_metadata( 'user', 0, $table_prefix . 'capabilities', null, true ); // delete all
1408 }
1409
1410 /**
1411  * Set blog defaults.
1412  *
1413  * This function creates a row in the wp_blogs table.
1414  *
1415  * @since MU
1416  * @deprecated MU
1417  * @deprecated Use wp_install_defaults()
1418  *
1419  * @global wpdb $wpdb WordPress database abstraction object.
1420  *
1421  * @param int $blog_id Ignored in this function.
1422  * @param int $user_id
1423  */
1424 function install_blog_defaults($blog_id, $user_id) {
1425         global $wpdb;
1426
1427         require_once( ABSPATH . 'wp-admin/includes/upgrade.php' );
1428
1429         $suppress = $wpdb->suppress_errors();
1430
1431         wp_install_defaults($user_id);
1432
1433         $wpdb->suppress_errors( $suppress );
1434 }
1435
1436 /**
1437  * Notify a user that their blog activation has been successful.
1438  *
1439  * Filter {@see 'wpmu_welcome_notification'} to disable or bypass.
1440  *
1441  * Filter {@see 'update_welcome_email'} and {@see 'update_welcome_subject'} to
1442  * modify the content and subject line of the notification email.
1443  *
1444  * @since MU
1445  *
1446  * @param int    $blog_id
1447  * @param int    $user_id
1448  * @param string $password
1449  * @param string $title    The new blog's title
1450  * @param array  $meta     Optional. Not used in the default function, but is passed along to hooks for customization.
1451  * @return bool
1452  */
1453 function wpmu_welcome_notification( $blog_id, $user_id, $password, $title, $meta = array() ) {
1454         $current_network = get_network();
1455
1456         /**
1457          * Filters whether to bypass the welcome email after site activation.
1458          *
1459          * Returning false disables the welcome email.
1460          *
1461          * @since MU
1462          *
1463          * @param int|bool $blog_id  Blog ID.
1464          * @param int      $user_id  User ID.
1465          * @param string   $password User password.
1466          * @param string   $title    Site title.
1467          * @param array    $meta     Signup meta data.
1468          */
1469         if ( ! apply_filters( 'wpmu_welcome_notification', $blog_id, $user_id, $password, $title, $meta ) )
1470                 return false;
1471
1472         $user = get_userdata( $user_id );
1473
1474         $switched_locale = switch_to_locale( get_user_locale( $user ) );
1475
1476         $welcome_email = get_site_option( 'welcome_email' );
1477         if ( $welcome_email == false ) {
1478                 /* translators: Do not translate USERNAME, SITE_NAME, BLOG_URL, PASSWORD: those are placeholders. */
1479                 $welcome_email = __( 'Howdy USERNAME,
1480
1481 Your new SITE_NAME site has been successfully set up at:
1482 BLOG_URL
1483
1484 You can log in to the administrator account with the following information:
1485
1486 Username: USERNAME
1487 Password: PASSWORD
1488 Log in here: BLOG_URLwp-login.php
1489
1490 We hope you enjoy your new site. Thanks!
1491
1492 --The Team @ SITE_NAME' );
1493         }
1494
1495         $url = get_blogaddress_by_id($blog_id);
1496
1497         $welcome_email = str_replace( 'SITE_NAME', $current_network->site_name, $welcome_email );
1498         $welcome_email = str_replace( 'BLOG_TITLE', $title, $welcome_email );
1499         $welcome_email = str_replace( 'BLOG_URL', $url, $welcome_email );
1500         $welcome_email = str_replace( 'USERNAME', $user->user_login, $welcome_email );
1501         $welcome_email = str_replace( 'PASSWORD', $password, $welcome_email );
1502
1503         /**
1504          * Filters the content of the welcome email after site activation.
1505          *
1506          * Content should be formatted for transmission via wp_mail().
1507          *
1508          * @since MU
1509          *
1510          * @param string $welcome_email Message body of the email.
1511          * @param int    $blog_id       Blog ID.
1512          * @param int    $user_id       User ID.
1513          * @param string $password      User password.
1514          * @param string $title         Site title.
1515          * @param array  $meta          Signup meta data.
1516          */
1517         $welcome_email = apply_filters( 'update_welcome_email', $welcome_email, $blog_id, $user_id, $password, $title, $meta );
1518         $admin_email = get_site_option( 'admin_email' );
1519
1520         if ( $admin_email == '' )
1521                 $admin_email = 'support@' . $_SERVER['SERVER_NAME'];
1522
1523         $from_name = get_site_option( 'site_name' ) == '' ? 'WordPress' : esc_html( get_site_option( 'site_name' ) );
1524         $message_headers = "From: \"{$from_name}\" <{$admin_email}>\n" . "Content-Type: text/plain; charset=\"" . get_option('blog_charset') . "\"\n";
1525         $message = $welcome_email;
1526
1527         if ( empty( $current_network->site_name ) )
1528                 $current_network->site_name = 'WordPress';
1529
1530         /* translators: New site notification email subject. 1: Network name, 2: New site name */
1531         $subject = __( 'New %1$s Site: %2$s' );
1532
1533         /**
1534          * Filters the subject of the welcome email after site activation.
1535          *
1536          * @since MU
1537          *
1538          * @param string $subject Subject of the email.
1539          */
1540         $subject = apply_filters( 'update_welcome_subject', sprintf( $subject, $current_network->site_name, wp_unslash( $title ) ) );
1541         wp_mail( $user->user_email, wp_specialchars_decode( $subject ), $message, $message_headers );
1542
1543         if ( $switched_locale ) {
1544                 restore_previous_locale();
1545         }
1546
1547         return true;
1548 }
1549
1550 /**
1551  * Notify a user that their account activation has been successful.
1552  *
1553  * Filter {@see 'wpmu_welcome_user_notification'} to disable or bypass.
1554  *
1555  * Filter {@see 'update_welcome_user_email'} and {@see 'update_welcome_user_subject'} to
1556  * modify the content and subject line of the notification email.
1557  *
1558  * @since MU
1559  *
1560  * @param int    $user_id
1561  * @param string $password
1562  * @param array  $meta     Optional. Not used in the default function, but is passed along to hooks for customization.
1563  * @return bool
1564  */
1565 function wpmu_welcome_user_notification( $user_id, $password, $meta = array() ) {
1566         $current_network = get_network();
1567
1568         /**
1569          * Filters whether to bypass the welcome email after user activation.
1570          *
1571          * Returning false disables the welcome email.
1572          *
1573          * @since MU
1574          *
1575          * @param int    $user_id  User ID.
1576          * @param string $password User password.
1577          * @param array  $meta     Signup meta data.
1578          */
1579         if ( ! apply_filters( 'wpmu_welcome_user_notification', $user_id, $password, $meta ) )
1580                 return false;
1581
1582         $welcome_email = get_site_option( 'welcome_user_email' );
1583
1584         $user = get_userdata( $user_id );
1585
1586         $switched_locale = switch_to_locale( get_user_locale( $user ) );
1587
1588         /**
1589          * Filters the content of the welcome email after user activation.
1590          *
1591          * Content should be formatted for transmission via wp_mail().
1592          *
1593          * @since MU
1594          *
1595          * @param string $welcome_email The message body of the account activation success email.
1596          * @param int    $user_id       User ID.
1597          * @param string $password      User password.
1598          * @param array  $meta          Signup meta data.
1599          */
1600         $welcome_email = apply_filters( 'update_welcome_user_email', $welcome_email, $user_id, $password, $meta );
1601         $welcome_email = str_replace( 'SITE_NAME', $current_network->site_name, $welcome_email );
1602         $welcome_email = str_replace( 'USERNAME', $user->user_login, $welcome_email );
1603         $welcome_email = str_replace( 'PASSWORD', $password, $welcome_email );
1604         $welcome_email = str_replace( 'LOGINLINK', wp_login_url(), $welcome_email );
1605
1606         $admin_email = get_site_option( 'admin_email' );
1607
1608         if ( $admin_email == '' )
1609                 $admin_email = 'support@' . $_SERVER['SERVER_NAME'];
1610
1611         $from_name = get_site_option( 'site_name' ) == '' ? 'WordPress' : esc_html( get_site_option( 'site_name' ) );
1612         $message_headers = "From: \"{$from_name}\" <{$admin_email}>\n" . "Content-Type: text/plain; charset=\"" . get_option('blog_charset') . "\"\n";
1613         $message = $welcome_email;
1614
1615         if ( empty( $current_network->site_name ) )
1616                 $current_network->site_name = 'WordPress';
1617
1618         /* translators: New user notification email subject. 1: Network name, 2: New user login */
1619         $subject = __( 'New %1$s User: %2$s' );
1620
1621         /**
1622          * Filters the subject of the welcome email after user activation.
1623          *
1624          * @since MU
1625          *
1626          * @param string $subject Subject of the email.
1627          */
1628         $subject = apply_filters( 'update_welcome_user_subject', sprintf( $subject, $current_network->site_name, $user->user_login) );
1629         wp_mail( $user->user_email, wp_specialchars_decode( $subject ), $message, $message_headers );
1630
1631         if ( $switched_locale ) {
1632                 restore_previous_locale();
1633         }
1634
1635         return true;
1636 }
1637
1638 /**
1639  * Get the current network.
1640  *
1641  * Returns an object containing the 'id', 'domain', 'path', and 'site_name'
1642  * properties of the network being viewed.
1643  *
1644  * @see wpmu_current_site()
1645  *
1646  * @since MU
1647  *
1648  * @global WP_Network $current_site
1649  *
1650  * @return WP_Network
1651  */
1652 function get_current_site() {
1653         global $current_site;
1654         return $current_site;
1655 }
1656
1657 /**
1658  * Get a user's most recent post.
1659  *
1660  * Walks through each of a user's blogs to find the post with
1661  * the most recent post_date_gmt.
1662  *
1663  * @since MU
1664  *
1665  * @global wpdb $wpdb WordPress database abstraction object.
1666  *
1667  * @param int $user_id
1668  * @return array Contains the blog_id, post_id, post_date_gmt, and post_gmt_ts
1669  */
1670 function get_most_recent_post_of_user( $user_id ) {
1671         global $wpdb;
1672
1673         $user_blogs = get_blogs_of_user( (int) $user_id );
1674         $most_recent_post = array();
1675
1676         // Walk through each blog and get the most recent post
1677         // published by $user_id
1678         foreach ( (array) $user_blogs as $blog ) {
1679                 $prefix = $wpdb->get_blog_prefix( $blog->userblog_id );
1680                 $recent_post = $wpdb->get_row( $wpdb->prepare("SELECT ID, post_date_gmt FROM {$prefix}posts WHERE post_author = %d AND post_type = 'post' AND post_status = 'publish' ORDER BY post_date_gmt DESC LIMIT 1", $user_id ), ARRAY_A);
1681
1682                 // Make sure we found a post
1683                 if ( isset($recent_post['ID']) ) {
1684                         $post_gmt_ts = strtotime($recent_post['post_date_gmt']);
1685
1686                         // If this is the first post checked or if this post is
1687                         // newer than the current recent post, make it the new
1688                         // most recent post.
1689                         if ( !isset($most_recent_post['post_gmt_ts']) || ( $post_gmt_ts > $most_recent_post['post_gmt_ts'] ) ) {
1690                                 $most_recent_post = array(
1691                                         'blog_id'               => $blog->userblog_id,
1692                                         'post_id'               => $recent_post['ID'],
1693                                         'post_date_gmt' => $recent_post['post_date_gmt'],
1694                                         'post_gmt_ts'   => $post_gmt_ts
1695                                 );
1696                         }
1697                 }
1698         }
1699
1700         return $most_recent_post;
1701 }
1702
1703 // Misc functions
1704
1705 /**
1706  * Get the size of a directory.
1707  *
1708  * A helper function that is used primarily to check whether
1709  * a blog has exceeded its allowed upload space.
1710  *
1711  * @since MU
1712  *
1713  * @param string $directory Full path of a directory.
1714  * @return int Size of the directory in MB.
1715  */
1716 function get_dirsize( $directory ) {
1717         $dirsize = get_transient( 'dirsize_cache' );
1718         if ( is_array( $dirsize ) && isset( $dirsize[ $directory ][ 'size' ] ) )
1719                 return $dirsize[ $directory ][ 'size' ];
1720
1721         if ( ! is_array( $dirsize ) )
1722                 $dirsize = array();
1723
1724         // Exclude individual site directories from the total when checking the main site,
1725         // as they are subdirectories and should not be counted.
1726         if ( is_main_site() ) {
1727                 $dirsize[ $directory ][ 'size' ] = recurse_dirsize( $directory, $directory . '/sites' );
1728         } else {
1729                 $dirsize[ $directory ][ 'size' ] = recurse_dirsize( $directory );
1730         }
1731
1732         set_transient( 'dirsize_cache', $dirsize, HOUR_IN_SECONDS );
1733         return $dirsize[ $directory ][ 'size' ];
1734 }
1735
1736 /**
1737  * Get the size of a directory recursively.
1738  *
1739  * Used by get_dirsize() to get a directory's size when it contains
1740  * other directories.
1741  *
1742  * @since MU
1743  * @since 4.3.0 $exclude parameter added.
1744  *
1745  * @param string $directory Full path of a directory.
1746  * @param string $exclude   Optional. Full path of a subdirectory to exclude from the total.
1747  * @return int|false Size in MB if a valid directory. False if not.
1748  */
1749 function recurse_dirsize( $directory, $exclude = null ) {
1750         $size = 0;
1751
1752         $directory = untrailingslashit( $directory );
1753
1754         if ( ! file_exists( $directory ) || ! is_dir( $directory ) || ! is_readable( $directory ) || $directory === $exclude ) {
1755                 return false;
1756         }
1757
1758         if ($handle = opendir($directory)) {
1759                 while(($file = readdir($handle)) !== false) {
1760                         $path = $directory.'/'.$file;
1761                         if ($file != '.' && $file != '..') {
1762                                 if (is_file($path)) {
1763                                         $size += filesize($path);
1764                                 } elseif (is_dir($path)) {
1765                                         $handlesize = recurse_dirsize( $path, $exclude );
1766                                         if ($handlesize > 0)
1767                                                 $size += $handlesize;
1768                                 }
1769                         }
1770                 }
1771                 closedir($handle);
1772         }
1773         return $size;
1774 }
1775
1776 /**
1777  * Check an array of MIME types against a whitelist.
1778  *
1779  * WordPress ships with a set of allowed upload filetypes,
1780  * which is defined in wp-includes/functions.php in
1781  * get_allowed_mime_types(). This function is used to filter
1782  * that list against the filetype whitelist provided by Multisite
1783  * Super Admins at wp-admin/network/settings.php.
1784  *
1785  * @since MU
1786  *
1787  * @param array $mimes
1788  * @return array
1789  */
1790 function check_upload_mimes( $mimes ) {
1791         $site_exts = explode( ' ', get_site_option( 'upload_filetypes', 'jpg jpeg png gif' ) );
1792         $site_mimes = array();
1793         foreach ( $site_exts as $ext ) {
1794                 foreach ( $mimes as $ext_pattern => $mime ) {
1795                         if ( $ext != '' && strpos( $ext_pattern, $ext ) !== false )
1796                                 $site_mimes[$ext_pattern] = $mime;
1797                 }
1798         }
1799         return $site_mimes;
1800 }
1801
1802 /**
1803  * Update a blog's post count.
1804  *
1805  * WordPress MS stores a blog's post count as an option so as
1806  * to avoid extraneous COUNTs when a blog's details are fetched
1807  * with get_site(). This function is called when posts are published
1808  * or unpublished to make sure the count stays current.
1809  *
1810  * @since MU
1811  *
1812  * @global wpdb $wpdb WordPress database abstraction object.
1813  *
1814  * @param string $deprecated Not used.
1815  */
1816 function update_posts_count( $deprecated = '' ) {
1817         global $wpdb;
1818         update_option( 'post_count', (int) $wpdb->get_var( "SELECT COUNT(ID) FROM {$wpdb->posts} WHERE post_status = 'publish' and post_type = 'post'" ) );
1819 }
1820
1821 /**
1822  * Logs user registrations.
1823  *
1824  * @since MU
1825  *
1826  * @global wpdb $wpdb WordPress database abstraction object.
1827  *
1828  * @param int $blog_id
1829  * @param int $user_id
1830  */
1831 function wpmu_log_new_registrations( $blog_id, $user_id ) {
1832         global $wpdb;
1833         $user = get_userdata( (int) $user_id );
1834         if ( $user )
1835                 $wpdb->insert( $wpdb->registration_log, array('email' => $user->user_email, 'IP' => preg_replace( '/[^0-9., ]/', '', wp_unslash( $_SERVER['REMOTE_ADDR'] ) ), 'blog_id' => $blog_id, 'date_registered' => current_time('mysql')) );
1836 }
1837
1838 /**
1839  * Maintains a canonical list of terms by syncing terms created for each blog with the global terms table.
1840  *
1841  * @since 3.0.0
1842  *
1843  * @see term_id_filter
1844  *
1845  * @global wpdb $wpdb WordPress database abstraction object.
1846  * @staticvar int $global_terms_recurse
1847  *
1848  * @param int    $term_id    An ID for a term on the current blog.
1849  * @param string $deprecated Not used.
1850  * @return int An ID from the global terms table mapped from $term_id.
1851  */
1852 function global_terms( $term_id, $deprecated = '' ) {
1853         global $wpdb;
1854         static $global_terms_recurse = null;
1855
1856         if ( !global_terms_enabled() )
1857                 return $term_id;
1858
1859         // prevent a race condition
1860         $recurse_start = false;
1861         if ( $global_terms_recurse === null ) {
1862                 $recurse_start = true;
1863                 $global_terms_recurse = 1;
1864         } elseif ( 10 < $global_terms_recurse++ ) {
1865                 return $term_id;
1866         }
1867
1868         $term_id = intval( $term_id );
1869         $c = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM $wpdb->terms WHERE term_id = %d", $term_id ) );
1870
1871         $global_id = $wpdb->get_var( $wpdb->prepare( "SELECT cat_ID FROM $wpdb->sitecategories WHERE category_nicename = %s", $c->slug ) );
1872         if ( $global_id == null ) {
1873                 $used_global_id = $wpdb->get_var( $wpdb->prepare( "SELECT cat_ID FROM $wpdb->sitecategories WHERE cat_ID = %d", $c->term_id ) );
1874                 if ( null == $used_global_id ) {
1875                         $wpdb->insert( $wpdb->sitecategories, array( 'cat_ID' => $term_id, 'cat_name' => $c->name, 'category_nicename' => $c->slug ) );
1876                         $global_id = $wpdb->insert_id;
1877                         if ( empty( $global_id ) )
1878                                 return $term_id;
1879                 } else {
1880                         $max_global_id = $wpdb->get_var( "SELECT MAX(cat_ID) FROM $wpdb->sitecategories" );
1881                         $max_local_id = $wpdb->get_var( "SELECT MAX(term_id) FROM $wpdb->terms" );
1882                         $new_global_id = max( $max_global_id, $max_local_id ) + mt_rand( 100, 400 );
1883                         $wpdb->insert( $wpdb->sitecategories, array( 'cat_ID' => $new_global_id, 'cat_name' => $c->name, 'category_nicename' => $c->slug ) );
1884                         $global_id = $wpdb->insert_id;
1885                 }
1886         } elseif ( $global_id != $term_id ) {
1887                 $local_id = $wpdb->get_var( $wpdb->prepare( "SELECT term_id FROM $wpdb->terms WHERE term_id = %d", $global_id ) );
1888                 if ( null != $local_id ) {
1889                         global_terms( $local_id );
1890                         if ( 10 < $global_terms_recurse ) {
1891                                 $global_id = $term_id;
1892                         }
1893                 }
1894         }
1895
1896         if ( $global_id != $term_id ) {
1897                 if ( get_option( 'default_category' ) == $term_id )
1898                         update_option( 'default_category', $global_id );
1899
1900                 $wpdb->update( $wpdb->terms, array('term_id' => $global_id), array('term_id' => $term_id) );
1901                 $wpdb->update( $wpdb->term_taxonomy, array('term_id' => $global_id), array('term_id' => $term_id) );
1902                 $wpdb->update( $wpdb->term_taxonomy, array('parent' => $global_id), array('parent' => $term_id) );
1903
1904                 clean_term_cache($term_id);
1905         }
1906         if ( $recurse_start )
1907                 $global_terms_recurse = null;
1908
1909         return $global_id;
1910 }
1911
1912 /**
1913  * Ensure that the current site's domain is listed in the allowed redirect host list.
1914  *
1915  * @see wp_validate_redirect()
1916  * @since MU
1917  *
1918  * @param array|string $deprecated Not used.
1919  * @return array The current site's domain
1920  */
1921 function redirect_this_site( $deprecated = '' ) {
1922         return array( get_network()->domain );
1923 }
1924
1925 /**
1926  * Check whether an upload is too big.
1927  *
1928  * @since MU
1929  *
1930  * @blessed
1931  *
1932  * @param array $upload
1933  * @return string|array If the upload is under the size limit, $upload is returned. Otherwise returns an error message.
1934  */
1935 function upload_is_file_too_big( $upload ) {
1936         if ( ! is_array( $upload ) || defined( 'WP_IMPORTING' ) || get_site_option( 'upload_space_check_disabled' ) )
1937                 return $upload;
1938
1939         if ( strlen( $upload['bits'] )  > ( KB_IN_BYTES * get_site_option( 'fileupload_maxk', 1500 ) ) ) {
1940                 return sprintf( __( 'This file is too big. Files must be less than %d KB in size.' ) . '<br />', get_site_option( 'fileupload_maxk', 1500 ) );
1941         }
1942
1943         return $upload;
1944 }
1945
1946 /**
1947  * Add a nonce field to the signup page.
1948  *
1949  * @since MU
1950  */
1951 function signup_nonce_fields() {
1952         $id = mt_rand();
1953         echo "<input type='hidden' name='signup_form_id' value='{$id}' />";
1954         wp_nonce_field('signup_form_' . $id, '_signup_form', false);
1955 }
1956
1957 /**
1958  * Process the signup nonce created in signup_nonce_fields().
1959  *
1960  * @since MU
1961  *
1962  * @param array $result
1963  * @return array
1964  */
1965 function signup_nonce_check( $result ) {
1966         if ( !strpos( $_SERVER[ 'PHP_SELF' ], 'wp-signup.php' ) )
1967                 return $result;
1968
1969         if ( wp_create_nonce('signup_form_' . $_POST[ 'signup_form_id' ]) != $_POST['_signup_form'] )
1970                 wp_die( __( 'Please try again.' ) );
1971
1972         return $result;
1973 }
1974
1975 /**
1976  * Correct 404 redirects when NOBLOGREDIRECT is defined.
1977  *
1978  * @since MU
1979  */
1980 function maybe_redirect_404() {
1981         /**
1982          * Filters the redirect URL for 404s on the main site.
1983          *
1984          * The filter is only evaluated if the NOBLOGREDIRECT constant is defined.
1985          *
1986          * @since 3.0.0
1987          *
1988          * @param string $no_blog_redirect The redirect URL defined in NOBLOGREDIRECT.
1989          */
1990         if ( is_main_site() && is_404() && defined( 'NOBLOGREDIRECT' ) && ( $destination = apply_filters( 'blog_redirect_404', NOBLOGREDIRECT ) ) ) {
1991                 if ( $destination == '%siteurl%' )
1992                         $destination = network_home_url();
1993                 wp_redirect( $destination );
1994                 exit();
1995         }
1996 }
1997
1998 /**
1999  * Add a new user to a blog by visiting /newbloguser/username/.
2000  *
2001  * This will only work when the user's details are saved as an option
2002  * keyed as 'new_user_x', where 'x' is the username of the user to be
2003  * added, as when a user is invited through the regular WP Add User interface.
2004  *
2005  * @since MU
2006  */
2007 function maybe_add_existing_user_to_blog() {
2008         if ( false === strpos( $_SERVER[ 'REQUEST_URI' ], '/newbloguser/' ) )
2009                 return;
2010
2011         $parts = explode( '/', $_SERVER[ 'REQUEST_URI' ] );
2012         $key = array_pop( $parts );
2013
2014         if ( $key == '' )
2015                 $key = array_pop( $parts );
2016
2017         $details = get_option( 'new_user_' . $key );
2018         if ( !empty( $details ) )
2019                 delete_option( 'new_user_' . $key );
2020
2021         if ( empty( $details ) || is_wp_error( add_existing_user_to_blog( $details ) ) )
2022                 wp_die( sprintf(__('An error occurred adding you to this site. Back to the <a href="%s">homepage</a>.'), home_url() ) );
2023
2024         wp_die( sprintf( __( 'You have been added to this site. Please visit the <a href="%s">homepage</a> or <a href="%s">log in</a> using your username and password.' ), home_url(), admin_url() ), __( 'WordPress &rsaquo; Success' ), array( 'response' => 200 ) );
2025 }
2026
2027 /**
2028  * Add a user to a blog based on details from maybe_add_existing_user_to_blog().
2029  *
2030  * @since MU
2031  *
2032  * @param array $details
2033  * @return true|WP_Error|void
2034  */
2035 function add_existing_user_to_blog( $details = false ) {
2036         if ( is_array( $details ) ) {
2037                 $blog_id = get_current_blog_id();
2038                 $result = add_user_to_blog( $blog_id, $details[ 'user_id' ], $details[ 'role' ] );
2039                 /**
2040                  * Fires immediately after an existing user is added to a site.
2041                  *
2042                  * @since MU
2043                  *
2044                  * @param int   $user_id User ID.
2045                  * @param mixed $result  True on success or a WP_Error object if the user doesn't exist.
2046                  */
2047                 do_action( 'added_existing_user', $details['user_id'], $result );
2048                 return $result;
2049         }
2050 }
2051
2052 /**
2053  * Adds a newly created user to the appropriate blog
2054  *
2055  * To add a user in general, use add_user_to_blog(). This function
2056  * is specifically hooked into the {@see 'wpmu_activate_user'} action.
2057  *
2058  * @since MU
2059  * @see add_user_to_blog()
2060  *
2061  * @param int   $user_id
2062  * @param mixed $password Ignored.
2063  * @param array $meta
2064  */
2065 function add_new_user_to_blog( $user_id, $password, $meta ) {
2066         if ( !empty( $meta[ 'add_to_blog' ] ) ) {
2067                 $blog_id = $meta[ 'add_to_blog' ];
2068                 $role = $meta[ 'new_role' ];
2069                 remove_user_from_blog($user_id, get_network()->site_id); // remove user from main blog.
2070                 add_user_to_blog( $blog_id, $user_id, $role );
2071                 update_user_meta( $user_id, 'primary_blog', $blog_id );
2072         }
2073 }
2074
2075 /**
2076  * Correct From host on outgoing mail to match the site domain
2077  *
2078  * @since MU
2079  *
2080  * @param PHPMailer $phpmailer The PHPMailer instance, passed by reference.
2081  */
2082 function fix_phpmailer_messageid( $phpmailer ) {
2083         $phpmailer->Hostname = get_network()->domain;
2084 }
2085
2086 /**
2087  * Check to see whether a user is marked as a spammer, based on user login.
2088  *
2089  * @since MU
2090  *
2091  * @param string|WP_User $user Optional. Defaults to current user. WP_User object,
2092  *                                 or user login name as a string.
2093  * @return bool
2094  */
2095 function is_user_spammy( $user = null ) {
2096     if ( ! ( $user instanceof WP_User ) ) {
2097                 if ( $user ) {
2098                         $user = get_user_by( 'login', $user );
2099                 } else {
2100                         $user = wp_get_current_user();
2101                 }
2102         }
2103
2104         return $user && isset( $user->spam ) && 1 == $user->spam;
2105 }
2106
2107 /**
2108  * Update this blog's 'public' setting in the global blogs table.
2109  *
2110  * Public blogs have a setting of 1, private blogs are 0.
2111  *
2112  * @since MU
2113  *
2114  * @param int $old_value
2115  * @param int $value     The new public value
2116  */
2117 function update_blog_public( $old_value, $value ) {
2118         update_blog_status( get_current_blog_id(), 'public', (int) $value );
2119 }
2120
2121 /**
2122  * Check whether a usermeta key has to do with the current blog.
2123  *
2124  * @since MU
2125  *
2126  * @global wpdb $wpdb WordPress database abstraction object.
2127  *
2128  * @param string $key
2129  * @param int    $user_id Optional. Defaults to current user.
2130  * @param int    $blog_id Optional. Defaults to current blog.
2131  * @return bool
2132  */
2133 function is_user_option_local( $key, $user_id = 0, $blog_id = 0 ) {
2134         global $wpdb;
2135
2136         $current_user = wp_get_current_user();
2137         if ( $blog_id == 0 ) {
2138                 $blog_id = $wpdb->blogid;
2139         }
2140         $local_key = $wpdb->get_blog_prefix( $blog_id ) . $key;
2141
2142         return isset( $current_user->$local_key );
2143 }
2144
2145 /**
2146  * Check whether users can self-register, based on Network settings.
2147  *
2148  * @since MU
2149  *
2150  * @return bool
2151  */
2152 function users_can_register_signup_filter() {
2153         $registration = get_site_option('registration');
2154         return ( $registration == 'all' || $registration == 'user' );
2155 }
2156
2157 /**
2158  * Ensure that the welcome message is not empty. Currently unused.
2159  *
2160  * @since MU
2161  *
2162  * @param string $text
2163  * @return string
2164  */
2165 function welcome_user_msg_filter( $text ) {
2166         if ( !$text ) {
2167                 remove_filter( 'site_option_welcome_user_email', 'welcome_user_msg_filter' );
2168
2169                 /* translators: Do not translate USERNAME, PASSWORD, LOGINLINK, SITE_NAME: those are placeholders. */
2170                 $text = __( 'Howdy USERNAME,
2171
2172 Your new account is set up.
2173
2174 You can log in with the following information:
2175 Username: USERNAME
2176 Password: PASSWORD
2177 LOGINLINK
2178
2179 Thanks!
2180
2181 --The Team @ SITE_NAME' );
2182                 update_site_option( 'welcome_user_email', $text );
2183         }
2184         return $text;
2185 }
2186
2187 /**
2188  * Whether to force SSL on content.
2189  *
2190  * @since 2.8.5
2191  *
2192  * @staticvar bool $forced_content
2193  *
2194  * @param bool $force
2195  * @return bool True if forced, false if not forced.
2196  */
2197 function force_ssl_content( $force = '' ) {
2198         static $forced_content = false;
2199
2200         if ( '' != $force ) {
2201                 $old_forced = $forced_content;
2202                 $forced_content = $force;
2203                 return $old_forced;
2204         }
2205
2206         return $forced_content;
2207 }
2208
2209 /**
2210  * Formats a URL to use https.
2211  *
2212  * Useful as a filter.
2213  *
2214  * @since 2.8.5
2215  *
2216  * @param string $url URL
2217  * @return string URL with https as the scheme
2218  */
2219 function filter_SSL( $url ) {
2220         if ( ! is_string( $url ) )
2221                 return get_bloginfo( 'url' ); // Return home blog url with proper scheme
2222
2223         if ( force_ssl_content() && is_ssl() )
2224                 $url = set_url_scheme( $url, 'https' );
2225
2226         return $url;
2227 }
2228
2229 /**
2230  * Schedule update of the network-wide counts for the current network.
2231  *
2232  * @since 3.1.0
2233  */
2234 function wp_schedule_update_network_counts() {
2235         if ( !is_main_site() )
2236                 return;
2237
2238         if ( ! wp_next_scheduled('update_network_counts') && ! wp_installing() )
2239                 wp_schedule_event(time(), 'twicedaily', 'update_network_counts');
2240 }
2241
2242 /**
2243  *  Update the network-wide counts for the current network.
2244  *
2245  *  @since 3.1.0
2246  */
2247 function wp_update_network_counts() {
2248         wp_update_network_user_counts();
2249         wp_update_network_site_counts();
2250 }
2251
2252 /**
2253  * Update the count of sites for the current network.
2254  *
2255  * If enabled through the {@see 'enable_live_network_counts'} filter, update the sites count
2256  * on a network when a site is created or its status is updated.
2257  *
2258  * @since 3.7.0
2259  */
2260 function wp_maybe_update_network_site_counts() {
2261         $is_small_network = ! wp_is_large_network( 'sites' );
2262
2263         /**
2264          * Filters whether to update network site or user counts when a new site is created.
2265          *
2266          * @since 3.7.0
2267          *
2268          * @see wp_is_large_network()
2269          *
2270          * @param bool   $small_network Whether the network is considered small.
2271          * @param string $context       Context. Either 'users' or 'sites'.
2272          */
2273         if ( ! apply_filters( 'enable_live_network_counts', $is_small_network, 'sites' ) )
2274                 return;
2275
2276         wp_update_network_site_counts();
2277 }
2278
2279 /**
2280  * Update the network-wide users count.
2281  *
2282  * If enabled through the {@see 'enable_live_network_counts'} filter, update the users count
2283  * on a network when a user is created or its status is updated.
2284  *
2285  * @since 3.7.0
2286  */
2287 function wp_maybe_update_network_user_counts() {
2288         $is_small_network = ! wp_is_large_network( 'users' );
2289
2290         /** This filter is documented in wp-includes/ms-functions.php */
2291         if ( ! apply_filters( 'enable_live_network_counts', $is_small_network, 'users' ) )
2292                 return;
2293
2294         wp_update_network_user_counts();
2295 }
2296
2297 /**
2298  * Update the network-wide site count.
2299  *
2300  * @since 3.7.0
2301  *
2302  * @global wpdb $wpdb WordPress database abstraction object.
2303  */
2304 function wp_update_network_site_counts() {
2305         global $wpdb;
2306
2307         $count = get_sites( array(
2308                 'network_id' => $wpdb->siteid,
2309                 'spam'       => 0,
2310                 'deleted'    => 0,
2311                 'archived'   => 0,
2312                 'count'      => true,
2313         ) );
2314
2315         update_site_option( 'blog_count', $count );
2316 }
2317
2318 /**
2319  * Update the network-wide user count.
2320  *
2321  * @since 3.7.0
2322  *
2323  * @global wpdb $wpdb WordPress database abstraction object.
2324  */
2325 function wp_update_network_user_counts() {
2326         global $wpdb;
2327
2328         $count = $wpdb->get_var( "SELECT COUNT(ID) as c FROM $wpdb->users WHERE spam = '0' AND deleted = '0'" );
2329         update_site_option( 'user_count', $count );
2330 }
2331
2332 /**
2333  * Returns the space used by the current blog.
2334  *
2335  * @since 3.5.0
2336  *
2337  * @return int Used space in megabytes
2338  */
2339 function get_space_used() {
2340         /**
2341          * Filters the amount of storage space used by the current site.
2342          *
2343          * @since 3.5.0
2344          *
2345          * @param int|bool $space_used The amount of used space, in megabytes. Default false.
2346          */
2347         $space_used = apply_filters( 'pre_get_space_used', false );
2348         if ( false === $space_used ) {
2349                 $upload_dir = wp_upload_dir();
2350                 $space_used = get_dirsize( $upload_dir['basedir'] ) / MB_IN_BYTES;
2351         }
2352
2353         return $space_used;
2354 }
2355
2356 /**
2357  * Returns the upload quota for the current blog.
2358  *
2359  * @since MU
2360  *
2361  * @return int Quota in megabytes
2362  */
2363 function get_space_allowed() {
2364         $space_allowed = get_option( 'blog_upload_space' );
2365
2366         if ( ! is_numeric( $space_allowed ) )
2367                 $space_allowed = get_site_option( 'blog_upload_space' );
2368
2369         if ( ! is_numeric( $space_allowed ) )
2370                 $space_allowed = 100;
2371
2372         /**
2373          * Filters the upload quota for the current site.
2374          *
2375          * @since 3.7.0
2376          *
2377          * @param int $space_allowed Upload quota in megabytes for the current blog.
2378          */
2379         return apply_filters( 'get_space_allowed', $space_allowed );
2380 }
2381
2382 /**
2383  * Determines if there is any upload space left in the current blog's quota.
2384  *
2385  * @since 3.0.0
2386  *
2387  * @return int of upload space available in bytes
2388  */
2389 function get_upload_space_available() {
2390         $allowed = get_space_allowed();
2391         if ( $allowed < 0 ) {
2392                 $allowed = 0;
2393         }
2394         $space_allowed = $allowed * MB_IN_BYTES;
2395         if ( get_site_option( 'upload_space_check_disabled' ) )
2396                 return $space_allowed;
2397
2398         $space_used = get_space_used() * MB_IN_BYTES;
2399
2400         if ( ( $space_allowed - $space_used ) <= 0 )
2401                 return 0;
2402
2403         return $space_allowed - $space_used;
2404 }
2405
2406 /**
2407  * Determines if there is any upload space left in the current blog's quota.
2408  *
2409  * @since 3.0.0
2410  * @return bool True if space is available, false otherwise.
2411  */
2412 function is_upload_space_available() {
2413         if ( get_site_option( 'upload_space_check_disabled' ) )
2414                 return true;
2415
2416         return (bool) get_upload_space_available();
2417 }
2418
2419 /**
2420  * Filters the maximum upload file size allowed, in bytes.
2421  *
2422  * @since 3.0.0
2423  *
2424  * @param  int $size Upload size limit in bytes.
2425  * @return int       Upload size limit in bytes.
2426  */
2427 function upload_size_limit_filter( $size ) {
2428         $fileupload_maxk = KB_IN_BYTES * get_site_option( 'fileupload_maxk', 1500 );
2429         if ( get_site_option( 'upload_space_check_disabled' ) )
2430                 return min( $size, $fileupload_maxk );
2431
2432         return min( $size, $fileupload_maxk, get_upload_space_available() );
2433 }
2434
2435 /**
2436  * Whether or not we have a large network.
2437  *
2438  * The default criteria for a large network is either more than 10,000 users or more than 10,000 sites.
2439  * Plugins can alter this criteria using the {@see 'wp_is_large_network'} filter.
2440  *
2441  * @since 3.3.0
2442  * @param string $using 'sites or 'users'. Default is 'sites'.
2443  * @return bool True if the network meets the criteria for large. False otherwise.
2444  */
2445 function wp_is_large_network( $using = 'sites' ) {
2446         if ( 'users' == $using ) {
2447                 $count = get_user_count();
2448                 /**
2449                  * Filters whether the network is considered large.
2450                  *
2451                  * @since 3.3.0
2452                  *
2453                  * @param bool   $is_large_network Whether the network has more than 10000 users or sites.
2454                  * @param string $component        The component to count. Accepts 'users', or 'sites'.
2455                  * @param int    $count            The count of items for the component.
2456                  */
2457                 return apply_filters( 'wp_is_large_network', $count > 10000, 'users', $count );
2458         }
2459
2460         $count = get_blog_count();
2461         /** This filter is documented in wp-includes/ms-functions.php */
2462         return apply_filters( 'wp_is_large_network', $count > 10000, 'sites', $count );
2463 }
2464
2465 /**
2466  * Retrieves a list of reserved site on a sub-directory Multisite install.
2467  *
2468  * @since 4.4.0
2469  *
2470  * @return array $names Array of reserved subdirectory names.
2471  */
2472 function get_subdirectory_reserved_names() {
2473         $names = array(
2474                 'page', 'comments', 'blog', 'files', 'feed', 'wp-admin',
2475                 'wp-content', 'wp-includes', 'wp-json', 'embed'
2476         );
2477
2478         /**
2479          * Filters reserved site names on a sub-directory Multisite install.
2480          *
2481          * @since 3.0.0
2482          * @since 4.4.0 'wp-admin', 'wp-content', 'wp-includes', 'wp-json', and 'embed' were added
2483          *              to the reserved names list.
2484          *
2485          * @param array $subdirectory_reserved_names Array of reserved names.
2486          */
2487         return apply_filters( 'subdirectory_reserved_names', $names );
2488 }