Issues (4967)

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.

src/wp-includes/ms-functions.php (23 issues)

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

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.
0 ignored issues
show
Consider making the return type a bit more specific; maybe use array<string,integer>.

This check looks for the generic type array as a return type and suggests a more specific type. This type is inferred from the actual code.

Loading history...
16
 */
17
function get_sitestats() {
18
	$stats = array(
19
		'blogs' => get_blog_count(),
20
		'users' => get_user_count(),
21
	);
22
23
	return $stats;
24
}
25
26
/**
27
 * Get one of a user's active blogs
28
 *
29
 * Returns the user's primary blog, if they have one and
30
 * it is active. If it's inactive, function returns another
31
 * active blog of the user. If none are found, the user
32
 * is added as a Subscriber to the Dashboard Blog and that blog
33
 * is returned.
34
 *
35
 * @since MU 1.0
36
 *
37
 * @global wpdb $wpdb WordPress database abstraction object.
38
 *
39
 * @param int $user_id The unique ID of the user
40
 * @return WP_Site|void The blog object
41
 */
42
function get_active_blog_for_user( $user_id ) {
43
	global $wpdb;
44
	$blogs = get_blogs_of_user( $user_id );
45
	if ( empty( $blogs ) )
46
		return;
47
48
	if ( !is_multisite() )
49
		return $blogs[$wpdb->blogid];
50
51
	$primary_blog = get_user_meta( $user_id, 'primary_blog', true );
52
	$first_blog = current($blogs);
53
	if ( false !== $primary_blog ) {
54
		if ( ! isset( $blogs[ $primary_blog ] ) ) {
55
			update_user_meta( $user_id, 'primary_blog', $first_blog->userblog_id );
56
			$primary = get_site( $first_blog->userblog_id );
57
		} else {
58
			$primary = get_site( $primary_blog );
59
		}
60
	} else {
61
		//TODO Review this call to add_user_to_blog too - to get here the user must have a role on this blog?
62
		add_user_to_blog( $first_blog->userblog_id, $user_id, 'subscriber' );
63
		update_user_meta( $user_id, 'primary_blog', $first_blog->userblog_id );
64
		$primary = $first_blog;
65
	}
66
67
	if ( ( ! is_object( $primary ) ) || ( $primary->archived == 1 || $primary->spam == 1 || $primary->deleted == 1 ) ) {
68
		$blogs = get_blogs_of_user( $user_id, true ); // if a user's primary blog is shut down, check their other blogs.
69
		$ret = false;
70
		if ( is_array( $blogs ) && count( $blogs ) > 0 ) {
71
			foreach ( (array) $blogs as $blog_id => $blog ) {
72
				if ( $blog->site_id != $wpdb->siteid )
73
					continue;
74
				$details = get_site( $blog_id );
75
				if ( is_object( $details ) && $details->archived == 0 && $details->spam == 0 && $details->deleted == 0 ) {
76
					$ret = $blog;
77
					if ( get_user_meta( $user_id , 'primary_blog', true ) != $blog_id )
78
						update_user_meta( $user_id, 'primary_blog', $blog_id );
79
					if ( !get_user_meta($user_id , 'source_domain', true) )
80
						update_user_meta( $user_id, 'source_domain', $blog->domain );
81
					break;
82
				}
83
			}
84
		} else {
85
			return;
86
		}
87
		return $ret;
88
	} else {
89
		return $primary;
90
	}
91
}
92
93
/**
94
 * The number of active users in your installation.
95
 *
96
 * The count is cached and updated twice daily. This is not a live count.
97
 *
98
 * @since MU 2.7
99
 * @since 4.8.0 The $network_id parameter has been added.
100
 *
101
 * @param int|null $network_id ID of the network. Default is the current network.
102
 * @return int Number of active users on the network.
103
 */
104
function get_user_count( $network_id = null ) {
105
	return get_network_option( $network_id, 'user_count' );
106
}
107
108
/**
109
 * The number of active sites on your installation.
110
 *
111
 * The count is cached and updated twice daily. This is not a live count.
112
 *
113
 * @since MU 1.0
114
 * @since 3.7.0 The $network_id parameter has been deprecated.
115
 * @since 4.8.0 The $network_id parameter is now being used.
116
 *
117
 * @param int|null $network_id ID of the network. Default is the current network.
118
 * @return int Number of active sites on the network.
119
 */
120
function get_blog_count( $network_id = null ) {
121
	return get_network_option( $network_id, 'blog_count' );
122
}
123
124
/**
125
 * Get a blog post from any site on the network.
126
 *
127
 * @since MU 1.0
128
 *
129
 * @param int $blog_id ID of the blog.
130
 * @param int $post_id ID of the post you're looking for.
131
 * @return WP_Post|null WP_Post on success or null on failure
0 ignored issues
show
Should the return type not be WP_Post|array|null? Also, consider making the array more specific, something like array<String>, or String[].

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

If the return type contains the type array, this check recommends the use of a more specific type like String[] or array<String>.

Loading history...
132
 */
133
function get_blog_post( $blog_id, $post_id ) {
134
	switch_to_blog( $blog_id );
135
	$post = get_post( $post_id );
136
	restore_current_blog();
137
138
	return $post;
139
}
140
141
/**
142
 * Adds a user to a blog.
143
 *
144
 * Use the {@see 'add_user_to_blog'} action to fire an event when users are added to a blog.
145
 *
146
 * @since MU 1.0
147
 *
148
 * @param int    $blog_id ID of the blog you're adding the user to.
149
 * @param int    $user_id ID of the user you're adding.
150
 * @param string $role    The role you want the user to have
151
 * @return true|WP_Error
0 ignored issues
show
Should the return type not be WP_Error|boolean?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
152
 */
153
function add_user_to_blog( $blog_id, $user_id, $role ) {
154
	switch_to_blog($blog_id);
155
156
	$user = get_userdata( $user_id );
157
158
	if ( ! $user ) {
159
		restore_current_blog();
160
		return new WP_Error( 'user_does_not_exist', __( 'The requested user does not exist.' ) );
161
	}
162
163
	if ( !get_user_meta($user_id, 'primary_blog', true) ) {
164
		update_user_meta($user_id, 'primary_blog', $blog_id);
165
		$site = get_site( $blog_id );
166
		update_user_meta( $user_id, 'source_domain', $site->domain );
167
	}
168
169
	$user->set_role($role);
170
171
	/**
172
	 * Fires immediately after a user is added to a site.
173
	 *
174
	 * @since MU
175
	 *
176
	 * @param int    $user_id User ID.
177
	 * @param string $role    User role.
178
	 * @param int    $blog_id Blog ID.
179
	 */
180
	do_action( 'add_user_to_blog', $user_id, $role, $blog_id );
181
	wp_cache_delete( $user_id, 'users' );
182
	wp_cache_delete( $blog_id . '_user_count', 'blog-details' );
183
	restore_current_blog();
184
	return true;
185
}
186
187
/**
188
 * Remove a user from a blog.
189
 *
190
 * Use the {@see 'remove_user_from_blog'} action to fire an event when
191
 * users are removed from a blog.
192
 *
193
 * Accepts an optional `$reassign` parameter, if you want to
194
 * reassign the user's blog posts to another user upon removal.
195
 *
196
 * @since MU 1.0
197
 *
198
 * @global wpdb $wpdb WordPress database abstraction object.
199
 *
200
 * @param int    $user_id  ID of the user you're removing.
201
 * @param int    $blog_id  ID of the blog you're removing the user from.
202
 * @param string $reassign Optional. A user to whom to reassign posts.
203
 * @return true|WP_Error
0 ignored issues
show
Should the return type not be WP_Error|boolean?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
204
 */
205
function remove_user_from_blog($user_id, $blog_id = '', $reassign = '') {
206
	global $wpdb;
207
	switch_to_blog($blog_id);
208
	$user_id = (int) $user_id;
209
	/**
210
	 * Fires before a user is removed from a site.
211
	 *
212
	 * @since MU
213
	 *
214
	 * @param int $user_id User ID.
215
	 * @param int $blog_id Blog ID.
216
	 */
217
	do_action( 'remove_user_from_blog', $user_id, $blog_id );
218
219
	// If being removed from the primary blog, set a new primary if the user is assigned
220
	// to multiple blogs.
221
	$primary_blog = get_user_meta($user_id, 'primary_blog', true);
222
	if ( $primary_blog == $blog_id ) {
223
		$new_id = '';
224
		$new_domain = '';
225
		$blogs = get_blogs_of_user($user_id);
226
		foreach ( (array) $blogs as $blog ) {
227
			if ( $blog->userblog_id == $blog_id )
228
				continue;
229
			$new_id = $blog->userblog_id;
230
			$new_domain = $blog->domain;
231
			break;
232
		}
233
234
		update_user_meta($user_id, 'primary_blog', $new_id);
235
		update_user_meta($user_id, 'source_domain', $new_domain);
236
	}
237
238
	// wp_revoke_user($user_id);
239
	$user = get_userdata( $user_id );
240
	if ( ! $user ) {
241
		restore_current_blog();
242
		return new WP_Error('user_does_not_exist', __('That user does not exist.'));
243
	}
244
245
	$user->remove_all_caps();
246
247
	$blogs = get_blogs_of_user($user_id);
248
	if ( count($blogs) == 0 ) {
249
		update_user_meta($user_id, 'primary_blog', '');
250
		update_user_meta($user_id, 'source_domain', '');
251
	}
252
253
	if ( $reassign != '' ) {
254
		$reassign = (int) $reassign;
255
		$post_ids = $wpdb->get_col( $wpdb->prepare( "SELECT ID FROM $wpdb->posts WHERE post_author = %d", $user_id ) );
256
		$link_ids = $wpdb->get_col( $wpdb->prepare( "SELECT link_id FROM $wpdb->links WHERE link_owner = %d", $user_id ) );
257
258 View Code Duplication
		if ( ! empty( $post_ids ) ) {
259
			$wpdb->query( $wpdb->prepare( "UPDATE $wpdb->posts SET post_author = %d WHERE post_author = %d", $reassign, $user_id ) );
260
			array_walk( $post_ids, 'clean_post_cache' );
261
		}
262
263 View Code Duplication
		if ( ! empty( $link_ids ) ) {
264
			$wpdb->query( $wpdb->prepare( "UPDATE $wpdb->links SET link_owner = %d WHERE link_owner = %d", $reassign, $user_id ) );
265
			array_walk( $link_ids, 'clean_bookmark_cache' );
266
		}
267
	}
268
269
	restore_current_blog();
270
271
	return true;
272
}
273
274
/**
275
 * Get the permalink for a post on another blog.
276
 *
277
 * @since MU 1.0
278
 *
279
 * @param int $blog_id ID of the source blog.
280
 * @param int $post_id ID of the desired post.
281
 * @return string The post's permalink
0 ignored issues
show
Should the return type not be string|false?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
282
 */
283
function get_blog_permalink( $blog_id, $post_id ) {
284
	switch_to_blog( $blog_id );
285
	$link = get_permalink( $post_id );
286
	restore_current_blog();
287
288
	return $link;
289
}
290
291
/**
292
 * Get a blog's numeric ID from its URL.
293
 *
294
 * On a subdirectory installation like example.com/blog1/,
295
 * $domain will be the root 'example.com' and $path the
296
 * subdirectory '/blog1/'. With subdomains like blog1.example.com,
297
 * $domain is 'blog1.example.com' and $path is '/'.
298
 *
299
 * @since MU 2.6.5
300
 *
301
 * @global wpdb $wpdb WordPress database abstraction object.
302
 *
303
 * @param string $domain
304
 * @param string $path   Optional. Not required for subdomain installations.
305
 * @return int 0 if no blog found, otherwise the ID of the matching blog
306
 */
307
function get_blog_id_from_url( $domain, $path = '/' ) {
308
	$domain = strtolower( $domain );
309
	$path = strtolower( $path );
310
	$id = wp_cache_get( md5( $domain . $path ), 'blog-id-cache' );
311
312
	if ( $id == -1 ) // blog does not exist
313
		return 0;
314
	elseif ( $id )
315
		return (int) $id;
316
317
	$args = array(
318
		'domain' => $domain,
319
		'path' => $path,
320
		'fields' => 'ids',
321
	);
322
	$result = get_sites( $args );
323
	$id = array_shift( $result );
324
325
	if ( ! $id ) {
326
		wp_cache_set( md5( $domain . $path ), -1, 'blog-id-cache' );
327
		return 0;
328
	}
329
330
	wp_cache_set( md5( $domain . $path ), $id, 'blog-id-cache' );
331
332
	return $id;
333
}
334
335
// Admin functions
336
337
/**
338
 * Checks an email address against a list of banned domains.
339
 *
340
 * This function checks against the Banned Email Domains list
341
 * at wp-admin/network/settings.php. The check is only run on
342
 * self-registrations; user creation at wp-admin/network/users.php
343
 * bypasses this check.
344
 *
345
 * @since MU
346
 *
347
 * @param string $user_email The email provided by the user at registration.
348
 * @return bool Returns true when the email address is banned.
349
 */
