Issues (2010)

Security Analysis    not enabled

This project does not seem to handle request data directly as such no vulnerable execution paths were found.

  Cross-Site Scripting
Cross-Site Scripting enables an attacker to inject code into the response of a web-request that is viewed by other users. It can for example be used to bypass access controls, or even to take over other users' accounts.
  File Exposure
File Exposure allows an attacker to gain access to local files that he should not be able to access. These files can for example include database credentials, or other configuration files.
  File Manipulation
File Manipulation enables an attacker to write custom data to files. This potentially leads to injection of arbitrary code on the server.
  Object Injection
Object Injection enables an attacker to inject an object into PHP code, and can lead to arbitrary code execution, file exposure, or file manipulation attacks.
  Code Injection
Code Injection enables an attacker to execute arbitrary code on the server.
  Response Splitting
Response Splitting can be used to send arbitrary responses.
  File Inclusion
File Inclusion enables an attacker to inject custom files into PHP's file loading mechanism, either explicitly passed to include, or for example via PHP's auto-loading mechanism.
  Command Injection
Command Injection enables an attacker to inject a shell command that is execute with the privileges of the web-server. This can be used to expose sensitive data, or gain access of your server.
  SQL Injection
SQL Injection enables an attacker to execute arbitrary SQL code on your database server gaining access to user data, or manipulating user data.
  XPath Injection
XPath Injection enables an attacker to modify the parts of XML document that are read. If that XML document is for example used for authentication, this can lead to further vulnerabilities similar to SQL Injection.
  LDAP Injection
LDAP Injection enables an attacker to inject LDAP statements potentially granting permission to run unauthorized queries, or modify content inside the LDAP tree.
  Header Injection
  Other Vulnerability
This category comprises other attack vectors such as manipulating the PHP runtime, loading custom extensions, freezing the runtime, or similar.
  Regex Injection
Regex Injection enables an attacker to execute arbitrary code in your PHP process.
  XML Injection
XML Injection enables an attacker to read files on your local filesystem including configuration files, or can be abused to freeze your web-server process.
  Variable Injection
Variable Injection enables an attacker to overwrite program variables with custom data, and can lead to further vulnerabilities.
Unfortunately, the security analysis is currently not available for your project. If you are a non-commercial open-source project, please contact support to gain access.

wp-includes/ms-functions.php (1 issue)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

Code
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 View Code Duplication
		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 View Code Duplication
		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 View Code Duplication
	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 View Code Duplication
	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 View Code Duplication
	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 View Code Duplication
	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 View Code Duplication
	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 )
0 ignored issues
show
Bug Best Practice introduced by
The expression $user_id of type integer|false is loosely compared to false; this is ambiguous if the integer can be zero. You might want to explicitly use === null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
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 View Code Duplication
	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
}
2448