]> scripts.mit.edu Git - autoinstalls/wordpress.git/blob - wp-includes/ms-functions.php
WordPress 4.6.1-scripts
[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_blog_details( $first_blog->userblog_id );
57                 } else {
58                         $primary = get_blog_details( $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_blog_details( $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                 $details = get_blog_details($blog_id);
165                 update_user_meta($user_id, 'source_domain', $details->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_site = get_current_site();
550         $base = $current_site->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_site->blog_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_site->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       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, $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       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, $user_email, $key, $meta ) ) {
788                 return false;
789         }
790
791         // Send email with activation link.
792         if ( !is_subdomain_install() || get_current_site()->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         $message = sprintf(
804                 /**
805                  * Filters the message content of the new blog notification email.
806                  *
807                  * Content should be formatted for transmission via wp_mail().
808                  *
809                  * @since MU
810                  *
811                  * @param string $content    Content of the notification email.
812                  * @param string $domain     Site domain.
813                  * @param string $path       Site path.
814                  * @param string $title      Site title.
815                  * @param string $user       User login name.
816                  * @param string $user_email User email address.
817                  * @param string $key        Activation key created in wpmu_signup_blog().
818                  * @param array  $meta       By default, contains the requested privacy setting and lang_id.
819                  */
820                 apply_filters( 'wpmu_signup_blog_notification_email',
821                         __( "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" ),
822                         $domain, $path, $title, $user, $user_email, $key, $meta
823                 ),
824                 $activate_url,
825                 esc_url( "http://{$domain}{$path}" ),
826                 $key
827         );
828         // TODO: Don't hard code activation link.
829         $subject = sprintf(
830                 /**
831                  * Filters the subject of the new blog notification email.
832                  *
833                  * @since MU
834                  *
835                  * @param string $subject    Subject of the notification email.
836                  * @param string $domain     Site domain.
837                  * @param string $path       Site path.
838                  * @param string $title      Site title.
839                  * @param string $user       User login name.
840                  * @param string $user_email User email address.
841                  * @param string $key        Activation key created in wpmu_signup_blog().
842                  * @param array  $meta       By default, contains the requested privacy setting and lang_id.
843                  */
844                 apply_filters( 'wpmu_signup_blog_notification_subject',
845                         __( '[%1$s] Activate %2$s' ),
846                         $domain, $path, $title, $user, $user_email, $key, $meta
847                 ),
848                 $from_name,
849                 esc_url( 'http://' . $domain . $path )
850         );
851         wp_mail( $user_email, wp_specialchars_decode( $subject ), $message, $message_headers );
852         return true;
853 }
854
855 /**
856  * Notify user of signup success.
857  *
858  * This is the notification function used when no new site has
859  * been requested.
860  *
861  * Filter {@see 'wpmu_signup_user_notification'} to bypass this function or
862  * replace it with your own notification behavior.
863  *
864  * Filter {@see 'wpmu_signup_user_notification_email'} and
865  * {@see 'wpmu_signup_user_notification_subject'} to change the content
866  * and subject line of the email sent to newly registered users.
867  *
868  * @since MU
869  *
870  * @param string $user       The user's login name.
871  * @param string $user_email The user's email address.
872  * @param string $key        The activation key created in wpmu_signup_user()
873  * @param array  $meta       By default, an empty array.
874  * @return bool
875  */
876 function wpmu_signup_user_notification( $user, $user_email, $key, $meta = array() ) {
877         /**
878          * Filters whether to bypass the email notification for new user sign-up.
879          *
880          * @since MU
881          *
882          * @param string $user       User login name.
883          * @param string $user_email User email address.
884          * @param string $key        Activation key created in wpmu_signup_user().
885          * @param array  $meta       Signup meta data.
886          */
887         if ( ! apply_filters( 'wpmu_signup_user_notification', $user, $user_email, $key, $meta ) )
888                 return false;
889
890         // Send email with activation link.
891         $admin_email = get_site_option( 'admin_email' );
892         if ( $admin_email == '' )
893                 $admin_email = 'support@' . $_SERVER['SERVER_NAME'];
894         $from_name = get_site_option( 'site_name' ) == '' ? 'WordPress' : esc_html( get_site_option( 'site_name' ) );
895         $message_headers = "From: \"{$from_name}\" <{$admin_email}>\n" . "Content-Type: text/plain; charset=\"" . get_option('blog_charset') . "\"\n";
896         $message = sprintf(
897                 /**
898                  * Filters the content of the notification email for new user sign-up.
899                  *
900                  * Content should be formatted for transmission via wp_mail().
901                  *
902                  * @since MU
903                  *
904                  * @param string $content    Content of the notification email.
905                  * @param string $user       User login name.
906                  * @param string $user_email User email address.
907                  * @param string $key        Activation key created in wpmu_signup_user().
908                  * @param array  $meta       Signup meta data.
909                  */
910                 apply_filters( 'wpmu_signup_user_notification_email',
911                         __( "To activate your user, please click the following link:\n\n%s\n\nAfter you activate, you will receive *another email* with your login." ),
912                         $user, $user_email, $key, $meta
913                 ),
914                 site_url( "wp-activate.php?key=$key" )
915         );
916         // TODO: Don't hard code activation link.
917         $subject = sprintf(
918                 /**
919                  * Filters the subject of the notification email of new user signup.
920                  *
921                  * @since MU
922                  *
923                  * @param string $subject    Subject of the notification email.
924                  * @param string $user       User login name.
925                  * @param string $user_email User email address.
926                  * @param string $key        Activation key created in wpmu_signup_user().
927                  * @param array  $meta       Signup meta data.
928                  */
929                 apply_filters( 'wpmu_signup_user_notification_subject',
930                         __( '[%1$s] Activate %2$s' ),
931                         $user, $user_email, $key, $meta
932                 ),
933                 $from_name,
934                 $user
935         );
936         wp_mail( $user_email, wp_specialchars_decode( $subject ), $message, $message_headers );
937         return true;
938 }
939
940 /**
941  * Activate a signup.
942  *
943  * Hook to {@see 'wpmu_activate_user'} or {@see 'wpmu_activate_blog'} for events
944  * that should happen only when users or sites are self-created (since
945  * those actions are not called when users and sites are created
946  * by a Super Admin).
947  *
948  * @since MU
949  *
950  * @global wpdb $wpdb WordPress database abstraction object.
951  *
952  * @param string $key The activation key provided to the user.
953  * @return array|WP_Error An array containing information about the activated user and/or blog
954  */
955 function wpmu_activate_signup($key) {
956         global $wpdb;
957
958         $signup = $wpdb->get_row( $wpdb->prepare("SELECT * FROM $wpdb->signups WHERE activation_key = %s", $key) );
959
960         if ( empty( $signup ) )
961                 return new WP_Error( 'invalid_key', __( 'Invalid activation key.' ) );
962
963         if ( $signup->active ) {
964                 if ( empty( $signup->domain ) )
965                         return new WP_Error( 'already_active', __( 'The user is already active.' ), $signup );
966                 else
967                         return new WP_Error( 'already_active', __( 'The site is already active.' ), $signup );
968         }
969
970         $meta = maybe_unserialize($signup->meta);
971         $password = wp_generate_password( 12, false );
972
973         $user_id = username_exists($signup->user_login);
974
975         if ( ! $user_id )
976                 $user_id = wpmu_create_user($signup->user_login, $password, $signup->user_email);
977         else
978                 $user_already_exists = true;
979
980         if ( ! $user_id )
981                 return new WP_Error('create_user', __('Could not create user'), $signup);
982
983         $now = current_time('mysql', true);
984
985         if ( empty($signup->domain) ) {
986                 $wpdb->update( $wpdb->signups, array('active' => 1, 'activated' => $now), array('activation_key' => $key) );
987
988                 if ( isset( $user_already_exists ) )
989                         return new WP_Error( 'user_already_exists', __( 'That username is already activated.' ), $signup);
990
991                 /**
992                  * Fires immediately after a new user is activated.
993                  *
994                  * @since MU
995                  *
996                  * @param int   $user_id  User ID.
997                  * @param int   $password User password.
998                  * @param array $meta     Signup meta data.
999                  */
1000                 do_action( 'wpmu_activate_user', $user_id, $password, $meta );
1001                 return array( 'user_id' => $user_id, 'password' => $password, 'meta' => $meta );
1002         }
1003
1004         $blog_id = wpmu_create_blog( $signup->domain, $signup->path, $signup->title, $user_id, $meta, $wpdb->siteid );
1005
1006         // TODO: What to do if we create a user but cannot create a blog?
1007         if ( is_wp_error($blog_id) ) {
1008                 // If blog is taken, that means a previous attempt to activate this blog failed in between creating the blog and
1009                 // setting the activation flag. Let's just set the active flag and instruct the user to reset their password.
1010                 if ( 'blog_taken' == $blog_id->get_error_code() ) {
1011                         $blog_id->add_data( $signup );
1012                         $wpdb->update( $wpdb->signups, array( 'active' => 1, 'activated' => $now ), array( 'activation_key' => $key ) );
1013                 }
1014                 return $blog_id;
1015         }
1016
1017         $wpdb->update( $wpdb->signups, array('active' => 1, 'activated' => $now), array('activation_key' => $key) );
1018         /**
1019          * Fires immediately after a site is activated.
1020          *
1021          * @since MU
1022          *
1023          * @param int    $blog_id       Blog ID.
1024          * @param int    $user_id       User ID.
1025          * @param int    $password      User password.
1026          * @param string $signup_title  Site title.
1027          * @param array  $meta          Signup meta data.
1028          */
1029         do_action( 'wpmu_activate_blog', $blog_id, $user_id, $password, $signup->title, $meta );
1030
1031         return array('blog_id' => $blog_id, 'user_id' => $user_id, 'password' => $password, 'title' => $signup->title, 'meta' => $meta);
1032 }
1033
1034 /**
1035  * Create a user.
1036  *
1037  * This function runs when a user self-registers as well as when
1038  * a Super Admin creates a new user. Hook to {@see 'wpmu_new_user'} for events
1039  * that should affect all new users, but only on Multisite (otherwise
1040  * use {@see'user_register'}).
1041  *
1042  * @since MU
1043  *
1044  * @param string $user_name The new user's login name.
1045  * @param string $password  The new user's password.
1046  * @param string $email     The new user's email address.
1047  * @return int|false Returns false on failure, or int $user_id on success
1048  */
1049 function wpmu_create_user( $user_name, $password, $email ) {
1050         $user_name = preg_replace( '/\s+/', '', sanitize_user( $user_name, true ) );
1051
1052         $user_id = wp_create_user( $user_name, $password, $email );
1053         if ( is_wp_error( $user_id ) )
1054                 return false;
1055
1056         // Newly created users have no roles or caps until they are added to a blog.
1057         delete_user_option( $user_id, 'capabilities' );
1058         delete_user_option( $user_id, 'user_level' );
1059
1060         /**
1061          * Fires immediately after a new user is created.
1062          *
1063          * @since MU
1064          *
1065          * @param int $user_id User ID.
1066          */
1067         do_action( 'wpmu_new_user', $user_id );
1068
1069         return $user_id;
1070 }
1071
1072 /**
1073  * Create a site.
1074  *
1075  * This function runs when a user self-registers a new site as well
1076  * as when a Super Admin creates a new site. Hook to {@see 'wpmu_new_blog'}
1077  * for events that should affect all new sites.
1078  *
1079  * On subdirectory installs, $domain is the same as the main site's
1080  * domain, and the path is the subdirectory name (eg 'example.com'
1081  * and '/blog1/'). On subdomain installs, $domain is the new subdomain +
1082  * root domain (eg 'blog1.example.com'), and $path is '/'.
1083  *
1084  * @since MU
1085  *
1086  * @param string $domain  The new site's domain.
1087  * @param string $path    The new site's path.
1088  * @param string $title   The new site's title.
1089  * @param int    $user_id The user ID of the new site's admin.
1090  * @param array  $meta    Optional. Used to set initial site options.
1091  * @param int    $site_id Optional. Only relevant on multi-network installs.
1092  * @return int|WP_Error Returns WP_Error object on failure, int $blog_id on success
1093  */
1094 function wpmu_create_blog( $domain, $path, $title, $user_id, $meta = array(), $site_id = 1 ) {
1095         $defaults = array( 'public' => 0 );
1096         $meta = wp_parse_args( $meta, $defaults );
1097
1098         $domain = preg_replace( '/\s+/', '', sanitize_user( $domain, true ) );
1099
1100         if ( is_subdomain_install() )
1101                 $domain = str_replace( '@', '', $domain );
1102
1103         $title = strip_tags( $title );
1104         $user_id = (int) $user_id;
1105
1106         if ( empty($path) )
1107                 $path = '/';
1108
1109         // Check if the domain has been used already. We should return an error message.
1110         if ( domain_exists($domain, $path, $site_id) )
1111                 return new WP_Error( 'blog_taken', __( 'Sorry, that site already exists!' ) );
1112
1113         if ( ! wp_installing() ) {
1114                 wp_installing( true );
1115         }
1116
1117         if ( ! $blog_id = insert_blog($domain, $path, $site_id) )
1118                 return new WP_Error('insert_blog', __('Could not create site.'));
1119
1120         switch_to_blog($blog_id);
1121         install_blog($blog_id, $title);
1122         wp_install_defaults($user_id);
1123
1124         add_user_to_blog($blog_id, $user_id, 'administrator');
1125
1126         foreach ( $meta as $key => $value ) {
1127                 if ( in_array( $key, array( 'public', 'archived', 'mature', 'spam', 'deleted', 'lang_id' ) ) )
1128                         update_blog_status( $blog_id, $key, $value );
1129                 else
1130                         update_option( $key, $value );
1131         }
1132
1133         add_option( 'WPLANG', get_site_option( 'WPLANG' ) );
1134         update_option( 'blog_public', (int) $meta['public'] );
1135
1136         if ( ! is_super_admin( $user_id ) && ! get_user_meta( $user_id, 'primary_blog', true ) )
1137                 update_user_meta( $user_id, 'primary_blog', $blog_id );
1138
1139         restore_current_blog();
1140         /**
1141          * Fires immediately after a new site is created.
1142          *
1143          * @since MU
1144          *
1145          * @param int    $blog_id Blog ID.
1146          * @param int    $user_id User ID.
1147          * @param string $domain  Site domain.
1148          * @param string $path    Site path.
1149          * @param int    $site_id Site ID. Only relevant on multi-network installs.
1150          * @param array  $meta    Meta data. Used to set initial site options.
1151          */
1152         do_action( 'wpmu_new_blog', $blog_id, $user_id, $domain, $path, $site_id, $meta );
1153
1154         wp_cache_set( 'last_changed', microtime(), 'sites' );
1155
1156         return $blog_id;
1157 }
1158
1159 /**
1160  * Notifies the network admin that a new site has been activated.
1161  *
1162  * Filter {@see 'newblog_notify_siteadmin'} to change the content of
1163  * the notification email.
1164  *
1165  * @since MU
1166  *
1167  * @param int    $blog_id    The new site's ID.
1168  * @param string $deprecated Not used.
1169  * @return bool
1170  */
1171 function newblog_notify_siteadmin( $blog_id, $deprecated = '' ) {
1172         if ( get_site_option( 'registrationnotification' ) != 'yes' )
1173                 return false;
1174
1175         $email = get_site_option( 'admin_email' );
1176         if ( is_email($email) == false )
1177                 return false;
1178
1179         $options_site_url = esc_url(network_admin_url('settings.php'));
1180
1181         switch_to_blog( $blog_id );
1182         $blogname = get_option( 'blogname' );
1183         $siteurl = site_url();
1184         restore_current_blog();
1185
1186         $msg = sprintf( __( 'New Site: %1$s
1187 URL: %2$s
1188 Remote IP: %3$s
1189
1190 Disable these notifications: %4$s' ), $blogname, $siteurl, wp_unslash( $_SERVER['REMOTE_ADDR'] ), $options_site_url);
1191         /**
1192          * Filters the message body of the new site activation email sent
1193          * to the network administrator.
1194          *
1195          * @since MU
1196          *
1197          * @param string $msg Email body.
1198          */
1199         $msg = apply_filters( 'newblog_notify_siteadmin', $msg );
1200
1201         wp_mail( $email, sprintf( __( 'New Site Registration: %s' ), $siteurl ), $msg );
1202         return true;
1203 }
1204
1205 /**
1206  * Notifies the network admin that a new user has been activated.
1207  *
1208  * Filter {@see 'newuser_notify_siteadmin'} to change the content of
1209  * the notification email.
1210  *
1211  * @since MU
1212  *
1213  * @param int $user_id The new user's ID.
1214  * @return bool
1215  */
1216 function newuser_notify_siteadmin( $user_id ) {
1217         if ( get_site_option( 'registrationnotification' ) != 'yes' )
1218                 return false;
1219
1220         $email = get_site_option( 'admin_email' );
1221
1222         if ( is_email($email) == false )
1223                 return false;
1224
1225         $user = get_userdata( $user_id );
1226
1227         $options_site_url = esc_url(network_admin_url('settings.php'));
1228         $msg = sprintf(__('New User: %1$s
1229 Remote IP: %2$s
1230
1231 Disable these notifications: %3$s'), $user->user_login, wp_unslash( $_SERVER['REMOTE_ADDR'] ), $options_site_url);
1232
1233         /**
1234          * Filters the message body of the new user activation email sent
1235          * to the network administrator.
1236          *
1237          * @since MU
1238          *
1239          * @param string  $msg  Email body.
1240          * @param WP_User $user WP_User instance of the new user.
1241          */
1242         $msg = apply_filters( 'newuser_notify_siteadmin', $msg, $user );
1243         wp_mail( $email, sprintf(__('New User Registration: %s'), $user->user_login), $msg );
1244         return true;
1245 }
1246
1247 /**
1248  * Check whether a blogname is already taken.
1249  *
1250  * Used during the new site registration process to ensure
1251  * that each blogname is unique.
1252  *
1253  * @since MU
1254  *
1255  * @global wpdb $wpdb WordPress database abstraction object.
1256  *
1257  * @param string $domain  The domain to be checked.
1258  * @param string $path    The path to be checked.
1259  * @param int    $site_id Optional. Relevant only on multi-network installs.
1260  * @return int
1261  */
1262 function domain_exists($domain, $path, $site_id = 1) {
1263         $path = trailingslashit( $path );
1264         $args = array(
1265                 'network_id' => $site_id,
1266                 'domain' => $domain,
1267                 'path' => $path,
1268                 'fields' => 'ids',
1269         );
1270         $result = get_sites( $args );
1271         $result = array_shift( $result );
1272
1273         /**
1274          * Filters whether a blogname is taken.
1275          *
1276          * @since 3.5.0
1277          *
1278          * @param int|null $result  The blog_id if the blogname exists, null otherwise.
1279          * @param string   $domain  Domain to be checked.
1280          * @param string   $path    Path to be checked.
1281          * @param int      $site_id Site ID. Relevant only on multi-network installs.
1282          */
1283         return apply_filters( 'domain_exists', $result, $domain, $path, $site_id );
1284 }
1285
1286 /**
1287  * Store basic site info in the blogs table.
1288  *
1289  * This function creates a row in the wp_blogs table and returns
1290  * the new blog's ID. It is the first step in creating a new blog.
1291  *
1292  * @since MU
1293  *
1294  * @global wpdb $wpdb WordPress database abstraction object.
1295  *
1296  * @param string $domain  The domain of the new site.
1297  * @param string $path    The path of the new site.
1298  * @param int    $site_id Unless you're running a multi-network install, be sure to set this value to 1.
1299  * @return int|false The ID of the new row
1300  */
1301 function insert_blog($domain, $path, $site_id) {
1302         global $wpdb;
1303
1304         $path = trailingslashit($path);
1305         $site_id = (int) $site_id;
1306
1307         $result = $wpdb->insert( $wpdb->blogs, array('site_id' => $site_id, 'domain' => $domain, 'path' => $path, 'registered' => current_time('mysql')) );
1308         if ( ! $result )
1309                 return false;
1310
1311         $blog_id = $wpdb->insert_id;
1312         refresh_blog_details( $blog_id );
1313
1314         wp_maybe_update_network_site_counts();
1315
1316         return $blog_id;
1317 }
1318
1319 /**
1320  * Install an empty blog.
1321  *
1322  * Creates the new blog tables and options. If calling this function
1323  * directly, be sure to use switch_to_blog() first, so that $wpdb
1324  * points to the new blog.
1325  *
1326  * @since MU
1327  *
1328  * @global wpdb     $wpdb
1329  * @global WP_Roles $wp_roles
1330  *
1331  * @param int    $blog_id    The value returned by insert_blog().
1332  * @param string $blog_title The title of the new site.
1333  */
1334 function install_blog( $blog_id, $blog_title = '' ) {
1335         global $wpdb, $wp_roles, $current_site;
1336
1337         // Cast for security
1338         $blog_id = (int) $blog_id;
1339
1340         require_once( ABSPATH . 'wp-admin/includes/upgrade.php' );
1341
1342         $suppress = $wpdb->suppress_errors();
1343         if ( $wpdb->get_results( "DESCRIBE {$wpdb->posts}" ) )
1344                 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>' );
1345         $wpdb->suppress_errors( $suppress );
1346
1347         $url = get_blogaddress_by_id( $blog_id );
1348
1349         // Set everything up
1350         make_db_current_silent( 'blog' );
1351         populate_options();
1352         populate_roles();
1353
1354         // populate_roles() clears previous role definitions so we start over.
1355         $wp_roles = new WP_Roles();
1356
1357         $siteurl = $home = untrailingslashit( $url );
1358
1359         if ( ! is_subdomain_install() ) {
1360
1361                 if ( 'https' === parse_url( get_site_option( 'siteurl' ), PHP_URL_SCHEME ) ) {
1362                         $siteurl = set_url_scheme( $siteurl, 'https' );
1363                 }
1364                 if ( 'https' === parse_url( get_home_url( $current_site->blog_id ), PHP_URL_SCHEME ) ) {
1365                         $home = set_url_scheme( $home, 'https' );
1366                 }
1367
1368         }
1369
1370         update_option( 'siteurl', $siteurl );
1371         update_option( 'home', $home );
1372
1373         if ( get_site_option( 'ms_files_rewriting' ) )
1374                 update_option( 'upload_path', UPLOADBLOGSDIR . "/$blog_id/files" );
1375         else
1376                 update_option( 'upload_path', get_blog_option( get_current_site()->blog_id, 'upload_path' ) );
1377
1378         update_option( 'blogname', wp_unslash( $blog_title ) );
1379         update_option( 'admin_email', '' );
1380
1381         // remove all perms
1382         $table_prefix = $wpdb->get_blog_prefix();
1383         delete_metadata( 'user', 0, $table_prefix . 'user_level',   null, true ); // delete all
1384         delete_metadata( 'user', 0, $table_prefix . 'capabilities', null, true ); // delete all
1385 }
1386
1387 /**
1388  * Set blog defaults.
1389  *
1390  * This function creates a row in the wp_blogs table.
1391  *
1392  * @since MU
1393  * @deprecated MU
1394  * @deprecated Use wp_install_defaults()
1395  *
1396  * @global wpdb $wpdb WordPress database abstraction object.
1397  *
1398  * @param int $blog_id Ignored in this function.
1399  * @param int $user_id
1400  */
1401 function install_blog_defaults($blog_id, $user_id) {
1402         global $wpdb;
1403
1404         require_once( ABSPATH . 'wp-admin/includes/upgrade.php' );
1405
1406         $suppress = $wpdb->suppress_errors();
1407
1408         wp_install_defaults($user_id);
1409
1410         $wpdb->suppress_errors( $suppress );
1411 }
1412
1413 /**
1414  * Notify a user that their blog activation has been successful.
1415  *
1416  * Filter {@see 'wpmu_welcome_notification'} to disable or bypass.
1417  *
1418  * Filter {@see 'update_welcome_email'} and {@see 'update_welcome_subject'} to
1419  * modify the content and subject line of the notification email.
1420  *
1421  * @since MU
1422  *
1423  * @param int    $blog_id
1424  * @param int    $user_id
1425  * @param string $password
1426  * @param string $title    The new blog's title
1427  * @param array  $meta     Optional. Not used in the default function, but is passed along to hooks for customization.
1428  * @return bool
1429  */
1430 function wpmu_welcome_notification( $blog_id, $user_id, $password, $title, $meta = array() ) {
1431         $current_site = get_current_site();
1432
1433         /**
1434          * Filters whether to bypass the welcome email after site activation.
1435          *
1436          * Returning false disables the welcome email.
1437          *
1438          * @since MU
1439          *
1440          * @param int|bool $blog_id  Blog ID.
1441          * @param int      $user_id  User ID.
1442          * @param string   $password User password.
1443          * @param string   $title    Site title.
1444          * @param array    $meta     Signup meta data.
1445          */
1446         if ( ! apply_filters( 'wpmu_welcome_notification', $blog_id, $user_id, $password, $title, $meta ) )
1447                 return false;
1448
1449         $welcome_email = get_site_option( 'welcome_email' );
1450         if ( $welcome_email == false ) {
1451                 /* translators: Do not translate USERNAME, SITE_NAME, BLOG_URL, PASSWORD: those are placeholders. */
1452                 $welcome_email = __( 'Howdy USERNAME,
1453
1454 Your new SITE_NAME site has been successfully set up at:
1455 BLOG_URL
1456
1457 You can log in to the administrator account with the following information:
1458
1459 Username: USERNAME
1460 Password: PASSWORD
1461 Log in here: BLOG_URLwp-login.php
1462
1463 We hope you enjoy your new site. Thanks!
1464
1465 --The Team @ SITE_NAME' );
1466         }
1467
1468         $url = get_blogaddress_by_id($blog_id);
1469         $user = get_userdata( $user_id );
1470
1471         $welcome_email = str_replace( 'SITE_NAME', $current_site->site_name, $welcome_email );
1472         $welcome_email = str_replace( 'BLOG_TITLE', $title, $welcome_email );
1473         $welcome_email = str_replace( 'BLOG_URL', $url, $welcome_email );
1474         $welcome_email = str_replace( 'USERNAME', $user->user_login, $welcome_email );
1475         $welcome_email = str_replace( 'PASSWORD', $password, $welcome_email );
1476
1477         /**
1478          * Filters the content of the welcome email after site activation.
1479          *
1480          * Content should be formatted for transmission via wp_mail().
1481          *
1482          * @since MU
1483          *
1484          * @param string $welcome_email Message body of the email.
1485          * @param int    $blog_id       Blog ID.
1486          * @param int    $user_id       User ID.
1487          * @param string $password      User password.
1488          * @param string $title         Site title.
1489          * @param array  $meta          Signup meta data.
1490          */
1491         $welcome_email = apply_filters( 'update_welcome_email', $welcome_email, $blog_id, $user_id, $password, $title, $meta );
1492         $admin_email = get_site_option( 'admin_email' );
1493
1494         if ( $admin_email == '' )
1495                 $admin_email = 'support@' . $_SERVER['SERVER_NAME'];
1496
1497         $from_name = get_site_option( 'site_name' ) == '' ? 'WordPress' : esc_html( get_site_option( 'site_name' ) );
1498         $message_headers = "From: \"{$from_name}\" <{$admin_email}>\n" . "Content-Type: text/plain; charset=\"" . get_option('blog_charset') . "\"\n";
1499         $message = $welcome_email;
1500
1501         if ( empty( $current_site->site_name ) )
1502                 $current_site->site_name = 'WordPress';
1503
1504         /**
1505          * Filters the subject of the welcome email after site activation.
1506          *
1507          * @since MU
1508          *
1509          * @param string $subject Subject of the email.
1510          */
1511         $subject = apply_filters( 'update_welcome_subject', sprintf( __( 'New %1$s Site: %2$s' ), $current_site->site_name, wp_unslash( $title ) ) );
1512         wp_mail( $user->user_email, wp_specialchars_decode( $subject ), $message, $message_headers );
1513         return true;
1514 }
1515
1516 /**
1517  * Notify a user that their account activation has been successful.
1518  *
1519  * Filter {@see 'wpmu_welcome_user_notification'} to disable or bypass.
1520  *
1521  * Filter {@see 'update_welcome_user_email'} and {@see 'update_welcome_user_subject'} to
1522  * modify the content and subject line of the notification email.
1523  *
1524  * @since MU
1525  *
1526  * @param int    $user_id
1527  * @param string $password
1528  * @param array  $meta     Optional. Not used in the default function, but is passed along to hooks for customization.
1529  * @return bool
1530  */
1531 function wpmu_welcome_user_notification( $user_id, $password, $meta = array() ) {
1532         $current_site = get_current_site();
1533
1534         /**
1535          * Filters whether to bypass the welcome email after user activation.
1536          *
1537          * Returning false disables the welcome email.
1538          *
1539          * @since MU
1540          *
1541          * @param int    $user_id  User ID.
1542          * @param string $password User password.
1543          * @param array  $meta     Signup meta data.
1544          */
1545         if ( ! apply_filters( 'wpmu_welcome_user_notification', $user_id, $password, $meta ) )
1546                 return false;
1547
1548         $welcome_email = get_site_option( 'welcome_user_email' );
1549
1550         $user = get_userdata( $user_id );
1551
1552         /**
1553          * Filters the content of the welcome email after user activation.
1554          *
1555          * Content should be formatted for transmission via wp_mail().
1556          *
1557          * @since MU
1558          *
1559          * @param string $welcome_email The message body of the account activation success email.
1560          * @param int    $user_id       User ID.
1561          * @param string $password      User password.
1562          * @param array  $meta          Signup meta data.
1563          */
1564         $welcome_email = apply_filters( 'update_welcome_user_email', $welcome_email, $user_id, $password, $meta );
1565         $welcome_email = str_replace( 'SITE_NAME', $current_site->site_name, $welcome_email );
1566         $welcome_email = str_replace( 'USERNAME', $user->user_login, $welcome_email );
1567         $welcome_email = str_replace( 'PASSWORD', $password, $welcome_email );
1568         $welcome_email = str_replace( 'LOGINLINK', wp_login_url(), $welcome_email );
1569
1570         $admin_email = get_site_option( 'admin_email' );
1571
1572         if ( $admin_email == '' )
1573                 $admin_email = 'support@' . $_SERVER['SERVER_NAME'];
1574
1575         $from_name = get_site_option( 'site_name' ) == '' ? 'WordPress' : esc_html( get_site_option( 'site_name' ) );
1576         $message_headers = "From: \"{$from_name}\" <{$admin_email}>\n" . "Content-Type: text/plain; charset=\"" . get_option('blog_charset') . "\"\n";
1577         $message = $welcome_email;
1578
1579         if ( empty( $current_site->site_name ) )
1580                 $current_site->site_name = 'WordPress';
1581
1582         /**
1583          * Filters the subject of the welcome email after user activation.
1584          *
1585          * @since MU
1586          *
1587          * @param string $subject Subject of the email.
1588          */
1589         $subject = apply_filters( 'update_welcome_user_subject', sprintf( __( 'New %1$s User: %2$s' ), $current_site->site_name, $user->user_login) );
1590         wp_mail( $user->user_email, wp_specialchars_decode( $subject ), $message, $message_headers );
1591         return true;
1592 }
1593
1594 /**
1595  * Get the current network.
1596  *
1597  * Returns an object containing the 'id', 'domain', 'path', and 'site_name'
1598  * properties of the network being viewed.
1599  *
1600  * @see wpmu_current_site()
1601  *
1602  * @since MU
1603  *
1604  * @global WP_Network $current_site
1605  *
1606  * @return WP_Network
1607  */
1608 function get_current_site() {
1609         global $current_site;
1610         return $current_site;
1611 }
1612
1613 /**
1614  * Get a user's most recent post.
1615  *
1616  * Walks through each of a user's blogs to find the post with
1617  * the most recent post_date_gmt.
1618  *
1619  * @since MU
1620  *
1621  * @global wpdb $wpdb WordPress database abstraction object.
1622  *
1623  * @param int $user_id
1624  * @return array Contains the blog_id, post_id, post_date_gmt, and post_gmt_ts
1625  */
1626 function get_most_recent_post_of_user( $user_id ) {
1627         global $wpdb;
1628
1629         $user_blogs = get_blogs_of_user( (int) $user_id );
1630         $most_recent_post = array();
1631
1632         // Walk through each blog and get the most recent post
1633         // published by $user_id
1634         foreach ( (array) $user_blogs as $blog ) {
1635                 $prefix = $wpdb->get_blog_prefix( $blog->userblog_id );
1636                 $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);
1637
1638                 // Make sure we found a post
1639                 if ( isset($recent_post['ID']) ) {
1640                         $post_gmt_ts = strtotime($recent_post['post_date_gmt']);
1641
1642                         // If this is the first post checked or if this post is
1643                         // newer than the current recent post, make it the new
1644                         // most recent post.
1645                         if ( !isset($most_recent_post['post_gmt_ts']) || ( $post_gmt_ts > $most_recent_post['post_gmt_ts'] ) ) {
1646                                 $most_recent_post = array(
1647                                         'blog_id'               => $blog->userblog_id,
1648                                         'post_id'               => $recent_post['ID'],
1649                                         'post_date_gmt' => $recent_post['post_date_gmt'],
1650                                         'post_gmt_ts'   => $post_gmt_ts
1651                                 );
1652                         }
1653                 }
1654         }
1655
1656         return $most_recent_post;
1657 }
1658
1659 // Misc functions
1660
1661 /**
1662  * Get the size of a directory.
1663  *
1664  * A helper function that is used primarily to check whether
1665  * a blog has exceeded its allowed upload space.
1666  *
1667  * @since MU
1668  *
1669  * @param string $directory Full path of a directory.
1670  * @return int Size of the directory in MB.
1671  */
1672 function get_dirsize( $directory ) {
1673         $dirsize = get_transient( 'dirsize_cache' );
1674         if ( is_array( $dirsize ) && isset( $dirsize[ $directory ][ 'size' ] ) )
1675                 return $dirsize[ $directory ][ 'size' ];
1676
1677         if ( ! is_array( $dirsize ) )
1678                 $dirsize = array();
1679
1680         // Exclude individual site directories from the total when checking the main site,
1681         // as they are subdirectories and should not be counted.
1682         if ( is_main_site() ) {
1683                 $dirsize[ $directory ][ 'size' ] = recurse_dirsize( $directory, $directory . '/sites' );
1684         } else {
1685                 $dirsize[ $directory ][ 'size' ] = recurse_dirsize( $directory );
1686         }
1687
1688         set_transient( 'dirsize_cache', $dirsize, HOUR_IN_SECONDS );
1689         return $dirsize[ $directory ][ 'size' ];
1690 }
1691
1692 /**
1693  * Get the size of a directory recursively.
1694  *
1695  * Used by get_dirsize() to get a directory's size when it contains
1696  * other directories.
1697  *
1698  * @since MU
1699  * @since 4.3.0 $exclude parameter added.
1700  *
1701  * @param string $directory Full path of a directory.
1702  * @param string $exclude   Optional. Full path of a subdirectory to exclude from the total.
1703  * @return int|false Size in MB if a valid directory. False if not.
1704  */
1705 function recurse_dirsize( $directory, $exclude = null ) {
1706         $size = 0;
1707
1708         $directory = untrailingslashit( $directory );
1709
1710         if ( ! file_exists( $directory ) || ! is_dir( $directory ) || ! is_readable( $directory ) || $directory === $exclude ) {
1711                 return false;
1712         }
1713
1714         if ($handle = opendir($directory)) {
1715                 while(($file = readdir($handle)) !== false) {
1716                         $path = $directory.'/'.$file;
1717                         if ($file != '.' && $file != '..') {
1718                                 if (is_file($path)) {
1719                                         $size += filesize($path);
1720                                 } elseif (is_dir($path)) {
1721                                         $handlesize = recurse_dirsize( $path, $exclude );
1722                                         if ($handlesize > 0)
1723                                                 $size += $handlesize;
1724                                 }
1725                         }
1726                 }
1727                 closedir($handle);
1728         }
1729         return $size;
1730 }
1731
1732 /**
1733  * Check an array of MIME types against a whitelist.
1734  *
1735  * WordPress ships with a set of allowed upload filetypes,
1736  * which is defined in wp-includes/functions.php in
1737  * get_allowed_mime_types(). This function is used to filter
1738  * that list against the filetype whitelist provided by Multisite
1739  * Super Admins at wp-admin/network/settings.php.
1740  *
1741  * @since MU
1742  *
1743  * @param array $mimes
1744  * @return array
1745  */
1746 function check_upload_mimes( $mimes ) {
1747         $site_exts = explode( ' ', get_site_option( 'upload_filetypes', 'jpg jpeg png gif' ) );
1748         $site_mimes = array();
1749         foreach ( $site_exts as $ext ) {
1750                 foreach ( $mimes as $ext_pattern => $mime ) {
1751                         if ( $ext != '' && strpos( $ext_pattern, $ext ) !== false )
1752                                 $site_mimes[$ext_pattern] = $mime;
1753                 }
1754         }
1755         return $site_mimes;
1756 }
1757
1758 /**
1759  * Update a blog's post count.
1760  *
1761  * WordPress MS stores a blog's post count as an option so as
1762  * to avoid extraneous COUNTs when a blog's details are fetched
1763  * with get_blog_details(). This function is called when posts
1764  * are published or unpublished to make sure the count stays current.
1765  *
1766  * @since MU
1767  *
1768  * @global wpdb $wpdb WordPress database abstraction object.
1769  *
1770  * @param string $deprecated Not used.
1771  */
1772 function update_posts_count( $deprecated = '' ) {
1773         global $wpdb;
1774         update_option( 'post_count', (int) $wpdb->get_var( "SELECT COUNT(ID) FROM {$wpdb->posts} WHERE post_status = 'publish' and post_type = 'post'" ) );
1775 }
1776
1777 /**
1778  * Logs user registrations.
1779  *
1780  * @since MU
1781  *
1782  * @global wpdb $wpdb WordPress database abstraction object.
1783  *
1784  * @param int $blog_id
1785  * @param int $user_id
1786  */
1787 function wpmu_log_new_registrations( $blog_id, $user_id ) {
1788         global $wpdb;
1789         $user = get_userdata( (int) $user_id );
1790         if ( $user )
1791                 $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')) );
1792 }
1793
1794 /**
1795  * Maintains a canonical list of terms by syncing terms created for each blog with the global terms table.
1796  *
1797  * @since 3.0.0
1798  *
1799  * @see term_id_filter
1800  *
1801  * @global wpdb $wpdb WordPress database abstraction object.
1802  * @staticvar int $global_terms_recurse
1803  *
1804  * @param int    $term_id    An ID for a term on the current blog.
1805  * @param string $deprecated Not used.
1806  * @return int An ID from the global terms table mapped from $term_id.
1807  */
1808 function global_terms( $term_id, $deprecated = '' ) {
1809         global $wpdb;
1810         static $global_terms_recurse = null;
1811
1812         if ( !global_terms_enabled() )
1813                 return $term_id;
1814
1815         // prevent a race condition
1816         $recurse_start = false;
1817         if ( $global_terms_recurse === null ) {
1818                 $recurse_start = true;
1819                 $global_terms_recurse = 1;
1820         } elseif ( 10 < $global_terms_recurse++ ) {
1821                 return $term_id;
1822         }
1823
1824         $term_id = intval( $term_id );
1825         $c = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM $wpdb->terms WHERE term_id = %d", $term_id ) );
1826
1827         $global_id = $wpdb->get_var( $wpdb->prepare( "SELECT cat_ID FROM $wpdb->sitecategories WHERE category_nicename = %s", $c->slug ) );
1828         if ( $global_id == null ) {
1829                 $used_global_id = $wpdb->get_var( $wpdb->prepare( "SELECT cat_ID FROM $wpdb->sitecategories WHERE cat_ID = %d", $c->term_id ) );
1830                 if ( null == $used_global_id ) {
1831                         $wpdb->insert( $wpdb->sitecategories, array( 'cat_ID' => $term_id, 'cat_name' => $c->name, 'category_nicename' => $c->slug ) );
1832                         $global_id = $wpdb->insert_id;
1833                         if ( empty( $global_id ) )
1834                                 return $term_id;
1835                 } else {
1836                         $max_global_id = $wpdb->get_var( "SELECT MAX(cat_ID) FROM $wpdb->sitecategories" );
1837                         $max_local_id = $wpdb->get_var( "SELECT MAX(term_id) FROM $wpdb->terms" );
1838                         $new_global_id = max( $max_global_id, $max_local_id ) + mt_rand( 100, 400 );
1839                         $wpdb->insert( $wpdb->sitecategories, array( 'cat_ID' => $new_global_id, 'cat_name' => $c->name, 'category_nicename' => $c->slug ) );
1840                         $global_id = $wpdb->insert_id;
1841                 }
1842         } elseif ( $global_id != $term_id ) {
1843                 $local_id = $wpdb->get_var( $wpdb->prepare( "SELECT term_id FROM $wpdb->terms WHERE term_id = %d", $global_id ) );
1844                 if ( null != $local_id ) {
1845                         global_terms( $local_id );
1846                         if ( 10 < $global_terms_recurse ) {
1847                                 $global_id = $term_id;
1848                         }
1849                 }
1850         }
1851
1852         if ( $global_id != $term_id ) {
1853                 if ( get_option( 'default_category' ) == $term_id )
1854                         update_option( 'default_category', $global_id );
1855
1856                 $wpdb->update( $wpdb->terms, array('term_id' => $global_id), array('term_id' => $term_id) );
1857                 $wpdb->update( $wpdb->term_taxonomy, array('term_id' => $global_id), array('term_id' => $term_id) );
1858                 $wpdb->update( $wpdb->term_taxonomy, array('parent' => $global_id), array('parent' => $term_id) );
1859
1860                 clean_term_cache($term_id);
1861         }
1862         if ( $recurse_start )
1863                 $global_terms_recurse = null;
1864
1865         return $global_id;
1866 }
1867
1868 /**
1869  * Ensure that the current site's domain is listed in the allowed redirect host list.
1870  *
1871  * @see wp_validate_redirect()
1872  * @since MU
1873  *
1874  * @param array|string $deprecated Not used.
1875  * @return array The current site's domain
1876  */
1877 function redirect_this_site( $deprecated = '' ) {
1878         return array( get_current_site()->domain );
1879 }
1880
1881 /**
1882  * Check whether an upload is too big.
1883  *
1884  * @since MU
1885  *
1886  * @blessed
1887  *
1888  * @param array $upload
1889  * @return string|array If the upload is under the size limit, $upload is returned. Otherwise returns an error message.
1890  */
1891 function upload_is_file_too_big( $upload ) {
1892         if ( ! is_array( $upload ) || defined( 'WP_IMPORTING' ) || get_site_option( 'upload_space_check_disabled' ) )
1893                 return $upload;
1894
1895         if ( strlen( $upload['bits'] )  > ( KB_IN_BYTES * get_site_option( 'fileupload_maxk', 1500 ) ) ) {
1896                 return sprintf( __( 'This file is too big. Files must be less than %d KB in size.' ) . '<br />', get_site_option( 'fileupload_maxk', 1500 ) );
1897         }
1898
1899         return $upload;
1900 }
1901
1902 /**
1903  * Add a nonce field to the signup page.
1904  *
1905  * @since MU
1906  */
1907 function signup_nonce_fields() {
1908         $id = mt_rand();
1909         echo "<input type='hidden' name='signup_form_id' value='{$id}' />";
1910         wp_nonce_field('signup_form_' . $id, '_signup_form', false);
1911 }
1912
1913 /**
1914  * Process the signup nonce created in signup_nonce_fields().
1915  *
1916  * @since MU
1917  *
1918  * @param array $result
1919  * @return array
1920  */
1921 function signup_nonce_check( $result ) {
1922         if ( !strpos( $_SERVER[ 'PHP_SELF' ], 'wp-signup.php' ) )
1923                 return $result;
1924
1925         if ( wp_create_nonce('signup_form_' . $_POST[ 'signup_form_id' ]) != $_POST['_signup_form'] )
1926                 wp_die( __( 'Please try again.' ) );
1927
1928         return $result;
1929 }
1930
1931 /**
1932  * Correct 404 redirects when NOBLOGREDIRECT is defined.
1933  *
1934  * @since MU
1935  */
1936 function maybe_redirect_404() {
1937         /**
1938          * Filters the redirect URL for 404s on the main site.
1939          *
1940          * The filter is only evaluated if the NOBLOGREDIRECT constant is defined.
1941          *
1942          * @since 3.0.0
1943          *
1944          * @param string $no_blog_redirect The redirect URL defined in NOBLOGREDIRECT.
1945          */
1946         if ( is_main_site() && is_404() && defined( 'NOBLOGREDIRECT' ) && ( $destination = apply_filters( 'blog_redirect_404', NOBLOGREDIRECT ) ) ) {
1947                 if ( $destination == '%siteurl%' )
1948                         $destination = network_home_url();
1949                 wp_redirect( $destination );
1950                 exit();
1951         }
1952 }
1953
1954 /**
1955  * Add a new user to a blog by visiting /newbloguser/username/.
1956  *
1957  * This will only work when the user's details are saved as an option
1958  * keyed as 'new_user_x', where 'x' is the username of the user to be
1959  * added, as when a user is invited through the regular WP Add User interface.
1960  *
1961  * @since MU
1962  */
1963 function maybe_add_existing_user_to_blog() {
1964         if ( false === strpos( $_SERVER[ 'REQUEST_URI' ], '/newbloguser/' ) )
1965                 return;
1966
1967         $parts = explode( '/', $_SERVER[ 'REQUEST_URI' ] );
1968         $key = array_pop( $parts );
1969
1970         if ( $key == '' )
1971                 $key = array_pop( $parts );
1972
1973         $details = get_option( 'new_user_' . $key );
1974         if ( !empty( $details ) )
1975                 delete_option( 'new_user_' . $key );
1976
1977         if ( empty( $details ) || is_wp_error( add_existing_user_to_blog( $details ) ) )
1978                 wp_die( sprintf(__('An error occurred adding you to this site. Back to the <a href="%s">homepage</a>.'), home_url() ) );
1979
1980         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 ) );
1981 }
1982
1983 /**
1984  * Add a user to a blog based on details from maybe_add_existing_user_to_blog().
1985  *
1986  * @since MU
1987  *
1988  * @global int $blog_id
1989  *
1990  * @param array $details
1991  * @return true|WP_Error|void
1992  */
1993 function add_existing_user_to_blog( $details = false ) {
1994         global $blog_id;
1995
1996         if ( is_array( $details ) ) {
1997                 $result = add_user_to_blog( $blog_id, $details[ 'user_id' ], $details[ 'role' ] );
1998                 /**
1999                  * Fires immediately after an existing user is added to a site.
2000                  *
2001                  * @since MU
2002                  *
2003                  * @param int   $user_id User ID.
2004                  * @param mixed $result  True on success or a WP_Error object if the user doesn't exist.
2005                  */
2006                 do_action( 'added_existing_user', $details['user_id'], $result );
2007                 return $result;
2008         }
2009 }
2010
2011 /**
2012  * Adds a newly created user to the appropriate blog
2013  *
2014  * To add a user in general, use add_user_to_blog(). This function
2015  * is specifically hooked into the {@see 'wpmu_activate_user'} action.
2016  *
2017  * @since MU
2018  * @see add_user_to_blog()
2019  *
2020  * @param int   $user_id
2021  * @param mixed $password Ignored.
2022  * @param array $meta
2023  */
2024 function add_new_user_to_blog( $user_id, $password, $meta ) {
2025         if ( !empty( $meta[ 'add_to_blog' ] ) ) {
2026                 $blog_id = $meta[ 'add_to_blog' ];
2027                 $role = $meta[ 'new_role' ];
2028                 remove_user_from_blog($user_id, get_current_site()->blog_id); // remove user from main blog.
2029                 add_user_to_blog( $blog_id, $user_id, $role );
2030                 update_user_meta( $user_id, 'primary_blog', $blog_id );
2031         }
2032 }
2033
2034 /**
2035  * Correct From host on outgoing mail to match the site domain
2036  *
2037  * @since MU
2038  *
2039  * @param PHPMailer $phpmailer The PHPMailer instance, passed by reference.
2040  */
2041 function fix_phpmailer_messageid( $phpmailer ) {
2042         $phpmailer->Hostname = get_current_site()->domain;
2043 }
2044
2045 /**
2046  * Check to see whether a user is marked as a spammer, based on user login.
2047  *
2048  * @since MU
2049  *
2050  * @param string|WP_User $user Optional. Defaults to current user. WP_User object,
2051  *                                 or user login name as a string.
2052  * @return bool
2053  */
2054 function is_user_spammy( $user = null ) {
2055     if ( ! ( $user instanceof WP_User ) ) {
2056                 if ( $user ) {
2057                         $user = get_user_by( 'login', $user );
2058                 } else {
2059                         $user = wp_get_current_user();
2060                 }
2061         }
2062
2063         return $user && isset( $user->spam ) && 1 == $user->spam;
2064 }
2065
2066 /**
2067  * Update this blog's 'public' setting in the global blogs table.
2068  *
2069  * Public blogs have a setting of 1, private blogs are 0.
2070  *
2071  * @since MU
2072  *
2073  * @param int $old_value
2074  * @param int $value     The new public value
2075  */
2076 function update_blog_public( $old_value, $value ) {
2077         update_blog_status( get_current_blog_id(), 'public', (int) $value );
2078 }
2079
2080 /**
2081  * Check whether a usermeta key has to do with the current blog.
2082  *
2083  * @since MU
2084  *
2085  * @global wpdb $wpdb WordPress database abstraction object.
2086  *
2087  * @param string $key
2088  * @param int    $user_id Optional. Defaults to current user.
2089  * @param int    $blog_id Optional. Defaults to current blog.
2090  * @return bool
2091  */
2092 function is_user_option_local( $key, $user_id = 0, $blog_id = 0 ) {
2093         global $wpdb;
2094
2095         $current_user = wp_get_current_user();
2096         if ( $blog_id == 0 ) {
2097                 $blog_id = $wpdb->blogid;
2098         }
2099         $local_key = $wpdb->get_blog_prefix( $blog_id ) . $key;
2100
2101         return isset( $current_user->$local_key );
2102 }
2103
2104 /**
2105  * Check whether users can self-register, based on Network settings.
2106  *
2107  * @since MU
2108  *
2109  * @return bool
2110  */
2111 function users_can_register_signup_filter() {
2112         $registration = get_site_option('registration');
2113         return ( $registration == 'all' || $registration == 'user' );
2114 }
2115
2116 /**
2117  * Ensure that the welcome message is not empty. Currently unused.
2118  *
2119  * @since MU
2120  *
2121  * @param string $text
2122  * @return string
2123  */
2124 function welcome_user_msg_filter( $text ) {
2125         if ( !$text ) {
2126                 remove_filter( 'site_option_welcome_user_email', 'welcome_user_msg_filter' );
2127
2128                 /* translators: Do not translate USERNAME, PASSWORD, LOGINLINK, SITE_NAME: those are placeholders. */
2129                 $text = __( 'Howdy USERNAME,
2130
2131 Your new account is set up.
2132
2133 You can log in with the following information:
2134 Username: USERNAME
2135 Password: PASSWORD
2136 LOGINLINK
2137
2138 Thanks!
2139
2140 --The Team @ SITE_NAME' );
2141                 update_site_option( 'welcome_user_email', $text );
2142         }
2143         return $text;
2144 }
2145
2146 /**
2147  * Whether to force SSL on content.
2148  *
2149  * @since 2.8.5
2150  *
2151  * @staticvar bool $forced_content
2152  *
2153  * @param bool $force
2154  * @return bool True if forced, false if not forced.
2155  */
2156 function force_ssl_content( $force = '' ) {
2157         static $forced_content = false;
2158
2159         if ( '' != $force ) {
2160                 $old_forced = $forced_content;
2161                 $forced_content = $force;
2162                 return $old_forced;
2163         }
2164
2165         return $forced_content;
2166 }
2167
2168 /**
2169  * Formats a URL to use https.
2170  *
2171  * Useful as a filter.
2172  *
2173  * @since 2.8.5
2174  *
2175  * @param string $url URL
2176  * @return string URL with https as the scheme
2177  */
2178 function filter_SSL( $url ) {
2179         if ( ! is_string( $url ) )
2180                 return get_bloginfo( 'url' ); // Return home blog url with proper scheme
2181
2182         if ( force_ssl_content() && is_ssl() )
2183                 $url = set_url_scheme( $url, 'https' );
2184
2185         return $url;
2186 }
2187
2188 /**
2189  * Schedule update of the network-wide counts for the current network.
2190  *
2191  * @since 3.1.0
2192  */
2193 function wp_schedule_update_network_counts() {
2194         if ( !is_main_site() )
2195                 return;
2196
2197         if ( ! wp_next_scheduled('update_network_counts') && ! wp_installing() )
2198                 wp_schedule_event(time(), 'twicedaily', 'update_network_counts');
2199 }
2200
2201 /**
2202  *  Update the network-wide counts for the current network.
2203  *
2204  *  @since 3.1.0
2205  */
2206 function wp_update_network_counts() {
2207         wp_update_network_user_counts();
2208         wp_update_network_site_counts();
2209 }
2210
2211 /**
2212  * Update the count of sites for the current network.
2213  *
2214  * If enabled through the {@see 'enable_live_network_counts'} filter, update the sites count
2215  * on a network when a site is created or its status is updated.
2216  *
2217  * @since 3.7.0
2218  */
2219 function wp_maybe_update_network_site_counts() {
2220         $is_small_network = ! wp_is_large_network( 'sites' );
2221
2222         /**
2223          * Filters whether to update network site or user counts when a new site is created.
2224          *
2225          * @since 3.7.0
2226          *
2227          * @see wp_is_large_network()
2228          *
2229          * @param bool   $small_network Whether the network is considered small.
2230          * @param string $context       Context. Either 'users' or 'sites'.
2231          */
2232         if ( ! apply_filters( 'enable_live_network_counts', $is_small_network, 'sites' ) )
2233                 return;
2234
2235         wp_update_network_site_counts();
2236 }
2237
2238 /**
2239  * Update the network-wide users count.
2240  *
2241  * If enabled through the {@see 'enable_live_network_counts'} filter, update the users count
2242  * on a network when a user is created or its status is updated.
2243  *
2244  * @since 3.7.0
2245  */
2246 function wp_maybe_update_network_user_counts() {
2247         $is_small_network = ! wp_is_large_network( 'users' );
2248
2249         /** This filter is documented in wp-includes/ms-functions.php */
2250         if ( ! apply_filters( 'enable_live_network_counts', $is_small_network, 'users' ) )
2251                 return;
2252
2253         wp_update_network_user_counts();
2254 }
2255
2256 /**
2257  * Update the network-wide site count.
2258  *
2259  * @since 3.7.0
2260  *
2261  * @global wpdb $wpdb WordPress database abstraction object.
2262  */
2263 function wp_update_network_site_counts() {
2264         global $wpdb;
2265
2266         $count = get_sites( array(
2267                 'network_id' => $wpdb->siteid,
2268                 'spam'       => 0,
2269                 'deleted'    => 0,
2270                 'archived'   => 0,
2271                 'count'      => true,
2272         ) );
2273
2274         update_site_option( 'blog_count', $count );
2275 }
2276
2277 /**
2278  * Update the network-wide user count.
2279  *
2280  * @since 3.7.0
2281  *
2282  * @global wpdb $wpdb WordPress database abstraction object.
2283  */
2284 function wp_update_network_user_counts() {
2285         global $wpdb;
2286
2287         $count = $wpdb->get_var( "SELECT COUNT(ID) as c FROM $wpdb->users WHERE spam = '0' AND deleted = '0'" );
2288         update_site_option( 'user_count', $count );
2289 }
2290
2291 /**
2292  * Returns the space used by the current blog.
2293  *
2294  * @since 3.5.0
2295  *
2296  * @return int Used space in megabytes
2297  */
2298 function get_space_used() {
2299         /**
2300          * Filters the amount of storage space used by the current site.
2301          *
2302          * @since 3.5.0
2303          *
2304          * @param int|bool $space_used The amount of used space, in megabytes. Default false.
2305          */
2306         $space_used = apply_filters( 'pre_get_space_used', false );
2307         if ( false === $space_used ) {
2308                 $upload_dir = wp_upload_dir();
2309                 $space_used = get_dirsize( $upload_dir['basedir'] ) / MB_IN_BYTES;
2310         }
2311
2312         return $space_used;
2313 }
2314
2315 /**
2316  * Returns the upload quota for the current blog.
2317  *
2318  * @since MU
2319  *
2320  * @return int Quota in megabytes
2321  */
2322 function get_space_allowed() {
2323         $space_allowed = get_option( 'blog_upload_space' );
2324
2325         if ( ! is_numeric( $space_allowed ) )
2326                 $space_allowed = get_site_option( 'blog_upload_space' );
2327
2328         if ( ! is_numeric( $space_allowed ) )
2329                 $space_allowed = 100;
2330
2331         /**
2332          * Filters the upload quota for the current site.
2333          *
2334          * @since 3.7.0
2335          *
2336          * @param int $space_allowed Upload quota in megabytes for the current blog.
2337          */
2338         return apply_filters( 'get_space_allowed', $space_allowed );
2339 }
2340
2341 /**
2342  * Determines if there is any upload space left in the current blog's quota.
2343  *
2344  * @since 3.0.0
2345  *
2346  * @return int of upload space available in bytes
2347  */
2348 function get_upload_space_available() {
2349         $allowed = get_space_allowed();
2350         if ( $allowed < 0 ) {
2351                 $allowed = 0;
2352         }
2353         $space_allowed = $allowed * MB_IN_BYTES;
2354         if ( get_site_option( 'upload_space_check_disabled' ) )
2355                 return $space_allowed;
2356
2357         $space_used = get_space_used() * MB_IN_BYTES;
2358
2359         if ( ( $space_allowed - $space_used ) <= 0 )
2360                 return 0;
2361
2362         return $space_allowed - $space_used;
2363 }
2364
2365 /**
2366  * Determines if there is any upload space left in the current blog's quota.
2367  *
2368  * @since 3.0.0
2369  * @return bool True if space is available, false otherwise.
2370  */
2371 function is_upload_space_available() {
2372         if ( get_site_option( 'upload_space_check_disabled' ) )
2373                 return true;
2374
2375         return (bool) get_upload_space_available();
2376 }
2377
2378 /**
2379  * Filters the maximum upload file size allowed, in bytes.
2380  *
2381  * @since 3.0.0
2382  *
2383  * @param  int $size Upload size limit in bytes.
2384  * @return int       Upload size limit in bytes.
2385  */
2386 function upload_size_limit_filter( $size ) {
2387         $fileupload_maxk = KB_IN_BYTES * get_site_option( 'fileupload_maxk', 1500 );
2388         if ( get_site_option( 'upload_space_check_disabled' ) )
2389                 return min( $size, $fileupload_maxk );
2390
2391         return min( $size, $fileupload_maxk, get_upload_space_available() );
2392 }
2393
2394 /**
2395  * Whether or not we have a large network.
2396  *
2397  * The default criteria for a large network is either more than 10,000 users or more than 10,000 sites.
2398  * Plugins can alter this criteria using the {@see 'wp_is_large_network'} filter.
2399  *
2400  * @since 3.3.0
2401  * @param string $using 'sites or 'users'. Default is 'sites'.
2402  * @return bool True if the network meets the criteria for large. False otherwise.
2403  */
2404 function wp_is_large_network( $using = 'sites' ) {
2405         if ( 'users' == $using ) {
2406                 $count = get_user_count();
2407                 /**
2408                  * Filters whether the network is considered large.
2409                  *
2410                  * @since 3.3.0
2411                  *
2412                  * @param bool   $is_large_network Whether the network has more than 10000 users or sites.
2413                  * @param string $component        The component to count. Accepts 'users', or 'sites'.
2414                  * @param int    $count            The count of items for the component.
2415                  */
2416                 return apply_filters( 'wp_is_large_network', $count > 10000, 'users', $count );
2417         }
2418
2419         $count = get_blog_count();
2420         /** This filter is documented in wp-includes/ms-functions.php */
2421         return apply_filters( 'wp_is_large_network', $count > 10000, 'sites', $count );
2422 }
2423
2424 /**
2425  * Retrieves a list of reserved site on a sub-directory Multisite install.
2426  *
2427  * @since 4.4.0
2428  *
2429  * @return array $names Array of reserved subdirectory names.
2430  */
2431 function get_subdirectory_reserved_names() {
2432         $names = array(
2433                 'page', 'comments', 'blog', 'files', 'feed', 'wp-admin',
2434                 'wp-content', 'wp-includes', 'wp-json', 'embed'
2435         );
2436
2437         /**
2438          * Filters reserved site names on a sub-directory Multisite install.
2439          *
2440          * @since 3.0.0
2441          * @since 4.4.0 'wp-admin', 'wp-content', 'wp-includes', 'wp-json', and 'embed' were added
2442          *              to the reserved names list.
2443          *
2444          * @param array $subdirectory_reserved_names Array of reserved names.
2445          */
2446         return apply_filters( 'subdirectory_reserved_names', $names );
2447 }