350
function is_email_address_unsafe( $user_email ) {
351
	$banned_names = get_site_option( 'banned_email_domains' );
352
	if ( $banned_names && ! is_array( $banned_names ) )
353
		$banned_names = explode( "\n", $banned_names );
354
355
	$is_email_address_unsafe = false;
356
357
	if ( $banned_names && is_array( $banned_names ) && false !== strpos( $user_email, '@', 1 ) ) {
358
		$banned_names = array_map( 'strtolower', $banned_names );
359
		$normalized_email = strtolower( $user_email );
360
361
		list( $email_local_part, $email_domain ) = explode( '@', $normalized_email );
0 ignored issues
show
The assignment to $email_local_part is unused. Consider omitting it like so list($first,,$third).

This checks looks for assignemnts to variables using the list(...) function, where not all assigned variables are subsequently used.

Consider the following code example.

<?php

function returnThreeValues() {
    return array('a', 'b', 'c');
}

list($a, $b, $c) = returnThreeValues();

print $a . " - " . $c;

Only the variables $a and $c are used. There was no need to assign $b.

Instead, the list call could have been.

list($a,, $c) = returnThreeValues();
Loading history...
362
363
		foreach ( $banned_names as $banned_domain ) {
364
			if ( ! $banned_domain )
365
				continue;
366
367
			if ( $email_domain == $banned_domain ) {
368
				$is_email_address_unsafe = true;
369
				break;
370
			}
371
372
			$dotted_domain = ".$banned_domain";
373
			if ( $dotted_domain === substr( $normalized_email, -strlen( $dotted_domain ) ) ) {
374
				$is_email_address_unsafe = true;
375
				break;
376
			}
377
		}
378
	}
379
380
	/**
381
	 * Filters whether an email address is unsafe.
382
	 *
383
	 * @since 3.5.0
384
	 *
385
	 * @param bool   $is_email_address_unsafe Whether the email address is "unsafe". Default false.
386
	 * @param string $user_email              User email address.
387
	 */
388
	return apply_filters( 'is_email_address_unsafe', $is_email_address_unsafe, $user_email );
389
}
390
391
/**
392
 * Sanitize and validate data required for a user sign-up.
393
 *
394
 * Verifies the validity and uniqueness of user names and user email addresses,
395
 * and checks email addresses against admin-provided domain whitelists and blacklists.
396
 *
397
 * The {@see 'wpmu_validate_user_signup'} hook provides an easy way to modify the sign-up
398
 * process. The value $result, which is passed to the hook, contains both the user-provided
399
 * info and the error messages created by the function. {@see 'wpmu_validate_user_signup'}
400
 * allows you to process the data in any way you'd like, and unset the relevant errors if
401
 * necessary.
402
 *
403
 * @since MU
404
 *
405
 * @global wpdb $wpdb WordPress database abstraction object.
406
 *
407
 * @param string $user_name  The login name provided by the user.
408
 * @param string $user_email The email provided by the user.
409
 * @return array Contains username, email, and error messages.
410
 */
411
function wpmu_validate_user_signup($user_name, $user_email) {
412
	global $wpdb;
413
414
	$errors = new WP_Error();
415
416
	$orig_username = $user_name;
417
	$user_name = preg_replace( '/\s+/', '', sanitize_user( $user_name, true ) );
418
419
	if ( $user_name != $orig_username || preg_match( '/[^a-z0-9]/', $user_name ) ) {
420
		$errors->add( 'user_name', __( 'Usernames can only contain lowercase letters (a-z) and numbers.' ) );
421
		$user_name = $orig_username;
422
	}
423
424
	$user_email = sanitize_email( $user_email );
425
426
	if ( empty( $user_name ) )
427
	   	$errors->add('user_name', __( 'Please enter a username.' ) );
428
429
	$illegal_names = get_site_option( 'illegal_names' );
430 View Code Duplication
	if ( ! is_array( $illegal_names ) ) {
431
		$illegal_names = array(  'www', 'web', 'root', 'admin', 'main', 'invite', 'administrator' );
432
		add_site_option( 'illegal_names', $illegal_names );
433
	}
434
	if ( in_array( $user_name, $illegal_names ) ) {
435
		$errors->add( 'user_name',  __( 'Sorry, that username is not allowed.' ) );
436
	}
437
438
	/** This filter is documented in wp-includes/user.php */
439
	$illegal_logins = (array) apply_filters( 'illegal_user_logins', array() );
440
441
	if ( in_array( strtolower( $user_name ), array_map( 'strtolower', $illegal_logins ) ) ) {
442
		$errors->add( 'user_name',  __( 'Sorry, that username is not allowed.' ) );
443
	}
444
445
	if ( ! is_email( $user_email ) ) {
446
		$errors->add( 'user_email', __( 'Please enter a valid email address.' ) );
447
	} elseif ( is_email_address_unsafe( $user_email ) ) {
448
		$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.' ) );
449
	}
450
451
	if ( strlen( $user_name ) < 4 )
452
		$errors->add('user_name',  __( 'Username must be at least 4 characters.' ) );
453
454
	if ( strlen( $user_name ) > 60 ) {
455
		$errors->add( 'user_name', __( 'Username may not be longer than 60 characters.' ) );
456
	}
457
458
	// all numeric?
459
	if ( preg_match( '/^[0-9]*$/', $user_name ) )
460
		$errors->add('user_name', __('Sorry, usernames must have letters too!'));
461
462
	$limited_email_domains = get_site_option( 'limited_email_domains' );
463
	if ( is_array( $limited_email_domains ) && ! empty( $limited_email_domains ) ) {
464
		$emaildomain = substr( $user_email, 1 + strpos( $user_email, '@' ) );
465
		if ( ! in_array( $emaildomain, $limited_email_domains ) ) {
466
			$errors->add('user_email', __('Sorry, that email address is not allowed!'));
467
		}
468
	}
469
470
	// Check if the username has been used already.
471
	if ( username_exists($user_name) )
472
		$errors->add( 'user_name', __( 'Sorry, that username already exists!' ) );
473
474
	// Check if the email address has been used already.
475
	if ( email_exists($user_email) )
476
		$errors->add( 'user_email', __( 'Sorry, that email address is already used!' ) );
477
478
	// Has someone already signed up for this username?
479
	$signup = $wpdb->get_row( $wpdb->prepare("SELECT * FROM $wpdb->signups WHERE user_login = %s", $user_name) );
480
	if ( $signup != null ) {
481
		$registered_at =  mysql2date('U', $signup->registered);
482
		$now = current_time( 'timestamp', true );
483
		$diff = $now - $registered_at;
484
		// If registered more than two days ago, cancel registration and let this signup go through.
485
		if ( $diff > 2 * DAY_IN_SECONDS )
486
			$wpdb->delete( $wpdb->signups, array( 'user_login' => $user_name ) );
487
		else
488
			$errors->add('user_name', __('That username is currently reserved but may be available in a couple of days.'));
489
	}
490
491
	$signup = $wpdb->get_row( $wpdb->prepare("SELECT * FROM $wpdb->signups WHERE user_email = %s", $user_email) );
492 View Code Duplication
	if ( $signup != null ) {
493
		$diff = current_time( 'timestamp', true ) - mysql2date('U', $signup->registered);
494
		// If registered more than two days ago, cancel registration and let this signup go through.
495
		if ( $diff > 2 * DAY_IN_SECONDS )
496
			$wpdb->delete( $wpdb->signups, array( 'user_email' => $user_email ) );
497
		else
498
			$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.'));
499
	}
500
501
	$result = array('user_name' => $user_name, 'orig_username' => $orig_username, 'user_email' => $user_email, 'errors' => $errors);
502
503
	/**
504
	 * Filters the validated user registration details.
505
	 *
506
	 * This does not allow you to override the username or email of the user during
507
	 * registration. The values are solely used for validation and error handling.
508
	 *
509
	 * @since MU
510
	 *
511
	 * @param array $result {
512
	 *     The array of user name, email and the error messages.
513
	 *
514
	 *     @type string   $user_name     Sanitized and unique username.
515
	 *     @type string   $orig_username Original username.
516
	 *     @type string   $user_email    User email address.
517
	 *     @type WP_Error $errors        WP_Error object containing any errors found.
518
	 * }
519
	 */
520
	return apply_filters( 'wpmu_validate_user_signup', $result );
521
}
522
523
/**
524
 * Processes new site registrations.
525
 *
526
 * Checks the data provided by the user during blog signup. Verifies
527
 * the validity and uniqueness of blog paths and domains.
528
 *
529
 * This function prevents the current user from registering a new site
530
 * with a blogname equivalent to another user's login name. Passing the
531
 * $user parameter to the function, where $user is the other user, is
532
 * effectively an override of this limitation.
533
 *
534
 * Filter {@see 'wpmu_validate_blog_signup'} if you want to modify
535
 * the way that WordPress validates new site signups.
536
 *
537
 * @since MU
538
 *
539
 * @global wpdb   $wpdb
540
 * @global string $domain
541
 *
542
 * @param string         $blogname   The blog name provided by the user. Must be unique.
543
 * @param string         $blog_title The blog title provided by the user.
544
 * @param WP_User|string $user       Optional. The user object to check against the new site name.
545
 * @return array Contains the new site data and error messages.
546
 */
547
function wpmu_validate_blog_signup( $blogname, $blog_title, $user = '' ) {
548
	global $wpdb, $domain;
549
550
	$current_network = get_network();
551
	$base = $current_network->path;
552
553
	$blog_title = strip_tags( $blog_title );
554
555
	$errors = new WP_Error();
556
	$illegal_names = get_site_option( 'illegal_names' );
557 View Code Duplication
	if ( $illegal_names == false ) {
558
		$illegal_names = array( 'www', 'web', 'root', 'admin', 'main', 'invite', 'administrator' );
559
		add_site_option( 'illegal_names', $illegal_names );
560
	}
561
562
	/*
563
	 * On sub dir installs, some names are so illegal, only a filter can
564
	 * spring them from jail.
565
	 */
566
	if ( ! is_subdomain_install() ) {
567
		$illegal_names = array_merge( $illegal_names, get_subdirectory_reserved_names() );
568
	}
569
570
	if ( empty( $blogname ) )
571
		$errors->add('blogname', __( 'Please enter a site name.' ) );
572
573
	if ( preg_match( '/[^a-z0-9]+/', $blogname ) ) {
574
		$errors->add( 'blogname', __( 'Site names can only contain lowercase letters (a-z) and numbers.' ) );
575
	}
576
577
	if ( in_array( $blogname, $illegal_names ) )
578
		$errors->add('blogname',  __( 'That name is not allowed.' ) );
579
580
	/**
581
	 * Filters the minimum site name length required when validating a site signup.
582
	 *
583
	 * @since 4.8.0
584
	 *
585
	 * @param int $length The minimum site name length. Default 4.
586
	 */
587
	$minimum_site_name_length = apply_filters( 'minimum_site_name_length', 4 );
588
589
	if ( strlen( $blogname ) < $minimum_site_name_length ) {
590
		/* translators: %s: minimum site name length */
591
		$errors->add( 'blogname', sprintf( _n( 'Site name must be at least %s character.', 'Site name must be at least %s characters.', $minimum_site_name_length ), number_format_i18n( $minimum_site_name_length ) ) );
592
	}
593
594
	// do not allow users to create a blog that conflicts with a page on the main blog.
595
	if ( !is_subdomain_install() && $wpdb->get_var( $wpdb->prepare( "SELECT post_name FROM " . $wpdb->get_blog_prefix( $current_network->site_id ) . "posts WHERE post_type = 'page' AND post_name = %s", $blogname ) ) )
596
		$errors->add( 'blogname', __( 'Sorry, you may not use that site name.' ) );
597
598
	// all numeric?
599
	if ( preg_match( '/^[0-9]*$/', $blogname ) )
600
		$errors->add('blogname', __('Sorry, site names must have letters too!'));
601
602
	/**
603
	 * Filters the new site name during registration.
604
	 *
605
	 * The name is the site's subdomain or the site's subdirectory
606
	 * path depending on the network settings.
607
	 *
608
	 * @since MU
609
	 *
610
	 * @param string $blogname Site name.
611
	 */
612
	$blogname = apply_filters( 'newblogname', $blogname );
613
614
	$blog_title = wp_unslash(  $blog_title );
615
616
	if ( empty( $blog_title ) )
617
		$errors->add('blog_title', __( 'Please enter a site title.' ) );
618
619
	// Check if the domain/path has been used already.
620
	if ( is_subdomain_install() ) {
621
		$mydomain = $blogname . '.' . preg_replace( '|^www\.|', '', $domain );
622
		$path = $base;
623
	} else {
624
		$mydomain = "$domain";
625
		$path = $base.$blogname.'/';
626
	}
627
	if ( domain_exists($mydomain, $path, $current_network->id) )
628
		$errors->add( 'blogname', __( 'Sorry, that site already exists!' ) );
629
630
	if ( username_exists( $blogname ) ) {
631
		if ( ! is_object( $user ) || ( is_object($user) && ( $user->user_login != $blogname ) ) )
632
			$errors->add( 'blogname', __( 'Sorry, that site is reserved!' ) );
633
	}
634
635
	// Has someone already signed up for this domain?
636
	$signup = $wpdb->get_row( $wpdb->prepare("SELECT * FROM $wpdb->signups WHERE domain = %s AND path = %s", $mydomain, $path) ); // TODO: Check email too?
637 View Code Duplication
	if ( ! empty($signup) ) {
638
		$diff = current_time( 'timestamp', true ) - mysql2date('U', $signup->registered);
639
		// If registered more than two days ago, cancel registration and let this signup go through.
640
		if ( $diff > 2 * DAY_IN_SECONDS )
641
			$wpdb->delete( $wpdb->signups, array( 'domain' => $mydomain , 'path' => $path ) );
642
		else
643
			$errors->add('blogname', __('That site is currently reserved but may be available in a couple days.'));
644
	}
645
646
	$result = array('domain' => $mydomain, 'path' => $path, 'blogname' => $blogname, 'blog_title' => $blog_title, 'user' => $user, 'errors' => $errors);
647
648
	/**
649
	 * Filters site details and error messages following registration.
650
	 *
651
	 * @since MU
652
	 *
653
	 * @param array $result {
654
	 *     Array of domain, path, blog name, blog title, user and error messages.
655
	 *
656
	 *     @type string         $domain     Domain for the site.
657
	 *     @type string         $path       Path for the site. Used in subdirectory installs.
658
	 *     @type string         $blogname   The unique site name (slug).
659
	 *     @type string         $blog_title Blog title.
660
	 *     @type string|WP_User $user       By default, an empty string. A user object if provided.
661
	 *     @type WP_Error       $errors     WP_Error containing any errors found.
662
	 * }
663
	 */
664
	return apply_filters( 'wpmu_validate_blog_signup', $result );
665
}
666
667
/**
668
 * Record site signup information for future activation.
669
 *
670
 * @since MU
671
 *
672
 * @global wpdb $wpdb WordPress database abstraction object.
673
 *
674
 * @param string $domain     The requested domain.
675
 * @param string $path       The requested path.
676
 * @param string $title      The requested site title.
677
 * @param string $user       The user's requested login name.
678
 * @param string $user_email The user's email address.
679
 * @param array  $meta       Optional. Signup meta data. By default, contains the requested privacy setting and lang_id.
680
 */
681
function wpmu_signup_blog( $domain, $path, $title, $user, $user_email, $meta = array() )  {
682
	global $wpdb;
683
684
	$key = substr( md5( time() . wp_rand() . $domain ), 0, 16 );
685
686
	/**
687
	 * Filters the metadata for a site signup.
688
	 *
689
	 * The metadata will be serialized prior to storing it in the database.
690
	 *
691
	 * @since 4.8.0
692
	 *
693
	 * @param array  $meta       Signup meta data. Default empty array.
694
	 * @param string $domain     The requested domain.
695
	 * @param string $path       The requested path.
696
	 * @param string $title      The requested site title.
697
	 * @param string $user       The user's requested login name.
698
	 * @param string $user_email The user's email address.
699
	 * @param string $key        The user's activation key.
700
	 */
701
	$meta = apply_filters( 'signup_site_meta', $meta, $domain, $path, $title, $user, $user_email, $key );
702
703
	$wpdb->insert( $wpdb->signups, array(
704
		'domain' => $domain,
705
		'path' => $path,
706
		'title' => $title,
707
		'user_login' => $user,
708
		'user_email' => $user_email,
709
		'registered' => current_time('mysql', true),
710
		'activation_key' => $key,
711
		'meta' => serialize( $meta )
712
	) );
713
714
	/**
715
	 * Fires after site signup information has been written to the database.
716
	 *
717
	 * @since 4.4.0
718
	 *
719
	 * @param string $domain     The requested domain.
720
	 * @param string $path       The requested path.
721
	 * @param string $title      The requested site title.
722
	 * @param string $user       The user's requested login name.
723
	 * @param string $user_email The user's email address.
724
	 * @param string $key        The user's activation key.
725
	 * @param array  $meta       Signup meta data. By default, contains the requested privacy setting and lang_id.
726
	 */
727
	do_action( 'after_signup_site', $domain, $path, $title, $user, $user_email, $key, $meta );
728
}
729
730
/**
731
 * Record user signup information for future activation.
732
 *
733
 * This function is used when user registration is open but
734
 * new site registration is not.
735
 *
736
 * @since MU
737
 *
738
 * @global wpdb $wpdb WordPress database abstraction object.
739
 *
740
 * @param string $user       The user's requested login name.
741
 * @param string $user_email The user's email address.
742
 * @param array  $meta       Optional. Signup meta data. Default empty array.
743
 */
744
function wpmu_signup_user( $user, $user_email, $meta = array() ) {
745
	global $wpdb;
746
747
	// Format data
748
	$user = preg_replace( '/\s+/', '', sanitize_user( $user, true ) );
749
	$user_email = sanitize_email( $user_email );
750
	$key = substr( md5( time() . wp_rand() . $user_email ), 0, 16 );
751
752
	/**
753
	 * Filters the metadata for a user signup.
754
	 *
755
	 * The metadata will be serialized prior to storing it in the database.
756
	 *
757
	 * @since 4.8.0
758
	 *
759
	 * @param array  $meta       Signup meta data. Default empty array.
760
	 * @param string $user       The user's requested login name.
761
	 * @param string $user_email The user's email address.
762
	 * @param string $key        The user's activation key.
763
	 */
764
	$meta = apply_filters( 'signup_user_meta', $meta, $user, $user_email, $key );
765
766
	$wpdb->insert( $wpdb->signups, array(
767
		'domain' => '',
768
		'path' => '',
769
		'title' => '',
770
		'user_login' => $user,
771
		'user_email' => $user_email,
772
		'registered' => current_time('mysql', true),
773
		'activation_key' => $key,
774
		'meta' => serialize( $meta )
775
	) );
776
777
	/**
778
	 * Fires after a user's signup information has been written to the database.
779
	 *
780
	 * @since 4.4.0
781
	 *
782
	 * @param string $user       The user's requested login name.
783
	 * @param string $user_email The user's email address.
784
	 * @param string $key        The user's activation key.
785
	 * @param array  $meta       Signup meta data. Default empty array.
786
	 */
787
	do_action( 'after_signup_user', $user, $user_email, $key, $meta );
788
}
789
790
/**
791
 * Notify user of signup success.
792
 *
793
 * This is the notification function used when site registration
794
 * is enabled.
795
 *
796
 * Filter {@see 'wpmu_signup_blog_notification'} to bypass this function or
797
 * replace it with your own notification behavior.
798
 *
799
 * Filter {@see 'wpmu_signup_blog_notification_email'} and
800
 * {@see 'wpmu_signup_blog_notification_subject'} to change the content
801
 * and subject line of the email sent to newly registered users.
802
 *
803
 * @since MU
804
 *
805
 * @param string $domain     The new blog domain.
806
 * @param string $path       The new blog path.
807
 * @param string $title      The site title.
808
 * @param string $user_login The user's login name.
809
 * @param string $user_email The user's email address.
810
 * @param string $key        The activation key created in wpmu_signup_blog()
811
 * @param array  $meta       Optional. Signup meta data. By default, contains the requested privacy setting and lang_id.
812
 * @return bool
813
 */
814
function wpmu_signup_blog_notification( $domain, $path, $title, $user_login, $user_email, $key, $meta = array() ) {
815
	/**
816
	 * Filters whether to bypass the new site email notification.
817
	 *
818
	 * @since MU
819
	 *
820
	 * @param string|bool $domain     Site domain.
821
	 * @param string      $path       Site path.
822
	 * @param string      $title      Site title.
823
	 * @param string      $user_login User login name.
824
	 * @param string      $user_email User email address.
825
	 * @param string      $key        Activation key created in wpmu_signup_blog().
826
	 * @param array       $meta       Signup meta data. By default, contains the requested privacy setting and lang_id.
827
	 */
828
	if ( ! apply_filters( 'wpmu_signup_blog_notification', $domain, $path, $title, $user_login, $user_email, $key, $meta ) ) {
829
		return false;
830
	}
831
832
	// Send email with activation link.
833
	if ( !is_subdomain_install() || get_current_network_id() != 1 )
834
		$activate_url = network_site_url("wp-activate.php?key=$key");
835
	else
836
		$activate_url = "http://{$domain}{$path}wp-activate.php?key=$key"; // @todo use *_url() API
837
838
	$activate_url = esc_url($activate_url);
839
	$admin_email = get_site_option( 'admin_email' );
840
	if ( $admin_email == '' )
841
		$admin_email = 'support@' . $_SERVER['SERVER_NAME'];
842
	$from_name = get_site_option( 'site_name' ) == '' ? 'WordPress' : esc_html( get_site_option( 'site_name' ) );
843
	$message_headers = "From: \"{$from_name}\" <{$admin_email}>\n" . "Content-Type: text/plain; charset=\"" . get_option('blog_charset') . "\"\n";
844
845
	$user = get_user_by( 'login', $user_login );
846
	$switched_locale = switch_to_locale( get_user_locale( $user ) );
847
848
	$message = sprintf(
849
		/**
850
		 * Filters the message content of the new blog notification email.
851
		 *
852
		 * Content should be formatted for transmission via wp_mail().
853
		 *
854
		 * @since MU
855
		 *
856
		 * @param string $content    Content of the notification email.
857
		 * @param string $domain     Site domain.
858
		 * @param string $path       Site path.
859
		 * @param string $title      Site title.
860
		 * @param string $user_login User login name.
861
		 * @param string $user_email User email address.
862
		 * @param string $key        Activation key created in wpmu_signup_blog().
863
		 * @param array  $meta       Signup meta data. By default, contains the requested privacy setting and lang_id.
864
		 */
865
		apply_filters( 'wpmu_signup_blog_notification_email',
866
			__( "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" ),
867
			$domain, $path, $title, $user_login, $user_email, $key, $meta
868
		),
869
		$activate_url,
870
		esc_url( "http://{$domain}{$path}" ),
871
		$key
872
	);
873
	// TODO: Don't hard code activation link.
874
	$subject = sprintf(
875
		/**
876
		 * Filters the subject of the new blog notification email.
877
		 *
878
		 * @since MU
879
		 *
880
		 * @param string $subject    Subject of the notification email.
881
		 * @param string $domain     Site domain.
882
		 * @param string $path       Site path.
883
		 * @param string $title      Site title.
884
		 * @param string $user_login User login name.
885
		 * @param string $user_email User email address.
886
		 * @param string $key        Activation key created in wpmu_signup_blog().
887
		 * @param array  $meta       Signup meta data. By default, contains the requested privacy setting and lang_id.
888
		 */
889
		apply_filters( 'wpmu_signup_blog_notification_subject',
890
			/* translators: New site notification email subject. 1: Network name, 2: New site URL */
891
			_x( '[%1$s] Activate %2$s', 'New site notification email subject' ),
892
			$domain, $path, $title, $user_login, $user_email, $key, $meta
893
		),
894
		$from_name,
895
		esc_url( 'http://' . $domain . $path )
896
	);
897
	wp_mail( $user_email, wp_specialchars_decode( $subject ), $message, $message_headers );
898
899
	if ( $switched_locale ) {
900
		restore_previous_locale();
901
	}
902
903
	return true;
904
}
905
906
/**
907
 * Notify user of signup success.
908
 *
909
 * This is the notification function used when no new site has
910
 * been requested.
911
 *
912
 * Filter {@see 'wpmu_signup_user_notification'} to bypass this function or
913
 * replace it with your own notification behavior.
914
 *
915
 * Filter {@see 'wpmu_signup_user_notification_email'} and
916
 * {@see 'wpmu_signup_user_notification_subject'} to change the content
917
 * and subject line of the email sent to newly registered users.
918
 *
919
 * @since MU
920
 *
921
 * @param string $user_login The user's login name.
922
 * @param string $user_email The user's email address.
923
 * @param string $key        The activation key created in wpmu_signup_user()
924
 * @param array  $meta       Optional. Signup meta data. Default empty array.
925
 * @return bool
926
 */
927
function wpmu_signup_user_notification( $user_login, $user_email, $key, $meta = array() ) {
928
	/**
929
	 * Filters whether to bypass the email notification for new user sign-up.
930
	 *
931
	 * @since MU
932
	 *
933
	 * @param string $user_login User login name.
934
	 * @param string $user_email User email address.
935
	 * @param string $key        Activation key created in wpmu_signup_user().
936
	 * @param array  $meta       Signup meta data. Default empty array.
937
	 */
938
	if ( ! apply_filters( 'wpmu_signup_user_notification', $user_login, $user_email, $key, $meta ) )
939
		return false;
940
941
	$user = get_user_by( 'login', $user_login );
942
	$switched_locale = switch_to_locale( get_user_locale( $user ) );
943
944
	// Send email with activation link.
945
	$admin_email = get_site_option( 'admin_email' );
946
	if ( $admin_email == '' )
947
		$admin_email = 'support@' . $_SERVER['SERVER_NAME'];
948
	$from_name = get_site_option( 'site_name' ) == '' ? 'WordPress' : esc_html( get_site_option( 'site_name' ) );
949
	$message_headers = "From: \"{$from_name}\" <{$admin_email}>\n" . "Content-Type: text/plain; charset=\"" . get_option('blog_charset') . "\"\n";
950
	$message = sprintf(
951
		/**
952
		 * Filters the content of the notification email for new user sign-up.
953
		 *
954
		 * Content should be formatted for transmission via wp_mail().
955
		 *
956
		 * @since MU
957
		 *
958
		 * @param string $content    Content of the notification email.
959
		 * @param string $user_login User login name.
960
		 * @param string $user_email User email address.
961
		 * @param string $key        Activation key created in wpmu_signup_user().
962
		 * @param array  $meta       Signup meta data. Default empty array.
963
		 */
964
		apply_filters( 'wpmu_signup_user_notification_email',
965
			__( "To activate your user, please click the following link:\n\n%s\n\nAfter you activate, you will receive *another email* with your login." ),
966
			$user_login, $user_email, $key, $meta
967
		),
968
		site_url( "wp-activate.php?key=$key" )
969
	);
970
	// TODO: Don't hard code activation link.
971
	$subject = sprintf(
972
		/**
973
		 * Filters the subject of the notification email of new user signup.
974
		 *
975
		 * @since MU
976
		 *
977
		 * @param string $subject    Subject of the notification email.
978
		 * @param string $user_login User login name.
979
		 * @param string $user_email User email address.
980
		 * @param string $key        Activation key created in wpmu_signup_user().
981
		 * @param array  $meta       Signup meta data. Default empty array.
982
		 */
983
		apply_filters( 'wpmu_signup_user_notification_subject',
984
			/* translators: New user notification email subject. 1: Network name, 2: New user login */
985
			_x( '[%1$s] Activate %2$s', 'New user notification email subject' ),
986
			$user_login, $user_email, $key, $meta
987
		),
988
		$from_name,
989
		$user_login
990
	);
991
	wp_mail( $user_email, wp_specialchars_decode( $subject ), $message, $message_headers );
992
993
	if ( $switched_locale ) {
994
		restore_previous_locale();
995
	}
996
997
	return true;
998
}
999
1000
/**
1001
 * Activate a signup.
1002
 *
1003
 * Hook to {@see 'wpmu_activate_user'} or {@see 'wpmu_activate_blog'} for events
1004
 * that should happen only when users or sites are self-created (since
1005
 * those actions are not called when users and sites are created
1006
 * by a Super Admin).
1007
 *
1008
 * @since MU
1009
 *
1010
 * @global wpdb $wpdb WordPress database abstraction object.
1011
 *
1012
 * @param string $key The activation key provided to the user.
1013
 * @return array|WP_Error An array containing information about the activated user and/or blog
0 ignored issues
show
Should the return type not be WP_Error|array|integer?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
1014
 */
1015
function wpmu_activate_signup($key) {
1016
	global $wpdb;
1017
1018
	$signup = $wpdb->get_row( $wpdb->prepare("SELECT * FROM $wpdb->signups WHERE activation_key = %s", $key) );
1019
1020
	if ( empty( $signup ) )
1021
		return new WP_Error( 'invalid_key', __( 'Invalid activation key.' ) );
1022
1023
	if ( $signup->active ) {
1024
		if ( empty( $signup->domain ) )
1025
			return new WP_Error( 'already_active', __( 'The user is already active.' ), $signup );
1026
		else
1027
			return new WP_Error( 'already_active', __( 'The site is already active.' ), $signup );
1028
	}
1029
1030
	$meta = maybe_unserialize($signup->meta);
1031
	$password = wp_generate_password( 12, false );
1032
1033
	$user_id = username_exists($signup->user_login);
1034
1035
	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...
1036
		$user_id = wpmu_create_user($signup->user_login, $password, $signup->user_email);
1037
	else
1038
		$user_already_exists = true;
1039
1040
	if ( ! $user_id )
1041
		return new WP_Error('create_user', __('Could not create user'), $signup);
1042
1043
	$now = current_time('mysql', true);
1044
1045
	if ( empty($signup->domain) ) {
1046
		$wpdb->update( $wpdb->signups, array('active' => 1, 'activated' => $now), array('activation_key' => $key) );
1047
1048
		if ( isset( $user_already_exists ) )
1049
			return new WP_Error( 'user_already_exists', __( 'That username is already activated.' ), $signup);
1050
1051
		/**
1052
		 * Fires immediately after a new user is activated.
1053
		 *
1054
		 * @since MU
1055
		 *
1056
		 * @param int   $user_id  User ID.
1057
		 * @param int   $password User password.
1058
		 * @param array $meta     Signup meta data.
1059
		 */
1060
		do_action( 'wpmu_activate_user', $user_id, $password, $meta );
1061
		return array( 'user_id' => $user_id, 'password' => $password, 'meta' => $meta );
1062
	}
1063
1064
	$blog_id = wpmu_create_blog( $signup->domain, $signup->path, $signup->title, $user_id, $meta, $wpdb->siteid );
0 ignored issues
show
It seems like $user_id can also be of type object<WP_Error>; however, wpmu_create_blog() does only seem to accept integer, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
1065
1066
	// TODO: What to do if we create a user but cannot create a blog?
1067
	if ( is_wp_error($blog_id) ) {
1068
		// If blog is taken, that means a previous attempt to activate this blog failed in between creating the blog and
1069
		// setting the activation flag. Let's just set the active flag and instruct the user to reset their password.
1070
		if ( 'blog_taken' == $blog_id->get_error_code() ) {
1071
			$blog_id->add_data( $signup );
1072
			$wpdb->update( $wpdb->signups, array( 'active' => 1, 'activated' => $now ), array( 'activation_key' => $key ) );
1073
		}
1074
		return $blog_id;
1075
	}
1076
1077
	$wpdb->update( $wpdb->signups, array('active' => 1, 'activated' => $now), array('activation_key' => $key) );
1078
	/**
1079
	 * Fires immediately after a site is activated.
1080
	 *
1081
	 * @since MU
1082
	 *
1083
	 * @param int    $blog_id       Blog ID.
1084
	 * @param int    $user_id       User ID.
1085
	 * @param int    $password      User password.
1086
	 * @param string $signup_title  Site title.
1087
	 * @param array  $meta          Signup meta data. By default, contains the requested privacy setting and lang_id.
1088
	 */
1089
	do_action( 'wpmu_activate_blog', $blog_id, $user_id, $password, $signup->title, $meta );
1090
1091
	return array('blog_id' => $blog_id, 'user_id' => $user_id, 'password' => $password, 'title' => $signup->title, 'meta' => $meta);
1092
}
1093
1094
/**
1095
 * Create a user.
1096
 *
1097
 * This function runs when a user self-registers as well as when
1098
 * a Super Admin creates a new user. Hook to {@see 'wpmu_new_user'} for events
1099
 * that should affect all new users, but only on Multisite (otherwise
1100
 * use {@see'user_register'}).
1101
 *
1102
 * @since MU
1103
 *
1104
 * @param string $user_name The new user's login name.
1105
 * @param string $password  The new user's password.
1106
 * @param string $email     The new user's email address.
1107
 * @return int|false Returns false on failure, or int $user_id on success
0 ignored issues
show
Should the return type not be false|WP_Error|integer?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
1108
 */
1109
function wpmu_create_user( $user_name, $password, $email ) {
1110
	$user_name = preg_replace( '/\s+/', '', sanitize_user( $user_name, true ) );
1111
1112
	$user_id = wp_create_user( $user_name, $password, $email );
1113
	if ( is_wp_error( $user_id ) )
1114
		return false;
1115
1116
	// Newly created users have no roles or caps until they are added to a blog.
1117
	delete_user_option( $user_id, 'capabilities' );
1118
	delete_user_option( $user_id, 'user_level' );
1119
1120
	/**
1121
	 * Fires immediately after a new user is created.
1122
	 *
1123
	 * @since MU
1124
	 *
1125
	 * @param int $user_id User ID.
1126
	 */
1127
	do_action( 'wpmu_new_user', $user_id );
1128
1129
	return $user_id;
1130
}
1131
1132
/**
1133
 * Create a site.
1134
 *
1135
 * This function runs when a user self-registers a new site as well
1136
 * as when a Super Admin creates a new site. Hook to {@see 'wpmu_new_blog'}
1137
 * for events that should affect all new sites.
1138
 *
1139
 * On subdirectory installs, $domain is the same as the main site's
1140
 * domain, and the path is the subdirectory name (eg 'example.com'
1141
 * and '/blog1/'). On subdomain installs, $domain is the new subdomain +
1142
 * root domain (eg 'blog1.example.com'), and $path is '/'.
1143
 *
1144
 * @since MU
1145
 *
1146
 * @param string $domain  The new site's domain.
1147
 * @param string $path    The new site's path.
1148
 * @param string $title   The new site's title.
1149
 * @param int    $user_id The user ID of the new site's admin.
1150
 * @param array  $meta    Optional. Used to set initial site options.
1151
 * @param int    $site_id Optional. Only relevant on multi-network installs.
1152
 * @return int|WP_Error Returns WP_Error object on failure, int $blog_id on success
1153
 */
1154
function wpmu_create_blog( $domain, $path, $title, $user_id, $meta = array(), $site_id = 1 ) {
1155
	$defaults = array(
1156
		'public' => 0,
1157
		'WPLANG' => get_site_option( 'WPLANG' ),
1158
	);
1159
	$meta = wp_parse_args( $meta, $defaults );
1160
1161
	$domain = preg_replace( '/\s+/', '', sanitize_user( $domain, true ) );
1162
1163
	if ( is_subdomain_install() )
1164
		$domain = str_replace( '@', '', $domain );
1165
1166
	$title = strip_tags( $title );
1167
	$user_id = (int) $user_id;
1168
1169
	if ( empty($path) )
1170
		$path = '/';
1171
1172
	// Check if the domain has been used already. We should return an error message.
1173
	if ( domain_exists($domain, $path, $site_id) )
1174
		return new WP_Error( 'blog_taken', __( 'Sorry, that site already exists!' ) );
1175
1176
	if ( ! wp_installing() ) {
1177
		wp_installing( true );
1178
	}
1179
1180
	if ( ! $blog_id = insert_blog($domain, $path, $site_id) )
1181
		return new WP_Error('insert_blog', __('Could not create site.'));
1182
1183
	switch_to_blog($blog_id);
1184
	install_blog($blog_id, $title);
1185
	wp_install_defaults($user_id);
1186
1187
	add_user_to_blog($blog_id, $user_id, 'administrator');
1188
1189
	foreach ( $meta as $key => $value ) {
1190
		if ( in_array( $key, array( 'public', 'archived', 'mature', 'spam', 'deleted', 'lang_id' ) ) )
1191
			update_blog_status( $blog_id, $key, $value );
1192
		else
1193
			update_option( $key, $value );
1194
	}
1195
1196
	update_option( 'blog_public', (int) $meta['public'] );
1197
1198
	if ( ! is_super_admin( $user_id ) && ! get_user_meta( $user_id, 'primary_blog', true ) )
1199
		update_user_meta( $user_id, 'primary_blog', $blog_id );
1200
1201
	restore_current_blog();
1202
	/**
1203
	 * Fires immediately after a new site is created.
1204
	 *
1205
	 * @since MU
1206
	 *
1207
	 * @param int    $blog_id Blog ID.
1208
	 * @param int    $user_id User ID.
1209
	 * @param string $domain  Site domain.
1210
	 * @param string $path    Site path.
1211
	 * @param int    $site_id Site ID. Only relevant on multi-network installs.
1212
	 * @param array  $meta    Meta data. Used to set initial site options.
1213
	 */
1214
	do_action( 'wpmu_new_blog', $blog_id, $user_id, $domain, $path, $site_id, $meta );
1215
1216
	wp_cache_set( 'last_changed', microtime(), 'sites' );
1217
1218
	return $blog_id;
1219
}
1220
1221
/**
1222
 * Notifies the network admin that a new site has been activated.
1223
 *
1224
 * Filter {@see 'newblog_notify_siteadmin'} to change the content of
1225
 * the notification email.
1226
 *
1227
 * @since MU
1228
 *
1229
 * @param int    $blog_id    The new site's ID.
1230
 * @param string $deprecated Not used.
1231
 * @return bool
1232
 */
1233
function newblog_notify_siteadmin( $blog_id, $deprecated = '' ) {
0 ignored issues
show
The parameter $deprecated is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
1234
	if ( get_site_option( 'registrationnotification' ) != 'yes' )
1235
		return false;
1236
1237
	$email = get_site_option( 'admin_email' );
1238
	if ( is_email($email) == false )
1239
		return false;
1240
1241
	$options_site_url = esc_url(network_admin_url('settings.php'));
1242
1243
	switch_to_blog( $blog_id );
1244
	$blogname = get_option( 'blogname' );
1245
	$siteurl = site_url();
1246
	restore_current_blog();
1247
1248
	/* translators: New site notification email. 1: Site URL, 2: User IP address, 3: Settings screen URL */
1249
	$msg = sprintf( __( 'New Site: %1$s
1250
URL: %2$s
1251
Remote IP: %3$s
1252
1253
Disable these notifications: %4$s' ), $blogname, $siteurl, wp_unslash( $_SERVER['REMOTE_ADDR'] ), $options_site_url);
1254
	/**
1255
	 * Filters the message body of the new site activation email sent
1256
	 * to the network administrator.
1257
	 *
1258
	 * @since MU
1259
	 *
1260
	 * @param string $msg Email body.
1261
	 */
1262
	$msg = apply_filters( 'newblog_notify_siteadmin', $msg );
1263
1264
	wp_mail( $email, sprintf( __( 'New Site Registration: %s' ), $siteurl ), $msg );
1265
	return true;
1266
}
1267
1268
/**
1269
 * Notifies the network admin that a new user has been activated.
1270
 *
1271
 * Filter {@see 'newuser_notify_siteadmin'} to change the content of
1272
 * the notification email.
1273
 *
1274
 * @since MU
1275
 *
1276
 * @param int $user_id The new user's ID.
1277
 * @return bool
1278
 */
1279
function newuser_notify_siteadmin( $user_id ) {
1280
	if ( get_site_option( 'registrationnotification' ) != 'yes' )
1281
		return false;
1282
1283
	$email = get_site_option( 'admin_email' );
1284
1285
	if ( is_email($email) == false )
1286
		return false;
1287
1288
	$user = get_userdata( $user_id );
1289
1290
	$options_site_url = esc_url(network_admin_url('settings.php'));
1291
	/* translators: New user notification email. 1: User login, 2: User IP address, 3: Settings screen URL */
1292
	$msg = sprintf(__('New User: %1$s
1293
Remote IP: %2$s
1294
1295
Disable these notifications: %3$s'), $user->user_login, wp_unslash( $_SERVER['REMOTE_ADDR'] ), $options_site_url);
1296
1297
	/**
1298
	 * Filters the message body of the new user activation email sent
1299
	 * to the network administrator.
1300
	 *
1301
	 * @since MU
1302
	 *
1303
	 * @param string  $msg  Email body.
1304
	 * @param WP_User $user WP_User instance of the new user.
1305
	 */
1306
	$msg = apply_filters( 'newuser_notify_siteadmin', $msg, $user );
1307
	wp_mail( $email, sprintf(__('New User Registration: %s'), $user->user_login), $msg );
1308
	return true;
1309
}
1310
1311
/**
1312
 * Check whether a blogname is already taken.
1313
 *
1314
 * Used during the new site registration process to ensure
1315
 * that each blogname is unique.
1316
 *
1317
 * @since MU
1318
 *
1319
 * @global wpdb $wpdb WordPress database abstraction object.
1320
 *
1321
 * @param string $domain  The domain to be checked.
1322
 * @param string $path    The path to be checked.
1323
 * @param int    $site_id Optional. Relevant only on multi-network installs.
1324
 * @return int
1325
 */
1326
function domain_exists($domain, $path, $site_id = 1) {
1327
	$path = trailingslashit( $path );
1328
	$args = array(
1329
		'network_id' => $site_id,
1330
		'domain' => $domain,
1331
		'path' => $path,
1332
		'fields' => 'ids',
1333
	);
1334
	$result = get_sites( $args );
1335
	$result = array_shift( $result );
1336
1337
	/**
1338
	 * Filters whether a blogname is taken.
1339
	 *
1340
	 * @since 3.5.0
1341
	 *
1342
	 * @param int|null $result  The blog_id if the blogname exists, null otherwise.
1343
	 * @param string   $domain  Domain to be checked.
1344
	 * @param string   $path    Path to be checked.
1345
	 * @param int      $site_id Site ID. Relevant only on multi-network installs.
1346
	 */
1347
	return apply_filters( 'domain_exists', $result, $domain, $path, $site_id );
1348
}
1349
1350
/**
1351
 * Store basic site info in the blogs table.
1352
 *
1353
 * This function creates a row in the wp_blogs table and returns
1354
 * the new blog's ID. It is the first step in creating a new blog.
1355
 *
1356
 * @since MU
1357
 *
1358
 * @global wpdb $wpdb WordPress database abstraction object.
1359
 *
1360
 * @param string $domain  The domain of the new site.
1361
 * @param string $path    The path of the new site.
1362
 * @param int    $site_id Unless you're running a multi-network install, be sure to set this value to 1.
1363
 * @return int|false The ID of the new row
1364
 */
1365
function insert_blog($domain, $path, $site_id) {
1366
	global $wpdb;
1367
1368
	$path = trailingslashit($path);
1369
	$site_id = (int) $site_id;
1370
1371
	$result = $wpdb->insert( $wpdb->blogs, array('site_id' => $site_id, 'domain' => $domain, 'path' => $path, 'registered' => current_time('mysql')) );
1372
	if ( ! $result )
1373
		return false;
1374
1375
	$blog_id = $wpdb->insert_id;
1376
	refresh_blog_details( $blog_id );
1377
1378
	wp_maybe_update_network_site_counts( $site_id );
1379
1380
	return $blog_id;
1381
}
1382
1383
/**
1384
 * Install an empty blog.
1385
 *
1386
 * Creates the new blog tables and options. If calling this function
1387
 * directly, be sure to use switch_to_blog() first, so that $wpdb
1388
 * points to the new blog.
1389
 *
1390
 * @since MU
1391
 *
1392
 * @global wpdb     $wpdb
1393
 * @global WP_Roles $wp_roles
1394
 *
1395
 * @param int    $blog_id    The value returned by insert_blog().
1396
 * @param string $blog_title The title of the new site.
1397
 */
1398
function install_blog( $blog_id, $blog_title = '' ) {
1399
	global $wpdb, $wp_roles;
1400
1401
	// Cast for security
1402
	$blog_id = (int) $blog_id;
1403
1404
	require_once( ABSPATH . 'wp-admin/includes/upgrade.php' );
1405
1406
	$suppress = $wpdb->suppress_errors();
1407
	if ( $wpdb->get_results( "DESCRIBE {$wpdb->posts}" ) )
1408
		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>' );
0 ignored issues
show
Coding Style Compatibility introduced by
The function install_blog() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
1409
	$wpdb->suppress_errors( $suppress );
1410
1411
	$url = get_blogaddress_by_id( $blog_id );
1412
1413
	// Set everything up
1414
	make_db_current_silent( 'blog' );
1415
	populate_options();
1416
	populate_roles();
1417
1418
	// populate_roles() clears previous role definitions so we start over.
1419
	$wp_roles = new WP_Roles();
1420
1421
	$siteurl = $home = untrailingslashit( $url );
1422
1423
	if ( ! is_subdomain_install() ) {
1424
1425
 		if ( 'https' === parse_url( get_site_option( 'siteurl' ), PHP_URL_SCHEME ) ) {
1426
 			$siteurl = set_url_scheme( $siteurl, 'https' );
1427
 		}
1428
 		if ( 'https' === parse_url( get_home_url( get_network()->site_id ), PHP_URL_SCHEME ) ) {
1429
 			$home = set_url_scheme( $home, 'https' );
1430
 		}
1431
1432
	}
1433
1434
	update_option( 'siteurl', $siteurl );
1435
	update_option( 'home', $home );
1436
1437
	if ( get_site_option( 'ms_files_rewriting' ) )
1438
		update_option( 'upload_path', UPLOADBLOGSDIR . "/$blog_id/files" );
1439
	else
1440
		update_option( 'upload_path', get_blog_option( get_network()->site_id, 'upload_path' ) );
1441
1442
	update_option( 'blogname', wp_unslash( $blog_title ) );
1443
	update_option( 'admin_email', '' );
1444
1445
	// remove all perms
1446
	$table_prefix = $wpdb->get_blog_prefix();
1447
	delete_metadata( 'user', 0, $table_prefix . 'user_level',   null, true ); // delete all
1448
	delete_metadata( 'user', 0, $table_prefix . 'capabilities', null, true ); // delete all
1449
}
1450
1451
/**
1452
 * Set blog defaults.
1453
 *
1454
 * This function creates a row in the wp_blogs table.
1455
 *
1456
 * @since MU
1457
 * @deprecated MU
1458
 * @deprecated Use wp_install_defaults()
1459
 *
1460
 * @global wpdb $wpdb WordPress database abstraction object.
1461
 *
1462
 * @param int $blog_id Ignored in this function.
1463
 * @param int $user_id
1464
 */
1465
function install_blog_defaults($blog_id, $user_id) {
1466
	global $wpdb;
1467
1468
	require_once( ABSPATH . 'wp-admin/includes/upgrade.php' );
1469
1470
	$suppress = $wpdb->suppress_errors();
1471
1472
	wp_install_defaults($user_id);
1473
1474
	$wpdb->suppress_errors( $suppress );
1475
}
1476
1477
/**
1478
 * Notify a user that their blog activation has been successful.
1479
 *
1480
 * Filter {@see 'wpmu_welcome_notification'} to disable or bypass.
1481
 *
1482
 * Filter {@see 'update_welcome_email'} and {@see 'update_welcome_subject'} to
1483
 * modify the content and subject line of the notification email.
1484
 *
1485
 * @since MU
1486
 *
1487
 * @param int    $blog_id  Blog ID.
1488
 * @param int    $user_id  User ID.
1489
 * @param string $password User password.
1490
 * @param string $title    Site title.
1491
 * @param array  $meta     Optional. Signup meta data. By default, contains the requested privacy setting and lang_id.
1492
 * @return bool
1493
 */
1494
function wpmu_welcome_notification( $blog_id, $user_id, $password, $title, $meta = array() ) {
1495
	$current_network = get_network();
1496
1497
	/**
1498
	 * Filters whether to bypass the welcome email after site activation.
1499
	 *
1500
	 * Returning false disables the welcome email.
1501
	 *
1502
	 * @since MU
1503
	 *
1504
	 * @param int|bool $blog_id  Blog ID.
1505
	 * @param int      $user_id  User ID.
1506
	 * @param string   $password User password.
1507
	 * @param string   $title    Site title.
1508
	 * @param array    $meta     Signup meta data. By default, contains the requested privacy setting and lang_id.
1509
	 */
1510
	if ( ! apply_filters( 'wpmu_welcome_notification', $blog_id, $user_id, $password, $title, $meta ) )
1511
		return false;
1512
1513
	$user = get_userdata( $user_id );
1514
1515
	$switched_locale = switch_to_locale( get_user_locale( $user ) );
1516
1517
	$welcome_email = get_site_option( 'welcome_email' );
1518
	if ( $welcome_email == false ) {
1519
		/* translators: Do not translate USERNAME, SITE_NAME, BLOG_URL, PASSWORD: those are placeholders. */
1520
		$welcome_email = __( 'Howdy USERNAME,
1521
1522
Your new SITE_NAME site has been successfully set up at:
1523
BLOG_URL
1524
1525
You can log in to the administrator account with the following information:
1526
1527
Username: USERNAME
1528
Password: PASSWORD
1529
Log in here: BLOG_URLwp-login.php
1530
1531
We hope you enjoy your new site. Thanks!
1532
1533
--The Team @ SITE_NAME' );
1534
	}
1535
1536
	$url = get_blogaddress_by_id($blog_id);
1537
1538
	$welcome_email = str_replace( 'SITE_NAME', $current_network->site_name, $welcome_email );
1539
	$welcome_email = str_replace( 'BLOG_TITLE', $title, $welcome_email );
1540
	$welcome_email = str_replace( 'BLOG_URL', $url, $welcome_email );
1541
	$welcome_email = str_replace( 'USERNAME', $user->user_login, $welcome_email );
1542
	$welcome_email = str_replace( 'PASSWORD', $password, $welcome_email );
1543
1544
	/**
1545
	 * Filters the content of the welcome email after site activation.
1546
	 *
1547
	 * Content should be formatted for transmission via wp_mail().
1548
	 *
1549
	 * @since MU
1550
	 *
1551
	 * @param string $welcome_email Message body of the email.
1552
	 * @param int    $blog_id       Blog ID.
1553
	 * @param int    $user_id       User ID.
1554
	 * @param string $password      User password.
1555
	 * @param string $title         Site title.
1556
	 * @param array  $meta          Signup meta data. By default, contains the requested privacy setting and lang_id.
1557
	 */
1558
	$welcome_email = apply_filters( 'update_welcome_email', $welcome_email, $blog_id, $user_id, $password, $title, $meta );
1559
	$admin_email = get_site_option( 'admin_email' );
1560
1561
	if ( $admin_email == '' )
1562
		$admin_email = 'support@' . $_SERVER['SERVER_NAME'];
1563
1564
	$from_name = get_site_option( 'site_name' ) == '' ? 'WordPress' : esc_html( get_site_option( 'site_name' ) );
1565
	$message_headers = "From: \"{$from_name}\" <{$admin_email}>\n" . "Content-Type: text/plain; charset=\"" . get_option('blog_charset') . "\"\n";
1566
	$message = $welcome_email;
1567
1568
	if ( empty( $current_network->site_name ) )
1569
		$current_network->site_name = 'WordPress';
1570
1571
	/* translators: New site notification email subject. 1: Network name, 2: New site name */
1572
	$subject = __( 'New %1$s Site: %2$s' );
1573
1574
	/**
1575
	 * Filters the subject of the welcome email after site activation.
1576
	 *
1577
	 * @since MU
1578
	 *
1579
	 * @param string $subject Subject of the email.
1580
	 */
1581
	$subject = apply_filters( 'update_welcome_subject', sprintf( $subject, $current_network->site_name, wp_unslash( $title ) ) );
1582
	wp_mail( $user->user_email, wp_specialchars_decode( $subject ), $message, $message_headers );
1583
1584
	if ( $switched_locale ) {
1585
		restore_previous_locale();
1586
	}
1587
1588
	return true;
1589
}
1590
1591
/**
1592
 * Notify a user that their account activation has been successful.
1593
 *
1594
 * Filter {@see 'wpmu_welcome_user_notification'} to disable or bypass.
1595
 *
1596
 * Filter {@see 'update_welcome_user_email'} and {@see 'update_welcome_user_subject'} to
1597
 * modify the content and subject line of the notification email.
1598
 *
1599
 * @since MU
1600
 *
1601
 * @param int    $user_id  User ID.
1602
 * @param string $password User password.
1603
 * @param array  $meta     Optional. Signup meta data. Default empty array.
1604
 * @return bool
1605
 */
1606
function wpmu_welcome_user_notification( $user_id, $password, $meta = array() ) {
1607
	$current_network = get_network();
1608
1609
	/**
1610
 	 * Filters whether to bypass the welcome email after user activation.
1611
	 *
1612
	 * Returning false disables the welcome email.
1613
	 *
1614
	 * @since MU
1615
	 *
1616
	 * @param int    $user_id  User ID.
1617
	 * @param string $password User password.
1618
	 * @param array  $meta     Signup meta data. Default empty array.
1619
	 */
1620
	if ( ! apply_filters( 'wpmu_welcome_user_notification', $user_id, $password, $meta ) )
1621
		return false;
1622
1623
	$welcome_email = get_site_option( 'welcome_user_email' );
1624
1625
	$user = get_userdata( $user_id );
1626
1627
	$switched_locale = switch_to_locale( get_user_locale( $user ) );
1628
1629
	/**
1630
	 * Filters the content of the welcome email after user activation.
1631
	 *
1632
	 * Content should be formatted for transmission via wp_mail().
1633
	 *
1634
	 * @since MU
1635
	 *
1636
	 * @param string $welcome_email The message body of the account activation success email.
1637
	 * @param int    $user_id       User ID.
1638
	 * @param string $password      User password.
1639
	 * @param array  $meta          Signup meta data. Default empty array.
1640
	 */
1641
	$welcome_email = apply_filters( 'update_welcome_user_email', $welcome_email, $user_id, $password, $meta );
1642
	$welcome_email = str_replace( 'SITE_NAME', $current_network->site_name, $welcome_email );
1643
	$welcome_email = str_replace( 'USERNAME', $user->user_login, $welcome_email );
1644
	$welcome_email = str_replace( 'PASSWORD', $password, $welcome_email );
1645
	$welcome_email = str_replace( 'LOGINLINK', wp_login_url(), $welcome_email );
1646
1647
	$admin_email = get_site_option( 'admin_email' );
1648
1649
	if ( $admin_email == '' )
1650
		$admin_email = 'support@' . $_SERVER['SERVER_NAME'];
1651
1652
	$from_name = get_site_option( 'site_name' ) == '' ? 'WordPress' : esc_html( get_site_option( 'site_name' ) );
1653
	$message_headers = "From: \"{$from_name}\" <{$admin_email}>\n" . "Content-Type: text/plain; charset=\"" . get_option('blog_charset') . "\"\n";
1654
	$message = $welcome_email;
1655
1656
	if ( empty( $current_network->site_name ) )
1657
		$current_network->site_name = 'WordPress';
1658
1659
	/* translators: New user notification email subject. 1: Network name, 2: New user login */
1660
	$subject = __( 'New %1$s User: %2$s' );
1661
1662
	/**
1663
	 * Filters the subject of the welcome email after user activation.
1664
	 *
1665
	 * @since MU
1666
	 *
1667
	 * @param string $subject Subject of the email.
1668
	 */
1669
	$subject = apply_filters( 'update_welcome_user_subject', sprintf( $subject, $current_network->site_name, $user->user_login) );
1670
	wp_mail( $user->user_email, wp_specialchars_decode( $subject ), $message, $message_headers );
1671
1672
	if ( $switched_locale ) {
1673
		restore_previous_locale();
1674
	}
1675
1676
	return true;
1677
}
1678
1679
/**
1680
 * Get the current network.
1681
 *
1682
 * Returns an object containing the 'id', 'domain', 'path', and 'site_name'
1683
 * properties of the network being viewed.
1684
 *
1685
 * @see wpmu_current_site()
1686
 *
1687
 * @since MU
1688
 *
1689
 * @global WP_Network $current_site
1690
 *
1691
 * @return WP_Network
1692
 */
1693
function get_current_site() {
1694
	global $current_site;
1695
	return $current_site;
1696
}
1697
1698
/**
1699
 * Get a user's most recent post.
1700
 *
1701
 * Walks through each of a user's blogs to find the post with
1702
 * the most recent post_date_gmt.
1703
 *
1704
 * @since MU
1705
 *
1706
 * @global wpdb $wpdb WordPress database abstraction object.
1707
 *
1708
 * @param int $user_id
1709
 * @return array Contains the blog_id, post_id, post_date_gmt, and post_gmt_ts
1710
 */
1711
function get_most_recent_post_of_user( $user_id ) {
1712
	global $wpdb;
1713
1714
	$user_blogs = get_blogs_of_user( (int) $user_id );
1715
	$most_recent_post = array();
1716
1717
	// Walk through each blog and get the most recent post
1718
	// published by $user_id
1719
	foreach ( (array) $user_blogs as $blog ) {
1720
		$prefix = $wpdb->get_blog_prefix( $blog->userblog_id );
1721
		$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);
1722
1723
		// Make sure we found a post
1724
		if ( isset($recent_post['ID']) ) {
1725
			$post_gmt_ts = strtotime($recent_post['post_date_gmt']);
1726
1727
			// If this is the first post checked or if this post is
1728
			// newer than the current recent post, make it the new
1729
			// most recent post.
1730
			if ( !isset($most_recent_post['post_gmt_ts']) || ( $post_gmt_ts > $most_recent_post['post_gmt_ts'] ) ) {
1731
				$most_recent_post = array(
1732
					'blog_id'		=> $blog->userblog_id,
1733
					'post_id'		=> $recent_post['ID'],
1734
					'post_date_gmt'	=> $recent_post['post_date_gmt'],
1735
					'post_gmt_ts'	=> $post_gmt_ts
1736
				);
1737
			}
1738
		}
1739
	}
1740
1741
	return $most_recent_post;
1742
}
1743
1744
// Misc functions
1745
1746
/**
1747
 * Get the size of a directory.
1748
 *
1749
 * A helper function that is used primarily to check whether
1750
 * a blog has exceeded its allowed upload space.
1751
 *
1752
 * @since MU
1753
 *
1754
 * @param string $directory Full path of a directory.
1755
 * @return int Size of the directory in MB.
1756
 */
1757
function get_dirsize( $directory ) {
1758
	$dirsize = get_transient( 'dirsize_cache' );
1759
	if ( is_array( $dirsize ) && isset( $dirsize[ $directory ][ 'size' ] ) )
1760
		return $dirsize[ $directory ][ 'size' ];
1761
1762
	if ( ! is_array( $dirsize ) )
1763
		$dirsize = array();
1764
1765
	// Exclude individual site directories from the total when checking the main site,
1766
	// as they are subdirectories and should not be counted.
1767
	if ( is_main_site() ) {
1768
		$dirsize[ $directory ][ 'size' ] = recurse_dirsize( $directory, $directory . '/sites' );
1769
	} else {
1770
		$dirsize[ $directory ][ 'size' ] = recurse_dirsize( $directory );
1771
	}
1772
1773
	set_transient( 'dirsize_cache', $dirsize, HOUR_IN_SECONDS );
1774
	return $dirsize[ $directory ][ 'size' ];
1775
}
1776
1777
/**
1778
 * Get the size of a directory recursively.
1779
 *
1780
 * Used by get_dirsize() to get a directory's size when it contains
1781
 * other directories.
1782
 *
1783
 * @since MU
1784
 * @since 4.3.0 $exclude parameter added.
1785
 *
1786
 * @param string $directory Full path of a directory.
1787
 * @param string $exclude   Optional. Full path of a subdirectory to exclude from the total.
1788
 * @return int|false Size in MB if a valid directory. False if not.
1789
 */
1790
function recurse_dirsize( $directory, $exclude = null ) {
1791
	$size = 0;
1792
1793
	$directory = untrailingslashit( $directory );
1794
1795
	if ( ! file_exists( $directory ) || ! is_dir( $directory ) || ! is_readable( $directory ) || $directory === $exclude ) {
1796
		return false;
1797
	}
1798
1799
	if ($handle = opendir($directory)) {
1800
		while(($file = readdir($handle)) !== false) {
1801
			$path = $directory.'/'.$file;
1802
			if ($file != '.' && $file != '..') {
1803
				if (is_file($path)) {
1804
					$size += filesize($path);
1805
				} elseif (is_dir($path)) {
1806
					$handlesize = recurse_dirsize( $path, $exclude );
1807
					if ($handlesize > 0)
1808
						$size += $handlesize;
1809
				}
1810
			}
1811
		}
1812
		closedir($handle);
1813
	}
1814
	return $size;
1815
}
1816
1817
/**
1818
 * Check an array of MIME types against a whitelist.
1819
 *
1820
 * WordPress ships with a set of allowed upload filetypes,
1821
 * which is defined in wp-includes/functions.php in
1822
 * get_allowed_mime_types(). This function is used to filter
1823
 * that list against the filetype whitelist provided by Multisite
1824
 * Super Admins at wp-admin/network/settings.php.
1825
 *
1826
 * @since MU
1827
 *
1828
 * @param array $mimes
1829
 * @return array
1830
 */
1831
function check_upload_mimes( $mimes ) {
1832
	$site_exts = explode( ' ', get_site_option( 'upload_filetypes', 'jpg jpeg png gif' ) );
1833
	$site_mimes = array();
1834
	foreach ( $site_exts as $ext ) {
1835
		foreach ( $mimes as $ext_pattern => $mime ) {
1836
			if ( $ext != '' && strpos( $ext_pattern, $ext ) !== false )
1837
				$site_mimes[$ext_pattern] = $mime;
1838
		}
1839
	}
1840
	return $site_mimes;
1841
}
1842
1843
/**
1844
 * Update a blog's post count.
1845
 *
1846
 * WordPress MS stores a blog's post count as an option so as
1847
 * to avoid extraneous COUNTs when a blog's details are fetched
1848
 * with get_site(). This function is called when posts are published
1849
 * or unpublished to make sure the count stays current.
1850
 *
1851
 * @since MU
1852
 *
1853
 * @global wpdb $wpdb WordPress database abstraction object.
1854
 *
1855
 * @param string $deprecated Not used.
1856
 */
1857
function update_posts_count( $deprecated = '' ) {
0 ignored issues
show
The parameter $deprecated is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
1858
	global $wpdb;
1859
	update_option( 'post_count', (int) $wpdb->get_var( "SELECT COUNT(ID) FROM {$wpdb->posts} WHERE post_status = 'publish' and post_type = 'post'" ) );
1860
}
1861
1862
/**
1863
 * Logs user registrations.
1864
 *
1865
 * @since MU
1866
 *
1867
 * @global wpdb $wpdb WordPress database abstraction object.
1868
 *
1869
 * @param int $blog_id
1870
 * @param int $user_id
1871
 */
1872
function wpmu_log_new_registrations( $blog_id, $user_id ) {
1873
	global $wpdb;
1874
	$user = get_userdata( (int) $user_id );
1875
	if ( $user )
1876
		$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')) );
1877
}
1878
1879
/**
1880
 * Maintains a canonical list of terms by syncing terms created for each blog with the global terms table.
1881
 *
1882
 * @since 3.0.0
1883
 *
1884
 * @see term_id_filter
1885
 *
1886
 * @global wpdb $wpdb WordPress database abstraction object.
1887
 * @staticvar int $global_terms_recurse
1888
 *
1889
 * @param int    $term_id    An ID for a term on the current blog.
1890
 * @param string $deprecated Not used.
1891
 * @return int An ID from the global terms table mapped from $term_id.
1892
 */
1893
function global_terms( $term_id, $deprecated = '' ) {
0 ignored issues
show
The parameter $deprecated is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
1894
	global $wpdb;
1895
	static $global_terms_recurse = null;
1896
1897
	if ( !global_terms_enabled() )
1898
		return $term_id;
1899
1900
	// prevent a race condition
1901
	$recurse_start = false;
1902
	if ( $global_terms_recurse === null ) {
1903
		$recurse_start = true;
1904
		$global_terms_recurse = 1;
1905
	} elseif ( 10 < $global_terms_recurse++ ) {
1906
		return $term_id;
1907
	}
1908
1909
	$term_id = intval( $term_id );
1910
	$c = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM $wpdb->terms WHERE term_id = %d", $term_id ) );
1911
1912
	$global_id = $wpdb->get_var( $wpdb->prepare( "SELECT cat_ID FROM $wpdb->sitecategories WHERE category_nicename = %s", $c->slug ) );
1913
	if ( $global_id == null ) {
1914
		$used_global_id = $wpdb->get_var( $wpdb->prepare( "SELECT cat_ID FROM $wpdb->sitecategories WHERE cat_ID = %d", $c->term_id ) );
1915
		if ( null == $used_global_id ) {
1916
			$wpdb->insert( $wpdb->sitecategories, array( 'cat_ID' => $term_id, 'cat_name' => $c->name, 'category_nicename' => $c->slug ) );
1917
			$global_id = $wpdb->insert_id;
1918
			if ( empty( $global_id ) )
1919
				return $term_id;
1920
		} else {
1921
			$max_global_id = $wpdb->get_var( "SELECT MAX(cat_ID) FROM $wpdb->sitecategories" );
1922
			$max_local_id = $wpdb->get_var( "SELECT MAX(term_id) FROM $wpdb->terms" );
1923
			$new_global_id = max( $max_global_id, $max_local_id ) + mt_rand( 100, 400 );
1924
			$wpdb->insert( $wpdb->sitecategories, array( 'cat_ID' => $new_global_id, 'cat_name' => $c->name, 'category_nicename' => $c->slug ) );
1925
			$global_id = $wpdb->insert_id;
1926
		}
1927
	} elseif ( $global_id != $term_id ) {
1928
		$local_id = $wpdb->get_var( $wpdb->prepare( "SELECT term_id FROM $wpdb->terms WHERE term_id = %d", $global_id ) );
1929
		if ( null != $local_id ) {
1930
			global_terms( $local_id );
1931
			if ( 10 < $global_terms_recurse ) {
1932
				$global_id = $term_id;
1933
			}
1934
		}
1935
	}
1936
1937
	if ( $global_id != $term_id ) {
1938
		if ( get_option( 'default_category' ) == $term_id )
1939
			update_option( 'default_category', $global_id );
1940
1941
		$wpdb->update( $wpdb->terms, array('term_id' => $global_id), array('term_id' => $term_id) );
1942
		$wpdb->update( $wpdb->term_taxonomy, array('term_id' => $global_id), array('term_id' => $term_id) );
1943
		$wpdb->update( $wpdb->term_taxonomy, array('parent' => $global_id), array('parent' => $term_id) );
1944
1945
		clean_term_cache($term_id);
1946
	}
1947
	if ( $recurse_start )
1948
		$global_terms_recurse = null;
1949
1950
	return $global_id;
1951
}
1952
1953
/**
1954
 * Ensure that the current site's domain is listed in the allowed redirect host list.
1955
 *
1956
 * @see wp_validate_redirect()
1957
 * @since MU
1958
 *
1959
 * @param array|string $deprecated Not used.
1960
 * @return array The current site's domain
1961
 */
1962
function redirect_this_site( $deprecated = '' ) {
0 ignored issues
show
The parameter $deprecated is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
1963
	return array( get_network()->domain );
1964
}
1965
1966
/**
1967
 * Check whether an upload is too big.
1968
 *
1969
 * @since MU
1970
 *
1971
 * @blessed
1972
 *
1973
 * @param array $upload
1974
 * @return string|array If the upload is under the size limit, $upload is returned. Otherwise returns an error message.
1975
 */
1976
function upload_is_file_too_big( $upload ) {
1977
	if ( ! is_array( $upload ) || defined( 'WP_IMPORTING' ) || get_site_option( 'upload_space_check_disabled' ) )
1978
		return $upload;
1979
1980 View Code Duplication
	if ( strlen( $upload['bits'] )  > ( KB_IN_BYTES * get_site_option( 'fileupload_maxk', 1500 ) ) ) {
1981
		return sprintf( __( 'This file is too big. Files must be less than %d KB in size.' ) . '<br />', get_site_option( 'fileupload_maxk', 1500 ) );
1982
	}
1983
1984
	return $upload;
1985
}
1986
1987
/**
1988
 * Add a nonce field to the signup page.
1989
 *
1990
 * @since MU
1991
 */
1992
function signup_nonce_fields() {
1993
	$id = mt_rand();
1994
	echo "<input type='hidden' name='signup_form_id' value='{$id}' />";
1995
	wp_nonce_field('signup_form_' . $id, '_signup_form', false);
1996
}
1997
1998
/**
1999
 * Process the signup nonce created in signup_nonce_fields().
2000
 *
2001
 * @since MU
2002
 *
2003
 * @param array $result
2004
 * @return array
2005
 */
2006
function signup_nonce_check( $result ) {
2007
	if ( !strpos( $_SERVER[ 'PHP_SELF' ], 'wp-signup.php' ) )
2008
		return $result;
2009
2010
	if ( wp_create_nonce('signup_form_' . $_POST[ 'signup_form_id' ]) != $_POST['_signup_form'] )
2011
		wp_die( __( 'Please try again.' ) );
2012
2013
	return $result;
2014
}
2015
2016
/**
2017
 * Correct 404 redirects when NOBLOGREDIRECT is defined.
2018
 *
2019
 * @since MU
2020
 */
2021
function maybe_redirect_404() {
2022
	/**
2023
	 * Filters the redirect URL for 404s on the main site.
2024
	 *
2025
	 * The filter is only evaluated if the NOBLOGREDIRECT constant is defined.
2026
	 *
2027
	 * @since 3.0.0
2028
	 *
2029
	 * @param string $no_blog_redirect The redirect URL defined in NOBLOGREDIRECT.
2030
	 */
2031
	if ( is_main_site() && is_404() && defined( 'NOBLOGREDIRECT' ) && ( $destination = apply_filters( 'blog_redirect_404', NOBLOGREDIRECT ) ) ) {
2032
		if ( $destination == '%siteurl%' )
2033
			$destination = network_home_url();
2034
		wp_redirect( $destination );
2035
		exit();
0 ignored issues
show
Coding Style Compatibility introduced by
The function maybe_redirect_404() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
2036
	}
2037
}
2038
2039
/**
2040
 * Add a new user to a blog by visiting /newbloguser/username/.
2041
 *
2042
 * This will only work when the user's details are saved as an option
2043
 * keyed as 'new_user_x', where 'x' is the username of the user to be
2044
 * added, as when a user is invited through the regular WP Add User interface.
2045
 *
2046
 * @since MU
2047
 */
2048
function maybe_add_existing_user_to_blog() {
2049
	if ( false === strpos( $_SERVER[ 'REQUEST_URI' ], '/newbloguser/' ) )
2050
		return;
2051
2052
	$parts = explode( '/', $_SERVER[ 'REQUEST_URI' ] );
2053
	$key = array_pop( $parts );
2054
2055
	if ( $key == '' )
2056
		$key = array_pop( $parts );
2057
2058
	$details = get_option( 'new_user_' . $key );
2059
	if ( !empty( $details ) )
2060
		delete_option( 'new_user_' . $key );
2061
2062
	if ( empty( $details ) || is_wp_error( add_existing_user_to_blog( $details ) ) )
2063
		wp_die( sprintf(__('An error occurred adding you to this site. Back to the <a href="%s">homepage</a>.'), home_url() ) );
2064
2065
	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 ) );
2066
}
2067
2068
/**
2069
 * Add a user to a blog based on details from maybe_add_existing_user_to_blog().
2070
 *
2071
 * @since MU
2072
 *
2073
 * @param array $details
0 ignored issues
show
Should the type for parameter $details not be false|array? Also, consider making the array more specific, something like array<String>, or String[].

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive. In addition it looks for parameters that have the generic type array and suggests a stricter type like array<String>.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
2074
 * @return true|WP_Error|void
0 ignored issues
show
Should the return type not be WP_Error|boolean|null?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
2075
 */
2076
function add_existing_user_to_blog( $details = false ) {
2077
	if ( is_array( $details ) ) {
2078
		$blog_id = get_current_blog_id();
2079
		$result = add_user_to_blog( $blog_id, $details[ 'user_id' ], $details[ 'role' ] );
2080
		/**
2081
		 * Fires immediately after an existing user is added to a site.
2082
		 *
2083
		 * @since MU
2084
		 *
2085
		 * @param int   $user_id User ID.
2086
		 * @param mixed $result  True on success or a WP_Error object if the user doesn't exist.
2087
		 */
2088
		do_action( 'added_existing_user', $details['user_id'], $result );
2089
		return $result;
2090
	}
2091
}
2092
2093
/**
2094
 * Adds a newly created user to the appropriate blog
2095
 *
2096
 * To add a user in general, use add_user_to_blog(). This function
2097
 * is specifically hooked into the {@see 'wpmu_activate_user'} action.
2098
 *
2099
 * @since MU
2100
 * @see add_user_to_blog()
2101
 *
2102
 * @param int   $user_id
2103
 * @param mixed $password Ignored.
2104
 * @param array $meta
2105
 */
2106
function add_new_user_to_blog( $user_id, $password, $meta ) {
0 ignored issues
show
The parameter $password is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
2107
	if ( !empty( $meta[ 'add_to_blog' ] ) ) {
2108
		$blog_id = $meta[ 'add_to_blog' ];
2109
		$role = $meta[ 'new_role' ];
2110
		remove_user_from_blog($user_id, get_network()->site_id); // remove user from main blog.
2111
		add_user_to_blog( $blog_id, $user_id, $role );
2112
		update_user_meta( $user_id, 'primary_blog', $blog_id );
2113
	}
2114
}
2115
2116
/**
2117
 * Correct From host on outgoing mail to match the site domain
2118
 *
2119
 * @since MU
2120
 *
2121
 * @param PHPMailer $phpmailer The PHPMailer instance, passed by reference.
2122
 */
2123
function fix_phpmailer_messageid( $phpmailer ) {
2124
	$phpmailer->Hostname = get_network()->domain;
2125
}
2126
2127
/**
2128
 * Check to see whether a user is marked as a spammer, based on user login.
2129
 *
2130
 * @since MU
2131
 *
2132
 * @param string|WP_User $user Optional. Defaults to current user. WP_User object,
2133
 * 	                           or user login name as a string.
2134
 * @return bool
2135
 */
2136
function is_user_spammy( $user = null ) {
2137
    if ( ! ( $user instanceof WP_User ) ) {
2138
		if ( $user ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $user of type string|null is loosely compared to true; this is ambiguous if the string can be empty. 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 string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
2139
			$user = get_user_by( 'login', $user );
2140
		} else {
2141
			$user = wp_get_current_user();
2142
		}
2143
	}
2144
2145
	return $user && isset( $user->spam ) && 1 == $user->spam;
2146
}
2147
2148
/**
2149
 * Update this blog's 'public' setting in the global blogs table.
2150
 *
2151
 * Public blogs have a setting of 1, private blogs are 0.
2152
 *
2153
 * @since MU
2154
 *
2155
 * @param int $old_value
2156
 * @param int $value     The new public value
2157
 */
2158
function update_blog_public( $old_value, $value ) {
0 ignored issues
show
The parameter $old_value is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
2159
	update_blog_status( get_current_blog_id(), 'public', (int) $value );
2160
}
2161
2162
/**
2163
 * Check whether a usermeta key has to do with the current blog.
2164
 *
2165
 * @since MU
2166
 *
2167
 * @global wpdb $wpdb WordPress database abstraction object.
2168
 *
2169
 * @param string $key
2170
 * @param int    $user_id Optional. Defaults to current user.
2171
 * @param int    $blog_id Optional. Defaults to current blog.
2172
 * @return bool
2173
 */
2174
function is_user_option_local( $key, $user_id = 0, $blog_id = 0 ) {
0 ignored issues
show
The parameter $user_id is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
2175
	global $wpdb;
2176
2177
	$current_user = wp_get_current_user();
2178
	if ( $blog_id == 0 ) {
2179
		$blog_id = $wpdb->blogid;
2180
	}
2181
	$local_key = $wpdb->get_blog_prefix( $blog_id ) . $key;
2182
2183
	return isset( $current_user->$local_key );
2184
}
2185
2186
/**
2187
 * Check whether users can self-register, based on Network settings.
2188
 *
2189
 * @since MU
2190
 *
2191
 * @return bool
2192
 */
2193
function users_can_register_signup_filter() {
2194
	$registration = get_site_option('registration');
2195
	return ( $registration == 'all' || $registration == 'user' );
2196
}
2197
2198
/**
2199
 * Ensure that the welcome message is not empty. Currently unused.
2200
 *
2201
 * @since MU
2202
 *
2203
 * @param string $text
2204
 * @return string
2205
 */
2206
function welcome_user_msg_filter( $text ) {
2207
	if ( !$text ) {
2208
		remove_filter( 'site_option_welcome_user_email', 'welcome_user_msg_filter' );
2209
2210
		/* translators: Do not translate USERNAME, PASSWORD, LOGINLINK, SITE_NAME: those are placeholders. */
2211
		$text = __( 'Howdy USERNAME,
2212
2213
Your new account is set up.
2214
2215
You can log in with the following information:
2216
Username: USERNAME
2217
Password: PASSWORD
2218
LOGINLINK
2219
2220
Thanks!
2221
2222
--The Team @ SITE_NAME' );
2223
		update_site_option( 'welcome_user_email', $text );
2224
	}
2225
	return $text;
2226
}
2227
2228
/**
2229
 * Whether to force SSL on content.
2230
 *
2231
 * @since 2.8.5
2232
 *
2233
 * @staticvar bool $forced_content
2234
 *
2235
 * @param bool $force
2236
 * @return bool True if forced, false if not forced.
2237
 */
2238
function force_ssl_content( $force = '' ) {
2239
	static $forced_content = false;
2240
2241
	if ( '' != $force ) {
2242
		$old_forced = $forced_content;
2243
		$forced_content = $force;
2244
		return $old_forced;
2245
	}
2246
2247
	return $forced_content;
2248
}
2249
2250
/**
2251
 * Formats a URL to use https.
2252
 *
2253
 * Useful as a filter.
2254
 *
2255
 * @since 2.8.5
2256
 *
2257
 * @param string $url URL
2258
 * @return string URL with https as the scheme
2259
 */
2260
function filter_SSL( $url ) {
2261
	if ( ! is_string( $url ) )
2262
		return get_bloginfo( 'url' ); // Return home blog url with proper scheme
2263
2264
	if ( force_ssl_content() && is_ssl() )
2265
		$url = set_url_scheme( $url, 'https' );
2266
2267
	return $url;
2268
}
2269
2270
/**
2271
 * Schedule update of the network-wide counts for the current network.
2272
 *
2273
 * @since 3.1.0
2274
 */
2275
function wp_schedule_update_network_counts() {
2276
	if ( !is_main_site() )
2277
		return;
2278
2279
	if ( ! wp_next_scheduled('update_network_counts') && ! wp_installing() )
2280
		wp_schedule_event(time(), 'twicedaily', 'update_network_counts');
2281
}
2282
2283
/**
2284
 * Update the network-wide counts for the current network.
2285
 *
2286
 * @since 3.1.0
2287
 * @since 4.8.0 The $network_id parameter has been added.
2288
 *
2289
 * @param int|null $network_id ID of the network. Default is the current network.
2290
 */
2291
function wp_update_network_counts( $network_id = null ) {
2292
	wp_update_network_user_counts( $network_id );
2293
	wp_update_network_site_counts( $network_id );
2294
}
2295
2296
/**
2297
 * Update the count of sites for the current network.
2298
 *
2299
 * If enabled through the {@see 'enable_live_network_counts'} filter, update the sites count
2300
 * on a network when a site is created or its status is updated.
2301
 *
2302
 * @since 3.7.0
2303
 * @since 4.8.0 The $network_id parameter has been added.
2304
 *
2305
 * @param int|null $network_id ID of the network. Default is the current network.
2306
 */
2307
function wp_maybe_update_network_site_counts( $network_id = null ) {
2308
	$is_small_network = ! wp_is_large_network( 'sites', $network_id );
2309
2310
	/**
2311
	 * Filters whether to update network site or user counts when a new site is created.
2312
	 *
2313
	 * @since 3.7.0
2314
	 *
2315
	 * @see wp_is_large_network()
2316
	 *
2317
	 * @param bool   $small_network Whether the network is considered small.
2318
	 * @param string $context       Context. Either 'users' or 'sites'.
2319
	 */
2320
	if ( ! apply_filters( 'enable_live_network_counts', $is_small_network, 'sites' ) )
2321
		return;
2322
2323
	wp_update_network_site_counts( $network_id );
2324
}
2325
2326
/**
2327
 * Update the network-wide users count.
2328
 *
2329
 * If enabled through the {@see 'enable_live_network_counts'} filter, update the users count
2330
 * on a network when a user is created or its status is updated.
2331
 *
2332
 * @since 3.7.0
2333
 * @since 4.8.0 The $network_id parameter has been added.
2334
 *
2335
 * @param int|null $network_id ID of the network. Default is the current network.
2336
 */
2337
function wp_maybe_update_network_user_counts( $network_id = null ) {
2338
	$is_small_network = ! wp_is_large_network( 'users', $network_id );
2339
2340
	/** This filter is documented in wp-includes/ms-functions.php */
2341
	if ( ! apply_filters( 'enable_live_network_counts', $is_small_network, 'users' ) )
2342
		return;
2343
2344
	wp_update_network_user_counts( $network_id );
2345
}
2346
2347
/**
2348
 * Update the network-wide site count.
2349
 *
2350
 * @since 3.7.0
2351
 * @since 4.8.0 The $network_id parameter has been added.
2352
 *
2353
 * @param int|null $network_id ID of the network. Default is the current network.
2354
 */
2355
function wp_update_network_site_counts( $network_id = null ) {
2356
	$network_id = (int) $network_id;
2357
	if ( ! $network_id ) {
2358
		$network_id = get_current_network_id();
2359
	}
2360
2361
	$count = get_sites( array(
2362
		'network_id' => $network_id,
2363
		'spam'       => 0,
2364
		'deleted'    => 0,
2365
		'archived'   => 0,
2366
		'count'      => true,
2367
	) );
2368
2369
	update_network_option( $network_id, 'blog_count', $count );
2370
}
2371
2372
/**
2373
 * Update the network-wide user count.
2374
 *
2375
 * @since 3.7.0
2376
 * @since 4.8.0 The $network_id parameter has been added.
2377
 *
2378
 * @global wpdb $wpdb WordPress database abstraction object.
2379
 *
2380
 * @param int|null $network_id ID of the network. Default is the current network.
2381
 */
2382
function wp_update_network_user_counts( $network_id = null ) {
2383
	global $wpdb;
2384
2385
	$count = $wpdb->get_var( "SELECT COUNT(ID) as c FROM $wpdb->users WHERE spam = '0' AND deleted = '0'" );
2386
	update_network_option( $network_id, 'user_count', $count );
2387
}
2388
2389
/**
2390
 * Returns the space used by the current blog.
2391
 *
2392
 * @since 3.5.0
2393
 *
2394
 * @return int Used space in megabytes
2395
 */
2396
function get_space_used() {
2397
	/**
2398
	 * Filters the amount of storage space used by the current site.
2399
	 *
2400
	 * @since 3.5.0
2401
	 *
2402
	 * @param int|bool $space_used The amount of used space, in megabytes. Default false.
2403
	 */
2404
	$space_used = apply_filters( 'pre_get_space_used', false );
2405
	if ( false === $space_used ) {
2406
		$upload_dir = wp_upload_dir();
2407
		$space_used = get_dirsize( $upload_dir['basedir'] ) / MB_IN_BYTES;
2408
	}
2409
2410
	return $space_used;
2411
}
2412
2413
/**
2414
 * Returns the upload quota for the current blog.
2415
 *
2416
 * @since MU
2417
 *
2418
 * @return int Quota in megabytes
2419
 */
2420
function get_space_allowed() {
2421
	$space_allowed = get_option( 'blog_upload_space' );
2422
2423
	if ( ! is_numeric( $space_allowed ) )
2424
		$space_allowed = get_site_option( 'blog_upload_space' );
2425
2426
	if ( ! is_numeric( $space_allowed ) )
2427
		$space_allowed = 100;
2428
2429
	/**
2430
	 * Filters the upload quota for the current site.
2431
	 *
2432
	 * @since 3.7.0
2433
	 *
2434
	 * @param int $space_allowed Upload quota in megabytes for the current blog.
2435
	 */
2436
	return apply_filters( 'get_space_allowed', $space_allowed );
2437
}
2438
2439
/**
2440
 * Determines if there is any upload space left in the current blog's quota.
2441
 *
2442
 * @since 3.0.0
2443
 *
2444
 * @return int of upload space available in bytes
2445
 */
2446
function get_upload_space_available() {
2447
	$allowed = get_space_allowed();
2448
	if ( $allowed < 0 ) {
2449
		$allowed = 0;
2450
	}
2451
	$space_allowed = $allowed * MB_IN_BYTES;
2452
	if ( get_site_option( 'upload_space_check_disabled' ) )
2453
		return $space_allowed;
2454
2455
	$space_used = get_space_used() * MB_IN_BYTES;
2456
2457
	if ( ( $space_allowed - $space_used ) <= 0 )
2458
		return 0;
2459
2460
	return $space_allowed - $space_used;
2461
}
2462
2463
/**
2464
 * Determines if there is any upload space left in the current blog's quota.
2465
 *
2466
 * @since 3.0.0
2467
 * @return bool True if space is available, false otherwise.
2468
 */
2469
function is_upload_space_available() {
2470
	if ( get_site_option( 'upload_space_check_disabled' ) )
2471
		return true;
2472
2473
	return (bool) get_upload_space_available();
2474
}
2475
2476
/**
2477
 * Filters the maximum upload file size allowed, in bytes.
2478
 *
2479
 * @since 3.0.0
2480
 *
2481
 * @param  int $size Upload size limit in bytes.
2482
 * @return int       Upload size limit in bytes.
0 ignored issues
show
Should the return type not be integer|double?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
2483
 */
2484
function upload_size_limit_filter( $size ) {
2485
	$fileupload_maxk = KB_IN_BYTES * get_site_option( 'fileupload_maxk', 1500 );
2486
	if ( get_site_option( 'upload_space_check_disabled' ) )
2487
		return min( $size, $fileupload_maxk );
2488
2489
	return min( $size, $fileupload_maxk, get_upload_space_available() );
2490
}
2491
2492
/**
2493
 * Whether or not we have a large network.
2494
 *
2495
 * The default criteria for a large network is either more than 10,000 users or more than 10,000 sites.
2496
 * Plugins can alter this criteria using the {@see 'wp_is_large_network'} filter.
2497
 *
2498
 * @since 3.3.0
2499
 * @since 4.8.0 The $network_id parameter has been added.
2500
 *
2501
 * @param string   $using      'sites or 'users'. Default is 'sites'.
2502
 * @param int|null $network_id ID of the network. Default is the current network.
2503
 * @return bool True if the network meets the criteria for large. False otherwise.
2504
 */
2505
function wp_is_large_network( $using = 'sites', $network_id = null ) {
2506
	$network_id = (int) $network_id;
2507
	if ( ! $network_id ) {
2508
		$network_id = get_current_network_id();
2509
	}
2510
2511
	if ( 'users' == $using ) {
2512
		$count = get_user_count( $network_id );
2513
		/**
2514
		 * Filters whether the network is considered large.
2515
		 *
2516
		 * @since 3.3.0
2517
		 * @since 4.8.0 The $network_id parameter has been added.
2518
		 *
2519
		 * @param bool   $is_large_network Whether the network has more than 10000 users or sites.
2520
		 * @param string $component        The component to count. Accepts 'users', or 'sites'.
2521
		 * @param int    $count            The count of items for the component.
2522
		 * @param int    $network_id       The ID of the network being checked.
2523
		 */
2524
		return apply_filters( 'wp_is_large_network', $count > 10000, 'users', $count, $network_id );
2525
	}
2526
2527
	$count = get_blog_count( $network_id );
2528
	/** This filter is documented in wp-includes/ms-functions.php */
2529
	return apply_filters( 'wp_is_large_network', $count > 10000, 'sites', $count, $network_id );
2530
}
2531
2532
/**
2533
 * Retrieves a list of reserved site on a sub-directory Multisite install.
2534
 *
2535
 * @since 4.4.0
2536
 *
2537
 * @return array $names Array of reserved subdirectory names.
2538
 */
2539
function get_subdirectory_reserved_names() {
2540
	$names = array(
2541
		'page', 'comments', 'blog', 'files', 'feed', 'wp-admin',
2542
		'wp-content', 'wp-includes', 'wp-json', 'embed'
2543
	);
2544
2545
	/**
2546
	 * Filters reserved site names on a sub-directory Multisite install.
2547
	 *
2548
	 * @since 3.0.0
2549
	 * @since 4.4.0 'wp-admin', 'wp-content', 'wp-includes', 'wp-json', and 'embed' were added
2550
	 *              to the reserved names list.
2551
	 *
2552
	 * @param array $subdirectory_reserved_names Array of reserved names.
2553
	 */
2554
	return apply_filters( 'subdirectory_reserved_names', $names );
2555
}
2556