run:R W Run
DIR
2026-03-11 16:18:51
R W Run
DIR
2026-03-11 16:18:52
R W Run
DIR
2026-03-11 16:18:51
R W Run
DIR
2026-03-11 16:18:52
R W Run
DIR
2026-03-11 16:18:52
R W Run
DIR
2026-03-11 16:18:51
R W Run
DIR
2026-03-11 16:18:51
R W Run
DIR
2026-03-11 16:18:51
R W Run
DIR
2026-03-11 16:18:51
R W Run
DIR
2026-03-11 16:18:52
R W Run
DIR
2026-03-11 16:18:52
R W Run
DIR
2026-03-11 16:18:52
R W Run
DIR
2026-03-11 16:18:51
R W Run
DIR
2026-03-11 16:18:52
R W Run
DIR
2026-03-11 16:18:52
R W Run
DIR
2026-03-11 16:18:51
R W Run
DIR
2026-03-11 16:18:52
R W Run
DIR
2026-03-11 16:18:51
R W Run
DIR
2026-03-11 16:18:51
R W Run
DIR
2026-03-11 16:18:51
R W Run
DIR
2026-03-11 16:18:52
R W Run
DIR
2026-03-11 16:18:52
R W Run
DIR
2026-03-11 16:18:52
R W Run
DIR
2026-03-11 16:18:52
R W Run
DIR
2026-03-11 16:18:51
R W Run
DIR
2026-03-11 16:18:52
R W Run
DIR
2026-03-11 16:18:52
R W Run
DIR
2026-03-11 16:18:51
R W Run
DIR
2026-03-11 16:18:51
R W Run
23.8 KB
2026-03-11 16:18:51
R W Run
7.8 KB
2026-03-11 16:18:52
R W Run
36.1 KB
2026-03-11 16:18:51
R W Run
11.9 KB
2026-03-11 16:18:52
R W Run
18.94 KB
2026-03-11 16:18:52
R W Run
7.35 KB
2026-03-11 16:18:52
R W Run
28.6 KB
2026-03-11 16:18:51
R W Run
316 By
2026-03-11 16:18:51
R W Run
12.9 KB
2026-03-11 16:18:51
R W Run
61.02 KB
2026-03-11 16:18:52
R W Run
15 KB
2026-03-11 16:18:51
R W Run
112.05 KB
2026-03-11 16:18:51
R W Run
12.47 KB
2026-03-11 16:18:51
R W Run
15.07 KB
2026-03-11 16:18:52
R W Run
9.84 KB
2026-03-11 16:18:52
R W Run
13.17 KB
2026-03-11 16:18:52
R W Run
33.83 KB
2026-03-11 16:18:51
R W Run
42.63 KB
2026-03-11 16:18:51
R W Run
55.71 KB
2026-03-11 16:18:52
R W Run
12.53 KB
2026-03-11 16:18:51
R W Run
2.55 KB
2026-03-11 16:18:52
R W Run
28.92 KB
2026-03-11 16:18:52
R W Run
539 By
2026-03-11 16:18:51
R W Run
367 By
2026-03-11 16:18:52
R W Run
42.65 KB
2026-03-11 16:18:51
R W Run
401 By
2026-03-11 16:18:51
R W Run
6.61 KB
2026-03-11 16:18:51
R W Run
664 By
2026-03-11 16:18:52
R W Run
20.63 KB
2026-03-11 16:18:51
R W Run
2.18 KB
2026-03-11 16:18:52
R W Run
453 By
2026-03-11 16:18:52
R W Run
457 By
2026-03-11 16:18:51
R W Run
36.83 KB
2026-03-11 16:18:52
R W Run
2.41 KB
2026-03-11 16:18:52
R W Run
8.28 KB
2026-03-11 16:18:51
R W Run
13.89 KB
2026-03-11 16:18:51
R W Run
11.76 KB
2026-03-11 16:18:51
R W Run
2.65 KB
2026-03-11 16:18:51
R W Run
7.43 KB
2026-03-11 16:18:51
R W Run
17.46 KB
2026-03-11 16:18:51
R W Run
5.14 KB
2026-03-11 16:18:52
R W Run
16.7 KB
2026-03-11 16:18:51
R W Run
8.28 KB
2026-03-11 16:18:52
R W Run
2.92 KB
2026-03-11 16:18:52
R W Run
1.32 KB
2026-03-11 16:18:51
R W Run
4.6 KB
2026-03-11 16:18:52
R W Run
11.62 KB
2026-03-11 16:18:52
R W Run
2.5 KB
2026-03-11 16:18:51
R W Run
1.97 KB
2026-03-11 16:18:51
R W Run
11.25 KB
2026-03-11 16:18:52
R W Run
5.32 KB
2026-03-11 16:18:51
R W Run
10.99 KB
2026-03-11 16:18:52
R W Run
68.32 KB
2026-03-11 16:18:51
R W Run
6.34 KB
2026-03-11 16:18:51
R W Run
5.49 KB
2026-03-11 16:18:51
R W Run
1.99 KB
2026-03-11 16:18:52
R W Run
7.02 KB
2026-03-11 16:18:51
R W Run
4.91 KB
2026-03-11 16:18:52
R W Run
16.86 KB
2026-03-11 16:18:51
R W Run
24.23 KB
2026-03-11 16:18:51
R W Run
3.97 KB
2026-03-11 16:18:51
R W Run
47.66 KB
2026-03-11 16:18:51
R W Run
9.22 KB
2026-03-11 16:18:51
R W Run
25.51 KB
2026-03-11 16:18:51
R W Run
198.38 KB
2026-03-11 16:18:52
R W Run
56.65 KB
2026-03-11 16:18:51
R W Run
10.46 KB
2026-03-11 16:18:51
R W Run
10.95 KB
2026-03-11 16:18:52
R W Run
29.26 KB
2026-03-11 16:18:51
R W Run
70.91 KB
2026-03-11 16:18:52
R W Run
35.3 KB
2026-03-11 16:18:52
R W Run
16.61 KB
2026-03-11 16:18:52
R W Run
2.57 KB
2026-03-11 16:18:52
R W Run
39.83 KB
2026-03-11 16:18:51
R W Run
70.64 KB
2026-03-11 16:18:51
R W Run
15.56 KB
2026-03-11 16:18:52
R W Run
7.33 KB
2026-03-11 16:18:52
R W Run
253 By
2026-03-11 16:18:51
R W Run
7.96 KB
2026-03-11 16:18:52
R W Run
3.23 KB
2026-03-11 16:18:52
R W Run
969 By
2026-03-11 16:18:52
R W Run
16.28 KB
2026-03-11 16:18:51
R W Run
7.22 KB
2026-03-11 16:18:51
R W Run
12.95 KB
2026-03-11 16:18:51
R W Run
6.53 KB
2026-03-11 16:18:51
R W Run
3.42 KB
2026-03-11 16:18:52
R W Run
5.84 KB
2026-03-11 16:18:51
R W Run
1.97 KB
2026-03-11 16:18:51
R W Run
4.3 KB
2026-03-11 16:18:52
R W Run
2.91 KB
2026-03-11 16:18:51
R W Run
16.46 KB
2026-03-11 16:18:52
R W Run
40.6 KB
2026-03-11 16:18:51
R W Run
20.22 KB
2026-03-11 16:18:51
R W Run
36.11 KB
2026-03-11 16:18:52
R W Run
17.01 KB
2026-03-11 16:18:51
R W Run
7.27 KB
2026-03-11 16:18:52
R W Run
6.62 KB
2026-03-11 16:18:52
R W Run
16.49 KB
2026-03-11 16:18:52
R W Run
1.79 KB
2026-03-11 16:18:52
R W Run
29.82 KB
2026-03-11 16:18:51
R W Run
6.67 KB
2026-03-11 16:18:52
R W Run
8.98 KB
2026-03-11 16:18:52
R W Run
19.42 KB
2026-03-11 16:18:51
R W Run
12.01 KB
2026-03-11 16:18:51
R W Run
17.11 KB
2026-03-11 16:18:51
R W Run
6.74 KB
2026-03-11 16:18:52
R W Run
30.93 KB
2026-03-11 16:18:51
R W Run
4.99 KB
2026-03-11 16:18:51
R W Run
4.25 KB
2026-03-11 16:18:51
R W Run
24.72 KB
2026-03-11 16:18:51
R W Run
29.96 KB
2026-03-11 16:18:52
R W Run
6.41 KB
2026-03-11 16:18:51
R W Run
160 KB
2026-03-11 16:18:51
R W Run
6.72 KB
2026-03-11 16:18:52
R W Run
10.92 KB
2026-03-11 16:18:51
R W Run
4.77 KB
2026-03-11 16:18:51
R W Run
3.38 KB
2026-03-11 16:18:51
R W Run
11.18 KB
2026-03-11 16:18:51
R W Run
62.19 KB
2026-03-11 16:18:51
R W Run
2.46 KB
2026-03-11 16:18:51
R W Run
9.17 KB
2026-03-11 16:18:51
R W Run
32.15 KB
2026-03-11 16:18:51
R W Run
34.05 KB
2026-03-11 16:18:52
R W Run
7.15 KB
2026-03-11 16:18:51
R W Run
3.47 KB
2026-03-11 16:18:52
R W Run
1.87 KB
2026-03-11 16:18:52
R W Run
30.91 KB
2026-03-11 16:18:51
R W Run
7.29 KB
2026-03-11 16:18:52
R W Run
7.35 KB
2026-03-11 16:18:51
R W Run
12.54 KB
2026-03-11 16:18:51
R W Run
19.12 KB
2026-03-11 16:18:51
R W Run
18.12 KB
2026-03-11 16:18:52
R W Run
39.99 KB
2026-03-11 16:18:52
R W Run
5.17 KB
2026-03-11 16:18:52
R W Run
979 By
2026-03-11 16:18:51
R W Run
18.44 KB
2026-03-11 16:18:52
R W Run
10.24 KB
2026-03-11 16:18:51
R W Run
1.77 KB
2026-03-11 16:18:52
R W Run
34.9 KB
2026-03-11 16:18:51
R W Run
7.19 KB
2026-03-11 16:18:52
R W Run
160.5 KB
2026-03-11 16:18:51
R W Run
64.27 KB
2026-03-11 16:18:51
R W Run
27.95 KB
2026-03-11 16:18:51
R W Run
4.69 KB
2026-03-11 16:18:51
R W Run
2.94 KB
2026-03-11 16:18:51
R W Run
43.13 KB
2026-03-11 16:18:52
R W Run
2.25 KB
2026-03-11 16:18:52
R W Run
22.5 KB
2026-03-11 16:18:51
R W Run
13.01 KB
2026-03-11 16:18:52
R W Run
3.27 KB
2026-03-11 16:18:51
R W Run
18 KB
2026-03-11 16:18:51
R W Run
210.4 KB
2026-03-11 16:18:52
R W Run
25.86 KB
2026-03-11 16:18:52
R W Run
115.85 KB
2026-03-11 16:18:51
R W Run
373 By
2026-03-11 16:18:52
R W Run
343 By
2026-03-11 16:18:52
R W Run
338 By
2026-03-11 16:18:51
R W Run
100.73 KB
2026-03-11 16:18:52
R W Run
130.93 KB
2026-03-11 16:18:51
R W Run
19.1 KB
2026-03-11 16:18:51
R W Run
17.41 KB
2026-03-11 16:18:52
R W Run
41.98 KB
2026-03-11 16:18:52
R W Run
400 By
2026-03-11 16:18:52
R W Run
11.1 KB
2026-03-11 16:18:52
R W Run
37.02 KB
2026-03-11 16:18:51
R W Run
2.24 KB
2026-03-11 16:18:51
R W Run
188.13 KB
2026-03-11 16:18:51
R W Run
338 By
2026-03-11 16:18:51
R W Run
38 KB
2026-03-11 16:18:51
R W Run
4.02 KB
2026-03-11 16:18:52
R W Run
5.38 KB
2026-03-11 16:18:51
R W Run
3.05 KB
2026-03-11 16:18:52
R W Run
2.61 KB
2026-03-11 16:18:51
R W Run
1.16 KB
2026-03-11 16:18:52
R W Run
4.04 KB
2026-03-11 16:18:51
R W Run
3.71 KB
2026-03-11 16:18:51
R W Run
24.6 KB
2026-03-11 16:18:51
R W Run
9.56 KB
2026-03-11 16:18:51
R W Run
346.43 KB
2026-03-11 16:18:52
R W Run
281.84 KB
2026-03-11 16:18:52
R W Run
14.95 KB
2026-03-11 16:18:51
R W Run
8.44 KB
2026-03-11 16:18:52
R W Run
168.95 KB
2026-03-11 16:18:52
R W Run
20.71 KB
2026-03-11 16:18:52
R W Run
25.27 KB
2026-03-11 16:18:51
R W Run
5.72 KB
2026-03-11 16:18:51
R W Run
4.63 KB
2026-03-11 16:18:52
R W Run
81.73 KB
2026-03-11 16:18:51
R W Run
67.18 KB
2026-03-11 16:18:51
R W Run
156.36 KB
2026-03-11 16:18:52
R W Run
55.19 KB
2026-03-11 16:18:51
R W Run
162 By
2026-03-11 16:18:51
R W Run
61.72 KB
2026-03-11 16:18:51
R W Run
216.06 KB
2026-03-11 16:18:52
R W Run
65.09 KB
2026-03-11 16:18:51
R W Run
25.24 KB
2026-03-11 16:18:52
R W Run
4.81 KB
2026-03-11 16:18:51
R W Run
6.48 KB
2026-03-11 16:18:52
R W Run
21.25 KB
2026-03-11 16:18:51
R W Run
2.79 KB
2026-03-11 16:18:52
R W Run
89.69 KB
2026-03-11 16:18:52
R W Run
19.42 KB
2026-03-11 16:18:52
R W Run
3.69 KB
2026-03-11 16:18:52
R W Run
4.11 KB
2026-03-11 16:18:51
R W Run
40.74 KB
2026-03-11 16:18:51
R W Run
25.38 KB
2026-03-11 16:18:51
R W Run
43.31 KB
2026-03-11 16:18:52
R W Run
102.57 KB
2026-03-11 16:18:52
R W Run
6.18 KB
2026-03-11 16:18:51
R W Run
124.47 KB
2026-03-11 16:18:52
R W Run
35.65 KB
2026-03-11 16:18:52
R W Run
6.94 KB
2026-03-11 16:18:52
R W Run
67.04 KB
2026-03-11 16:18:52
R W Run
10.62 KB
2026-03-11 16:18:51
R W Run
289.35 KB
2026-03-11 16:18:52
R W Run
36.23 KB
2026-03-11 16:18:51
R W Run
200 By
2026-03-11 16:18:52
R W Run
200 By
2026-03-11 16:18:52
R W Run
98.29 KB
2026-03-11 16:18:52
R W Run
30.02 KB
2026-03-11 16:18:52
R W Run
19.03 KB
2026-03-11 16:18:52
R W Run
5.06 KB
2026-03-11 16:18:52
R W Run
255 By
2026-03-11 16:18:51
R W Run
22.66 KB
2026-03-11 16:18:52
R W Run
154.63 KB
2026-03-11 16:18:51
R W Run
9.68 KB
2026-03-11 16:18:51
R W Run
258 By
2026-03-11 16:18:51
R W Run
23.49 KB
2026-03-11 16:18:51
R W Run
3.16 KB
2026-03-11 16:18:51
R W Run
8.4 KB
2026-03-11 16:18:52
R W Run
441 By
2026-03-11 16:18:51
R W Run
7.39 KB
2026-03-11 16:18:51
R W Run
173 KB
2026-03-11 16:18:52
R W Run
544 By
2026-03-11 16:18:52
R W Run
4.17 KB
2026-03-11 16:18:51
R W Run
35.97 KB
2026-03-11 16:18:52
R W Run
1.69 KB
2026-03-11 16:18:51
R W Run
2.84 KB
2026-03-11 16:18:52
R W Run
6.09 KB
2026-03-11 16:18:51
R W Run
8.71 KB
2026-03-11 16:18:51
R W Run
131.84 KB
2026-03-11 16:18:51
R W Run
37.45 KB
2026-03-11 16:18:51
R W Run
173.89 KB
2026-03-11 16:18:51
R W Run
7.09 KB
2026-03-11 16:18:51
R W Run
6.41 KB
2026-03-11 16:18:51
R W Run
1.08 KB
2026-03-11 16:18:51
R W Run
69.46 KB
2026-03-11 16:18:52
R W Run
445 By
2026-03-11 16:18:51
R W Run
799 By
2026-03-11 16:18:52
R W Run
error_log
📄ms-functions.php
1<?php
2/**
3 * Multisite WordPress API
4 *
5 * @package WordPress
6 * @subpackage Multisite
7 * @since 3.0.0
8 */
9
10/**
11 * Gets the network's site and user counts.
12 *
13 * @since MU (3.0.0)
14 *
15 * @return int[] {
16 * Site and user count for the network.
17 *
18 * @type int $blogs Number of sites on the network.
19 * @type int $users Number of users on the network.
20 * }
21 */
22function get_sitestats() {
23 $stats = array(
24 'blogs' => get_blog_count(),
25 'users' => get_user_count(),
26 );
27
28 return $stats;
29}
30
31/**
32 * Gets one of a user's active blogs.
33 *
34 * Returns the user's primary blog, if they have one and
35 * it is active. If it's inactive, function returns another
36 * active blog of the user. If none are found, the user
37 * is added as a Subscriber to the Dashboard Blog and that blog
38 * is returned.
39 *
40 * @since MU (3.0.0)
41 *
42 * @param int $user_id The unique ID of the user
43 * @return WP_Site|void The blog object
44 */
45function get_active_blog_for_user( $user_id ) {
46 $blogs = get_blogs_of_user( $user_id );
47 if ( empty( $blogs ) ) {
48 return;
49 }
50
51 if ( ! is_multisite() ) {
52 return $blogs[ get_current_blog_id() ];
53 }
54
55 $primary_blog = get_user_meta( $user_id, 'primary_blog', true );
56 $first_blog = current( $blogs );
57 if ( false !== $primary_blog ) {
58 if ( ! isset( $blogs[ $primary_blog ] ) ) {
59 update_user_meta( $user_id, 'primary_blog', $first_blog->userblog_id );
60 $primary = get_site( $first_blog->userblog_id );
61 } else {
62 $primary = get_site( $primary_blog );
63 }
64 } else {
65 // TODO: Review this call to add_user_to_blog too - to get here the user must have a role on this blog?
66 $result = add_user_to_blog( $first_blog->userblog_id, $user_id, 'subscriber' );
67
68 if ( ! is_wp_error( $result ) ) {
69 update_user_meta( $user_id, 'primary_blog', $first_blog->userblog_id );
70 $primary = $first_blog;
71 }
72 }
73
74 if ( ( ! is_object( $primary ) )
75 || ( '1' === $primary->archived || '1' === $primary->spam || '1' === $primary->deleted )
76 ) {
77 $blogs = get_blogs_of_user( $user_id, true ); // If a user's primary blog is shut down, check their other blogs.
78 $ret = false;
79
80 if ( is_array( $blogs ) && count( $blogs ) > 0 ) {
81 $current_network_id = get_current_network_id();
82
83 foreach ( (array) $blogs as $blog_id => $blog ) {
84 if ( $blog->site_id !== $current_network_id ) {
85 continue;
86 }
87
88 $details = get_site( $blog_id );
89 if ( is_object( $details )
90 && '0' === $details->archived && '0' === $details->spam && '0' === $details->deleted
91 ) {
92 $ret = $details;
93 if ( (int) get_user_meta( $user_id, 'primary_blog', true ) !== $blog_id ) {
94 update_user_meta( $user_id, 'primary_blog', $blog_id );
95 }
96 if ( ! get_user_meta( $user_id, 'source_domain', true ) ) {
97 update_user_meta( $user_id, 'source_domain', $details->domain );
98 }
99 break;
100 }
101 }
102 } else {
103 return;
104 }
105
106 return $ret;
107 } else {
108 return $primary;
109 }
110}
111
112/**
113 * Gets the number of active sites on the installation.
114 *
115 * The count is cached and updated twice daily. This is not a live count.
116 *
117 * @since MU (3.0.0)
118 * @since 3.7.0 The `$network_id` parameter has been deprecated.
119 * @since 4.8.0 The `$network_id` parameter is now being used.
120 *
121 * @param int|null $network_id ID of the network. Default is the current network.
122 * @return int Number of active sites on the network.
123 */
124function get_blog_count( $network_id = null ) {
125 return get_network_option( $network_id, 'blog_count' );
126}
127
128/**
129 * Gets a blog post from any site on the network.
130 *
131 * This function is similar to get_post(), except that it can retrieve a post
132 * from any site on the network, not just the current site.
133 *
134 * @since MU (3.0.0)
135 *
136 * @param int $blog_id ID of the blog.
137 * @param int $post_id ID of the post being looked for.
138 * @return WP_Post|null WP_Post object on success, null on failure
139 */
140function get_blog_post( $blog_id, $post_id ) {
141 switch_to_blog( $blog_id );
142 $post = get_post( $post_id );
143 restore_current_blog();
144
145 return $post;
146}
147
148/**
149 * Adds a user to a blog, along with specifying the user's role.
150 *
151 * Use the {@see 'add_user_to_blog'} action to fire an event when users are added to a blog.
152 *
153 * @since MU (3.0.0)
154 *
155 * @param int $blog_id ID of the blog the user is being added to.
156 * @param int $user_id ID of the user being added.
157 * @param string $role User role.
158 * @return true|WP_Error True on success or a WP_Error object if the user doesn't exist
159 * or could not be added.
160 */
161function add_user_to_blog( $blog_id, $user_id, $role ) {
162 switch_to_blog( $blog_id );
163
164 $user = get_userdata( $user_id );
165
166 if ( ! $user ) {
167 restore_current_blog();
168 return new WP_Error( 'user_does_not_exist', __( 'The requested user does not exist.' ) );
169 }
170
171 /**
172 * Filters whether a user should be added to a site.
173 *
174 * @since 4.9.0
175 *
176 * @param true|WP_Error $retval True if the user should be added to the site, error
177 * object otherwise.
178 * @param int $user_id User ID.
179 * @param string $role User role.
180 * @param int $blog_id Site ID.
181 */
182 $can_add_user = apply_filters( 'can_add_user_to_blog', true, $user_id, $role, $blog_id );
183
184 if ( true !== $can_add_user ) {
185 restore_current_blog();
186
187 if ( is_wp_error( $can_add_user ) ) {
188 return $can_add_user;
189 }
190
191 return new WP_Error( 'user_cannot_be_added', __( 'User cannot be added to this site.' ) );
192 }
193
194 if ( ! get_user_meta( $user_id, 'primary_blog', true ) ) {
195 update_user_meta( $user_id, 'primary_blog', $blog_id );
196 $site = get_site( $blog_id );
197 update_user_meta( $user_id, 'source_domain', $site->domain );
198 }
199
200 $user->set_role( $role );
201
202 /**
203 * Fires immediately after a user is added to a site.
204 *
205 * @since MU (3.0.0)
206 *
207 * @param int $user_id User ID.
208 * @param string $role User role.
209 * @param int $blog_id Blog ID.
210 */
211 do_action( 'add_user_to_blog', $user_id, $role, $blog_id );
212
213 clean_user_cache( $user_id );
214 wp_cache_delete( $blog_id . '_user_count', 'blog-details' );
215
216 restore_current_blog();
217
218 return true;
219}
220
221/**
222 * Removes a user from a blog.
223 *
224 * Use the {@see 'remove_user_from_blog'} action to fire an event when
225 * users are removed from a blog.
226 *
227 * Accepts an optional `$reassign` parameter, if you want to
228 * reassign the user's blog posts to another user upon removal.
229 *
230 * @since MU (3.0.0)
231 *
232 * @global wpdb $wpdb WordPress database abstraction object.
233 *
234 * @param int $user_id ID of the user being removed.
235 * @param int $blog_id Optional. ID of the blog the user is being removed from. Default 0.
236 * @param int $reassign Optional. ID of the user to whom to reassign posts. Default 0.
237 * @return true|WP_Error True on success or a WP_Error object if the user doesn't exist.
238 */
239function remove_user_from_blog( $user_id, $blog_id = 0, $reassign = 0 ) {
240 global $wpdb;
241
242 $user_id = (int) $user_id;
243 $blog_id = (int) $blog_id;
244
245 switch_to_blog( $blog_id );
246
247 /**
248 * Fires before a user is removed from a site.
249 *
250 * @since MU (3.0.0)
251 * @since 5.4.0 Added the `$reassign` parameter.
252 *
253 * @param int $user_id ID of the user being removed.
254 * @param int $blog_id ID of the blog the user is being removed from.
255 * @param int $reassign ID of the user to whom to reassign posts.
256 */
257 do_action( 'remove_user_from_blog', $user_id, $blog_id, $reassign );
258
259 /*
260 * If being removed from the primary blog, set a new primary
261 * if the user is assigned to multiple blogs.
262 */
263 $primary_blog = (int) get_user_meta( $user_id, 'primary_blog', true );
264 if ( $primary_blog === $blog_id ) {
265 $new_id = '';
266 $new_domain = '';
267 $blogs = get_blogs_of_user( $user_id );
268 foreach ( (array) $blogs as $blog ) {
269 if ( $blog->userblog_id === $blog_id ) {
270 continue;
271 }
272 $new_id = $blog->userblog_id;
273 $new_domain = $blog->domain;
274 break;
275 }
276
277 update_user_meta( $user_id, 'primary_blog', $new_id );
278 update_user_meta( $user_id, 'source_domain', $new_domain );
279 }
280
281 $user = get_userdata( $user_id );
282 if ( ! $user ) {
283 restore_current_blog();
284 return new WP_Error( 'user_does_not_exist', __( 'That user does not exist.' ) );
285 }
286
287 $user->remove_all_caps();
288
289 $blogs = get_blogs_of_user( $user_id );
290 if ( count( $blogs ) === 0 ) {
291 update_user_meta( $user_id, 'primary_blog', '' );
292 update_user_meta( $user_id, 'source_domain', '' );
293 }
294
295 if ( $reassign ) {
296 $reassign = (int) $reassign;
297 $post_ids = $wpdb->get_col( $wpdb->prepare( "SELECT ID FROM $wpdb->posts WHERE post_author = %d", $user_id ) );
298 $link_ids = $wpdb->get_col( $wpdb->prepare( "SELECT link_id FROM $wpdb->links WHERE link_owner = %d", $user_id ) );
299
300 if ( ! empty( $post_ids ) ) {
301 $wpdb->query( $wpdb->prepare( "UPDATE $wpdb->posts SET post_author = %d WHERE post_author = %d", $reassign, $user_id ) );
302 array_walk( $post_ids, 'clean_post_cache' );
303 }
304
305 if ( ! empty( $link_ids ) ) {
306 $wpdb->query( $wpdb->prepare( "UPDATE $wpdb->links SET link_owner = %d WHERE link_owner = %d", $reassign, $user_id ) );
307 array_walk( $link_ids, 'clean_bookmark_cache' );
308 }
309 }
310
311 clean_user_cache( $user_id );
312 restore_current_blog();
313
314 return true;
315}
316
317/**
318 * Gets the permalink for a post on another blog.
319 *
320 * @since MU (3.0.0) 1.0
321 *
322 * @param int $blog_id ID of the source blog.
323 * @param int $post_id ID of the desired post.
324 * @return string The post's permalink.
325 */
326function get_blog_permalink( $blog_id, $post_id ) {
327 switch_to_blog( $blog_id );
328 $link = get_permalink( $post_id );
329 restore_current_blog();
330
331 return $link;
332}
333
334/**
335 * Gets a blog's numeric ID from its URL.
336 *
337 * On a subdirectory installation like example.com/blog1/,
338 * $domain will be the root 'example.com' and $path the
339 * subdirectory '/blog1/'. With subdomains like blog1.example.com,
340 * $domain is 'blog1.example.com' and $path is '/'.
341 *
342 * @since MU (3.0.0)
343 *
344 * @global wpdb $wpdb WordPress database abstraction object.
345 *
346 * @param string $domain Website domain.
347 * @param string $path Optional. Not required for subdomain installations. Default '/'.
348 * @return int 0 if no blog found, otherwise the ID of the matching blog.
349 */
350function get_blog_id_from_url( $domain, $path = '/' ) {
351 $domain = strtolower( $domain );
352 $path = strtolower( $path );
353 $id = wp_cache_get( md5( $domain . $path ), 'blog-id-cache' );
354
355 if ( -1 === $id ) { // Blog does not exist.
356 return 0;
357 } elseif ( $id ) {
358 return (int) $id;
359 }
360
361 $args = array(
362 'domain' => $domain,
363 'path' => $path,
364 'fields' => 'ids',
365 'number' => 1,
366 'update_site_meta_cache' => false,
367 );
368 $result = get_sites( $args );
369 $id = array_shift( $result );
370
371 if ( ! $id ) {
372 wp_cache_set( md5( $domain . $path ), -1, 'blog-id-cache' );
373 return 0;
374 }
375
376 wp_cache_set( md5( $domain . $path ), $id, 'blog-id-cache' );
377
378 return $id;
379}
380
381//
382// Admin functions.
383//
384
385/**
386 * Checks an email address against a list of banned domains.
387 *
388 * This function checks against the Banned Email Domains list
389 * at wp-admin/network/settings.php. The check is only run on
390 * self-registrations; user creation at wp-admin/network/users.php
391 * bypasses this check.
392 *
393 * @since MU (3.0.0)
394 *
395 * @param string $user_email The email provided by the user at registration.
396 * @return bool True when the email address is banned, false otherwise.
397 */
398function is_email_address_unsafe( $user_email ) {
399 $banned_names = get_site_option( 'banned_email_domains' );
400 if ( $banned_names && ! is_array( $banned_names ) ) {
401 $banned_names = explode( "\n", $banned_names );
402 }
403
404 $is_email_address_unsafe = false;
405
406 if ( $banned_names && is_array( $banned_names ) && false !== strpos( $user_email, '@', 1 ) ) {
407 $banned_names = array_map( 'strtolower', $banned_names );
408 $normalized_email = strtolower( $user_email );
409
410 list( $email_local_part, $email_domain ) = explode( '@', $normalized_email );
411
412 foreach ( $banned_names as $banned_domain ) {
413 if ( ! $banned_domain ) {
414 continue;
415 }
416
417 if ( $email_domain === $banned_domain ) {
418 $is_email_address_unsafe = true;
419 break;
420 }
421
422 if ( str_ends_with( $normalized_email, ".$banned_domain" ) ) {
423 $is_email_address_unsafe = true;
424 break;
425 }
426 }
427 }
428
429 /**
430 * Filters whether an email address is unsafe.
431 *
432 * @since 3.5.0
433 *
434 * @param bool $is_email_address_unsafe Whether the email address is "unsafe". Default false.
435 * @param string $user_email User email address.
436 */
437 return apply_filters( 'is_email_address_unsafe', $is_email_address_unsafe, $user_email );
438}
439
440/**
441 * Sanitizes and validates data required for a user sign-up.
442 *
443 * Verifies the validity and uniqueness of user names and user email addresses,
444 * and checks email addresses against allowed and disallowed domains provided by
445 * administrators.
446 *
447 * The {@see 'wpmu_validate_user_signup'} hook provides an easy way to modify the sign-up
448 * process. The value $result, which is passed to the hook, contains both the user-provided
449 * info and the error messages created by the function. {@see 'wpmu_validate_user_signup'}
450 * allows you to process the data in any way you'd like, and unset the relevant errors if
451 * necessary.
452 *
453 * @since MU (3.0.0)
454 *
455 * @global wpdb $wpdb WordPress database abstraction object.
456 *
457 * @param string $user_name The login name provided by the user.
458 * @param string $user_email The email provided by the user.
459 * @return array {
460 * The array of user name, email, and the error messages.
461 *
462 * @type string $user_name Sanitized and unique username.
463 * @type string $orig_username Original username.
464 * @type string $user_email User email address.
465 * @type WP_Error $errors WP_Error object containing any errors found.
466 * }
467 */
468function wpmu_validate_user_signup( $user_name, $user_email ) {
469 global $wpdb;
470
471 $errors = new WP_Error();
472
473 $orig_username = $user_name;
474 $user_name = preg_replace( '/\s+/', '', sanitize_user( $user_name, true ) );
475
476 if ( $user_name !== $orig_username || preg_match( '/[^a-z0-9]/', $user_name ) ) {
477 $errors->add( 'user_name', __( 'Usernames can only contain lowercase letters (a-z) and numbers.' ) );
478 $user_name = $orig_username;
479 }
480
481 $user_email = sanitize_email( $user_email );
482
483 if ( empty( $user_name ) ) {
484 $errors->add( 'user_name', __( 'Please enter a username.' ) );
485 }
486
487 $illegal_names = get_site_option( 'illegal_names' );
488
489 if ( ! is_array( $illegal_names ) ) {
490 $illegal_names = array( 'www', 'web', 'root', 'admin', 'main', 'invite', 'administrator' );
491 add_site_option( 'illegal_names', $illegal_names );
492 }
493
494 if ( in_array( $user_name, $illegal_names, true ) ) {
495 $errors->add( 'user_name', __( 'Sorry, that username is not allowed.' ) );
496 }
497
498 /** This filter is documented in wp-includes/user.php */
499 $illegal_logins = (array) apply_filters( 'illegal_user_logins', array() );
500
501 if ( in_array( strtolower( $user_name ), array_map( 'strtolower', $illegal_logins ), true ) ) {
502 $errors->add( 'user_name', __( 'Sorry, that username is not allowed.' ) );
503 }
504
505 if ( ! is_email( $user_email ) ) {
506 $errors->add( 'user_email', __( 'Please enter a valid email address.' ) );
507 } elseif ( is_email_address_unsafe( $user_email ) ) {
508 $errors->add( 'user_email', __( 'You cannot use that email address to signup. There are problems with them blocking some emails from WordPress. Please use another email provider.' ) );
509 }
510
511 if ( strlen( $user_name ) < 4 ) {
512 $errors->add( 'user_name', __( 'Username must be at least 4 characters.' ) );
513 }
514
515 if ( strlen( $user_name ) > 60 ) {
516 $errors->add( 'user_name', __( 'Username may not be longer than 60 characters.' ) );
517 }
518
519 // All numeric?
520 if ( preg_match( '/^[0-9]*$/', $user_name ) ) {
521 $errors->add( 'user_name', __( 'Sorry, usernames must have letters too!' ) );
522 }
523
524 $limited_email_domains = get_site_option( 'limited_email_domains' );
525
526 if ( is_array( $limited_email_domains ) && ! empty( $limited_email_domains ) ) {
527 $limited_email_domains = array_map( 'strtolower', $limited_email_domains );
528 $email_domain = strtolower( substr( $user_email, 1 + strpos( $user_email, '@' ) ) );
529
530 if ( ! in_array( $email_domain, $limited_email_domains, true ) ) {
531 $errors->add( 'user_email', __( 'Sorry, that email address is not allowed!' ) );
532 }
533 }
534
535 // Check if the username has been used already.
536 if ( username_exists( $user_name ) ) {
537 $errors->add( 'user_name', __( 'Sorry, that username already exists!' ) );
538 }
539
540 // Check if the email address has been used already.
541 if ( email_exists( $user_email ) ) {
542 $errors->add(
543 'user_email',
544 sprintf(
545 /* translators: %s: Link to the login page. */
546 __( '<strong>Error:</strong> This email address is already registered. <a href="%s">Log in</a> with this address or choose another one.' ),
547 wp_login_url()
548 )
549 );
550 }
551
552 // Has someone already signed up for this username?
553 $signup = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM $wpdb->signups WHERE user_login = %s", $user_name ) );
554 if ( $signup instanceof stdClass ) {
555 $registered_at = mysql2date( 'U', $signup->registered );
556 $now = time();
557 $diff = $now - $registered_at;
558 // If registered more than two days ago, cancel registration and let this signup go through.
559 if ( $diff > 2 * DAY_IN_SECONDS ) {
560 $wpdb->delete( $wpdb->signups, array( 'user_login' => $user_name ) );
561 } else {
562 $errors->add( 'user_name', __( 'That username is currently reserved but may be available in a couple of days.' ) );
563 }
564 }
565
566 $signup = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM $wpdb->signups WHERE user_email = %s", $user_email ) );
567 if ( $signup instanceof stdClass ) {
568 $diff = time() - mysql2date( 'U', $signup->registered );
569 // If registered more than two days ago, cancel registration and let this signup go through.
570 if ( $diff > 2 * DAY_IN_SECONDS ) {
571 $wpdb->delete( $wpdb->signups, array( 'user_email' => $user_email ) );
572 } else {
573 $errors->add( 'user_email', __( 'That email address is pending activation and is not available for new registration. If you made a previous attempt with this email address, please check your inbox for an activation email. If left unconfirmed, it will become available in a couple of days.' ) );
574 }
575 }
576
577 $result = array(
578 'user_name' => $user_name,
579 'orig_username' => $orig_username,
580 'user_email' => $user_email,
581 'errors' => $errors,
582 );
583
584 /**
585 * Filters the validated user registration details.
586 *
587 * This does not allow you to override the username or email of the user during
588 * registration. The values are solely used for validation and error handling.
589 *
590 * @since MU (3.0.0)
591 *
592 * @param array $result {
593 * The array of user name, email, and the error messages.
594 *
595 * @type string $user_name Sanitized and unique username.
596 * @type string $orig_username Original username.
597 * @type string $user_email User email address.
598 * @type WP_Error $errors WP_Error object containing any errors found.
599 * }
600 */
601 return apply_filters( 'wpmu_validate_user_signup', $result );
602}
603
604/**
605 * Processes new site registrations.
606 *
607 * Checks the data provided by the user during blog signup. Verifies
608 * the validity and uniqueness of blog paths and domains.
609 *
610 * This function prevents the current user from registering a new site
611 * with a blogname equivalent to another user's login name. Passing the
612 * $user parameter to the function, where $user is the other user, is
613 * effectively an override of this limitation.
614 *
615 * Filter {@see 'wpmu_validate_blog_signup'} if you want to modify
616 * the way that WordPress validates new site signups.
617 *
618 * @since MU (3.0.0)
619 *
620 * @global wpdb $wpdb WordPress database abstraction object.
621 * @global string $domain
622 *
623 * @param string $blogname The site name provided by the user. Must be unique.
624 * @param string $blog_title The site title provided by the user.
625 * @param WP_User|string $user Optional. The user object to check against the new site name.
626 * Default empty string.
627 * @return array {
628 * Array of domain, path, site name, site title, user and error messages.
629 *
630 * @type string $domain Domain for the site.
631 * @type string $path Path for the site. Used in subdirectory installations.
632 * @type string $blogname The unique site name (slug).
633 * @type string $blog_title Blog title.
634 * @type string|WP_User $user By default, an empty string. A user object if provided.
635 * @type WP_Error $errors WP_Error containing any errors found.
636 * }
637 */
638function wpmu_validate_blog_signup( $blogname, $blog_title, $user = '' ) {
639 global $wpdb, $domain;
640
641 $current_network = get_network();
642 $base = $current_network->path;
643
644 $blog_title = strip_tags( $blog_title );
645
646 $errors = new WP_Error();
647 $illegal_names = get_site_option( 'illegal_names' );
648
649 if ( ! is_array( $illegal_names ) ) {
650 $illegal_names = array( 'www', 'web', 'root', 'admin', 'main', 'invite', 'administrator' );
651 add_site_option( 'illegal_names', $illegal_names );
652 }
653
654 /*
655 * On sub dir installations, some names are so illegal, only a filter can
656 * spring them from jail.
657 */
658 if ( ! is_subdomain_install() ) {
659 $illegal_names = array_merge( $illegal_names, get_subdirectory_reserved_names() );
660 }
661
662 if ( empty( $blogname ) ) {
663 $errors->add( 'blogname', __( 'Please enter a site name.' ) );
664 }
665
666 if ( preg_match( '/[^a-z0-9]+/', $blogname ) ) {
667 $errors->add( 'blogname', __( 'Site names can only contain lowercase letters (a-z) and numbers.' ) );
668 }
669
670 if ( in_array( $blogname, $illegal_names, true ) ) {
671 $errors->add( 'blogname', __( 'That name is not allowed.' ) );
672 }
673
674 /**
675 * Filters the minimum site name length required when validating a site signup.
676 *
677 * @since 4.8.0
678 *
679 * @param int $length The minimum site name length. Default 4.
680 */
681 $minimum_site_name_length = apply_filters( 'minimum_site_name_length', 4 );
682
683 if ( strlen( $blogname ) < $minimum_site_name_length ) {
684 /* translators: %s: Minimum site name length. */
685 $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 ) ) );
686 }
687
688 // Do not allow users to create a site that conflicts with a page on the main blog.
689 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 ) ) ) {
690 $errors->add( 'blogname', __( 'Sorry, you may not use that site name.' ) );
691 }
692
693 // All numeric?
694 if ( preg_match( '/^[0-9]*$/', $blogname ) ) {
695 $errors->add( 'blogname', __( 'Sorry, site names must have letters too!' ) );
696 }
697
698 /**
699 * Filters the new site name during registration.
700 *
701 * The name is the site's subdomain or the site's subdirectory
702 * path depending on the network settings.
703 *
704 * @since MU (3.0.0)
705 *
706 * @param string $blogname Site name.
707 */
708 $blogname = apply_filters( 'newblogname', $blogname );
709
710 $blog_title = wp_unslash( $blog_title );
711
712 if ( empty( $blog_title ) ) {
713 $errors->add( 'blog_title', __( 'Please enter a site title.' ) );
714 }
715
716 // Check if the domain/path has been used already.
717 if ( is_subdomain_install() ) {
718 $mydomain = $blogname . '.' . preg_replace( '|^www\.|', '', $domain );
719 $path = $base;
720 } else {
721 $mydomain = $domain;
722 $path = $base . $blogname . '/';
723 }
724 if ( domain_exists( $mydomain, $path, $current_network->id ) ) {
725 $errors->add( 'blogname', __( 'Sorry, that site already exists!' ) );
726 }
727
728 /*
729 * Do not allow users to create a site that matches an existing user's login name,
730 * unless it's the user's own username.
731 */
732 if ( username_exists( $blogname ) ) {
733 if ( ! $user instanceof WP_User || $user->user_login !== $blogname ) {
734 $errors->add( 'blogname', __( 'Sorry, that site is reserved!' ) );
735 }
736 }
737
738 /*
739 * Has someone already signed up for this domain?
740 * TODO: Check email too?
741 */
742 $signup = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM $wpdb->signups WHERE domain = %s AND path = %s", $mydomain, $path ) );
743 if ( $signup instanceof stdClass ) {
744 $diff = time() - mysql2date( 'U', $signup->registered );
745 // If registered more than two days ago, cancel registration and let this signup go through.
746 if ( $diff > 2 * DAY_IN_SECONDS ) {
747 $wpdb->delete(
748 $wpdb->signups,
749 array(
750 'domain' => $mydomain,
751 'path' => $path,
752 )
753 );
754 } else {
755 $errors->add( 'blogname', __( 'That site is currently reserved but may be available in a couple days.' ) );
756 }
757 }
758
759 $result = array(
760 'domain' => $mydomain,
761 'path' => $path,
762 'blogname' => $blogname,
763 'blog_title' => $blog_title,
764 'user' => $user,
765 'errors' => $errors,
766 );
767
768 /**
769 * Filters site details and error messages following registration.
770 *
771 * @since MU (3.0.0)
772 *
773 * @param array $result {
774 * Array of domain, path, site name, site title, user and error messages.
775 *
776 * @type string $domain Domain for the site.
777 * @type string $path Path for the site. Used in subdirectory installations.
778 * @type string $blogname The unique site name (slug).
779 * @type string $blog_title Site title.
780 * @type string|WP_User $user By default, an empty string. A user object if provided.
781 * @type WP_Error $errors WP_Error containing any errors found.
782 * }
783 */
784 return apply_filters( 'wpmu_validate_blog_signup', $result );
785}
786
787/**
788 * Records site signup information for future activation.
789 *
790 * @since MU (3.0.0)
791 *
792 * @global wpdb $wpdb WordPress database abstraction object.
793 *
794 * @param string $domain The requested domain.
795 * @param string $path The requested path.
796 * @param string $title The requested site title.
797 * @param string $user The user's requested login name.
798 * @param string $user_email The user's email address.
799 * @param array $meta Optional. Signup meta data. By default, contains the requested privacy setting and lang_id.
800 */
801function wpmu_signup_blog( $domain, $path, $title, $user, $user_email, $meta = array() ) {
802 global $wpdb;
803
804 $key = substr( md5( time() . wp_rand() . $domain ), 0, 16 );
805
806 /**
807 * Filters the metadata for a site signup.
808 *
809 * The metadata will be serialized prior to storing it in the database.
810 *
811 * @since 4.8.0
812 *
813 * @param array $meta Signup meta data. Default empty array.
814 * @param string $domain The requested domain.
815 * @param string $path The requested path.
816 * @param string $title The requested site title.
817 * @param string $user The user's requested login name.
818 * @param string $user_email The user's email address.
819 * @param string $key The user's activation key.
820 */
821 $meta = apply_filters( 'signup_site_meta', $meta, $domain, $path, $title, $user, $user_email, $key );
822
823 $wpdb->insert(
824 $wpdb->signups,
825 array(
826 'domain' => $domain,
827 'path' => $path,
828 'title' => $title,
829 'user_login' => $user,
830 'user_email' => $user_email,
831 'registered' => current_time( 'mysql', true ),
832 'activation_key' => $key,
833 'meta' => serialize( $meta ),
834 )
835 );
836
837 /**
838 * Fires after site signup information has been written to the database.
839 *
840 * @since 4.4.0
841 *
842 * @param string $domain The requested domain.
843 * @param string $path The requested path.
844 * @param string $title The requested site title.
845 * @param string $user The user's requested login name.
846 * @param string $user_email The user's email address.
847 * @param string $key The user's activation key.
848 * @param array $meta Signup meta data. By default, contains the requested privacy setting and lang_id.
849 */
850 do_action( 'after_signup_site', $domain, $path, $title, $user, $user_email, $key, $meta );
851}
852
853/**
854 * Records user signup information for future activation.
855 *
856 * This function is used when user registration is open but
857 * new site registration is not.
858 *
859 * @since MU (3.0.0)
860 *
861 * @global wpdb $wpdb WordPress database abstraction object.
862 *
863 * @param string $user The user's requested login name.
864 * @param string $user_email The user's email address.
865 * @param array $meta Optional. Signup meta data. Default empty array.
866 */
867function wpmu_signup_user( $user, $user_email, $meta = array() ) {
868 global $wpdb;
869
870 // Format data.
871 $user = preg_replace( '/\s+/', '', sanitize_user( $user, true ) );
872 $user_email = sanitize_email( $user_email );
873 $key = substr( md5( time() . wp_rand() . $user_email ), 0, 16 );
874
875 /**
876 * Filters the metadata for a user signup.
877 *
878 * The metadata will be serialized prior to storing it in the database.
879 *
880 * @since 4.8.0
881 *
882 * @param array $meta Signup meta data. Default empty array.
883 * @param string $user The user's requested login name.
884 * @param string $user_email The user's email address.
885 * @param string $key The user's activation key.
886 */
887 $meta = apply_filters( 'signup_user_meta', $meta, $user, $user_email, $key );
888
889 $wpdb->insert(
890 $wpdb->signups,
891 array(
892 'domain' => '',
893 'path' => '',
894 'title' => '',
895 'user_login' => $user,
896 'user_email' => $user_email,
897 'registered' => current_time( 'mysql', true ),
898 'activation_key' => $key,
899 'meta' => serialize( $meta ),
900 )
901 );
902
903 /**
904 * Fires after a user's signup information has been written to the database.
905 *
906 * @since 4.4.0
907 *
908 * @param string $user The user's requested login name.
909 * @param string $user_email The user's email address.
910 * @param string $key The user's activation key.
911 * @param array $meta Signup meta data. Default empty array.
912 */
913 do_action( 'after_signup_user', $user, $user_email, $key, $meta );
914}
915
916/**
917 * Sends a confirmation request email to a user when they sign up for a new site. The new site will not become active
918 * until the confirmation link is clicked.
919 *
920 * This is the notification function used when site registration
921 * is enabled.
922 *
923 * Filter {@see 'wpmu_signup_blog_notification'} to bypass this function or
924 * replace it with your own notification behavior.
925 *
926 * Filter {@see 'wpmu_signup_blog_notification_email'} and
927 * {@see 'wpmu_signup_blog_notification_subject'} to change the content
928 * and subject line of the email sent to newly registered users.
929 *
930 * @since MU (3.0.0)
931 *
932 * @param string $domain The new blog domain.
933 * @param string $path The new blog path.
934 * @param string $title The site title.
935 * @param string $user_login The user's login name.
936 * @param string $user_email The user's email address.
937 * @param string $key The activation key created in wpmu_signup_blog().
938 * @param array $meta Optional. Signup meta data. By default, contains the requested privacy setting and lang_id.
939 * @return bool
940 */
941function wpmu_signup_blog_notification(
942 $domain,
943 $path,
944 $title,
945 $user_login,
946 $user_email,
947 #[\SensitiveParameter]
948 $key,
949 $meta = array()
950) {
951 /**
952 * Filters whether to bypass the new site email notification.
953 *
954 * @since MU (3.0.0)
955 *
956 * @param string|false $domain Site domain, or false to prevent the email from sending.
957 * @param string $path Site path.
958 * @param string $title Site title.
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_blog().
962 * @param array $meta Signup meta data. By default, contains the requested privacy setting and lang_id.
963 */
964 if ( ! apply_filters( 'wpmu_signup_blog_notification', $domain, $path, $title, $user_login, $user_email, $key, $meta ) ) {
965 return false;
966 }
967
968 // Send email with activation link.
969 if ( ! is_subdomain_install() || get_current_network_id() !== 1 ) {
970 $activate_url = network_site_url( "wp-activate.php?key=$key" );
971 } else {
972 $activate_url = "http://{$domain}{$path}wp-activate.php?key=$key"; // @todo Use *_url() API.
973 }
974
975 $activate_url = esc_url( $activate_url );
976
977 $admin_email = get_site_option( 'admin_email' );
978
979 if ( '' === $admin_email ) {
980 $admin_email = 'support@' . wp_parse_url( network_home_url(), PHP_URL_HOST );
981 }
982
983 $from_name = ( '' !== get_site_option( 'site_name' ) ) ? esc_html( get_site_option( 'site_name' ) ) : 'WordPress';
984 $message_headers = "From: \"{$from_name}\" <{$admin_email}>\n" . 'Content-Type: text/plain; charset="' . get_option( 'blog_charset' ) . "\"\n";
985
986 $user = get_user_by( 'login', $user_login );
987 $switched_locale = $user && switch_to_user_locale( $user->ID );
988
989 $message = sprintf(
990 /**
991 * Filters the message content of the new blog notification email.
992 *
993 * Content should be formatted for transmission via wp_mail().
994 *
995 * @since MU (3.0.0)
996 *
997 * @param string $content Content of the notification email.
998 * @param string $domain Site domain.
999 * @param string $path Site path.
1000 * @param string $title Site title.
1001 * @param string $user_login User login name.
1002 * @param string $user_email User email address.
1003 * @param string $key Activation key created in wpmu_signup_blog().
1004 * @param array $meta Signup meta data. By default, contains the requested privacy setting and lang_id.
1005 */
1006 apply_filters(
1007 'wpmu_signup_blog_notification_email',
1008 /* translators: New site notification email. 1: Activation URL, 2: New site URL. */
1009 __( "To activate your site, please click the following link:\n\n%1\$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%2\$s" ),
1010 $domain,
1011 $path,
1012 $title,
1013 $user_login,
1014 $user_email,
1015 $key,
1016 $meta
1017 ),
1018 $activate_url,
1019 esc_url( "http://{$domain}{$path}" ),
1020 $key
1021 );
1022
1023 $subject = sprintf(
1024 /**
1025 * Filters the subject of the new blog notification email.
1026 *
1027 * @since MU (3.0.0)
1028 *
1029 * @param string $subject Subject of the notification email.
1030 * @param string $domain Site domain.
1031 * @param string $path Site path.
1032 * @param string $title Site title.
1033 * @param string $user_login User login name.
1034 * @param string $user_email User email address.
1035 * @param string $key Activation key created in wpmu_signup_blog().
1036 * @param array $meta Signup meta data. By default, contains the requested privacy setting and lang_id.
1037 */
1038 apply_filters(
1039 'wpmu_signup_blog_notification_subject',
1040 /* translators: New site notification email subject. 1: Network title, 2: New site URL. */
1041 _x( '[%1$s] Activate %2$s', 'New site notification email subject' ),
1042 $domain,
1043 $path,
1044 $title,
1045 $user_login,
1046 $user_email,
1047 $key,
1048 $meta
1049 ),
1050 $from_name,
1051 esc_url( 'http://' . $domain . $path )
1052 );
1053
1054 wp_mail( $user_email, wp_specialchars_decode( $subject ), $message, $message_headers );
1055
1056 if ( $switched_locale ) {
1057 restore_previous_locale();
1058 }
1059
1060 return true;
1061}
1062
1063/**
1064 * Sends a confirmation request email to a user when they sign up for a new user account (without signing up for a site
1065 * at the same time). The user account will not become active until the confirmation link is clicked.
1066 *
1067 * This is the notification function used when no new site has
1068 * been requested.
1069 *
1070 * Filter {@see 'wpmu_signup_user_notification'} to bypass this function or
1071 * replace it with your own notification behavior.
1072 *
1073 * Filter {@see 'wpmu_signup_user_notification_email'} and
1074 * {@see 'wpmu_signup_user_notification_subject'} to change the content
1075 * and subject line of the email sent to newly registered users.
1076 *
1077 * @since MU (3.0.0)
1078 *
1079 * @param string $user_login The user's login name.
1080 * @param string $user_email The user's email address.
1081 * @param string $key The activation key created in wpmu_signup_user()
1082 * @param array $meta Optional. Signup meta data. Default empty array.
1083 * @return bool
1084 */
1085function wpmu_signup_user_notification(
1086 $user_login,
1087 $user_email,
1088 #[\SensitiveParameter]
1089 $key,
1090 $meta = array()
1091) {
1092 /**
1093 * Filters whether to bypass the email notification for new user sign-up.
1094 *
1095 * @since MU (3.0.0)
1096 *
1097 * @param string $user_login User login name.
1098 * @param string $user_email User email address.
1099 * @param string $key Activation key created in wpmu_signup_user().
1100 * @param array $meta Signup meta data. Default empty array.
1101 */
1102 if ( ! apply_filters( 'wpmu_signup_user_notification', $user_login, $user_email, $key, $meta ) ) {
1103 return false;
1104 }
1105
1106 $user = get_user_by( 'login', $user_login );
1107 $switched_locale = $user && switch_to_user_locale( $user->ID );
1108
1109 // Send email with activation link.
1110 $admin_email = get_site_option( 'admin_email' );
1111
1112 if ( '' === $admin_email ) {
1113 $admin_email = 'support@' . wp_parse_url( network_home_url(), PHP_URL_HOST );
1114 }
1115
1116 $from_name = ( '' !== get_site_option( 'site_name' ) ) ? esc_html( get_site_option( 'site_name' ) ) : 'WordPress';
1117 $message_headers = "From: \"{$from_name}\" <{$admin_email}>\n" . 'Content-Type: text/plain; charset="' . get_option( 'blog_charset' ) . "\"\n";
1118 $message = sprintf(
1119 /**
1120 * Filters the content of the notification email for new user sign-up.
1121 *
1122 * Content should be formatted for transmission via wp_mail().
1123 *
1124 * @since MU (3.0.0)
1125 *
1126 * @param string $content Content of the notification email.
1127 * @param string $user_login User login name.
1128 * @param string $user_email User email address.
1129 * @param string $key Activation key created in wpmu_signup_user().
1130 * @param array $meta Signup meta data. Default empty array.
1131 */
1132 apply_filters(
1133 'wpmu_signup_user_notification_email',
1134 /* translators: New user notification email. %s: Activation URL. */
1135 __( "To activate your user, please click the following link:\n\n%s\n\nAfter you activate, you will receive *another email* with your login." ),
1136 $user_login,
1137 $user_email,
1138 $key,
1139 $meta
1140 ),
1141 site_url( "wp-activate.php?key=$key" )
1142 );
1143
1144 $subject = sprintf(
1145 /**
1146 * Filters the subject of the notification email of new user signup.
1147 *
1148 * @since MU (3.0.0)
1149 *
1150 * @param string $subject Subject of the notification email.
1151 * @param string $user_login User login name.
1152 * @param string $user_email User email address.
1153 * @param string $key Activation key created in wpmu_signup_user().
1154 * @param array $meta Signup meta data. Default empty array.
1155 */
1156 apply_filters(
1157 'wpmu_signup_user_notification_subject',
1158 /* translators: New user notification email subject. 1: Network title, 2: New user login. */
1159 _x( '[%1$s] Activate %2$s', 'New user notification email subject' ),
1160 $user_login,
1161 $user_email,
1162 $key,
1163 $meta
1164 ),
1165 $from_name,
1166 $user_login
1167 );
1168
1169 wp_mail( $user_email, wp_specialchars_decode( $subject ), $message, $message_headers );
1170
1171 if ( $switched_locale ) {
1172 restore_previous_locale();
1173 }
1174
1175 return true;
1176}
1177
1178/**
1179 * Activates a signup.
1180 *
1181 * Hook to {@see 'wpmu_activate_user'} or {@see 'wpmu_activate_blog'} for events
1182 * that should happen only when users or sites are self-created (since
1183 * those actions are not called when users and sites are created
1184 * by a Super Admin).
1185 *
1186 * @since MU (3.0.0)
1187 *
1188 * @global wpdb $wpdb WordPress database abstraction object.
1189 *
1190 * @param string $key The activation key provided to the user.
1191 * @return array|WP_Error An array containing information about the activated user and/or blog.
1192 */
1193function wpmu_activate_signup(
1194 #[\SensitiveParameter]
1195 $key
1196) {
1197 global $wpdb;
1198
1199 $signup = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM $wpdb->signups WHERE activation_key = %s", $key ) );
1200
1201 if ( empty( $signup ) ) {
1202 return new WP_Error( 'invalid_key', __( 'Invalid activation key.' ) );
1203 }
1204
1205 if ( $signup->active ) {
1206 if ( empty( $signup->domain ) ) {
1207 return new WP_Error( 'already_active', __( 'The user is already active.' ), $signup );
1208 } else {
1209 return new WP_Error( 'already_active', __( 'The site is already active.' ), $signup );
1210 }
1211 }
1212
1213 $meta = maybe_unserialize( $signup->meta );
1214 $password = wp_generate_password( 12, false );
1215
1216 $user_id = username_exists( $signup->user_login );
1217
1218 if ( ! $user_id ) {
1219 $user_id = wpmu_create_user( $signup->user_login, $password, $signup->user_email );
1220 } else {
1221 $user_already_exists = true;
1222 }
1223
1224 if ( ! $user_id ) {
1225 return new WP_Error( 'create_user', __( 'Could not create user' ), $signup );
1226 }
1227
1228 $now = current_time( 'mysql', true );
1229
1230 if ( empty( $signup->domain ) ) {
1231 $wpdb->update(
1232 $wpdb->signups,
1233 array(
1234 'active' => 1,
1235 'activated' => $now,
1236 ),
1237 array( 'activation_key' => $key )
1238 );
1239
1240 if ( isset( $user_already_exists ) ) {
1241 return new WP_Error( 'user_already_exists', __( 'That username is already activated.' ), $signup );
1242 }
1243
1244 /**
1245 * Fires immediately after a new user is activated.
1246 *
1247 * @since MU (3.0.0)
1248 *
1249 * @param int $user_id User ID.
1250 * @param string $password User password.
1251 * @param array $meta Signup meta data.
1252 */
1253 do_action( 'wpmu_activate_user', $user_id, $password, $meta );
1254
1255 return array(
1256 'user_id' => $user_id,
1257 'password' => $password,
1258 'meta' => $meta,
1259 );
1260 }
1261
1262 $blog_id = wpmu_create_blog( $signup->domain, $signup->path, $signup->title, $user_id, $meta, get_current_network_id() );
1263
1264 // TODO: What to do if we create a user but cannot create a blog?
1265 if ( is_wp_error( $blog_id ) ) {
1266 /*
1267 * If blog is taken, that means a previous attempt to activate this blog
1268 * failed in between creating the blog and setting the activation flag.
1269 * Let's just set the active flag and instruct the user to reset their password.
1270 */
1271 if ( 'blog_taken' === $blog_id->get_error_code() ) {
1272 $blog_id->add_data( $signup );
1273 $wpdb->update(
1274 $wpdb->signups,
1275 array(
1276 'active' => 1,
1277 'activated' => $now,
1278 ),
1279 array( 'activation_key' => $key )
1280 );
1281 }
1282 return $blog_id;
1283 }
1284
1285 $wpdb->update(
1286 $wpdb->signups,
1287 array(
1288 'active' => 1,
1289 'activated' => $now,
1290 ),
1291 array( 'activation_key' => $key )
1292 );
1293
1294 /**
1295 * Fires immediately after a site is activated.
1296 *
1297 * @since MU (3.0.0)
1298 *
1299 * @param int $blog_id Blog ID.
1300 * @param int $user_id User ID.
1301 * @param string $password User password.
1302 * @param string $signup_title Site title.
1303 * @param array $meta Signup meta data. By default, contains the requested privacy setting and lang_id.
1304 */
1305 do_action( 'wpmu_activate_blog', $blog_id, $user_id, $password, $signup->title, $meta );
1306
1307 return array(
1308 'blog_id' => $blog_id,
1309 'user_id' => $user_id,
1310 'password' => $password,
1311 'title' => $signup->title,
1312 'meta' => $meta,
1313 );
1314}
1315
1316/**
1317 * Deletes an associated signup entry when a user is deleted from the database.
1318 *
1319 * @since 5.5.0
1320 *
1321 * @global wpdb $wpdb WordPress database abstraction object.
1322 *
1323 * @param int $id ID of the user to delete.
1324 * @param int|null $reassign ID of the user to reassign posts and links to.
1325 * @param WP_User $user User object.
1326 */
1327function wp_delete_signup_on_user_delete( $id, $reassign, $user ) {
1328 global $wpdb;
1329
1330 $wpdb->delete( $wpdb->signups, array( 'user_login' => $user->user_login ) );
1331}
1332
1333/**
1334 * Creates a user.
1335 *
1336 * This function runs when a user self-registers as well as when
1337 * a Super Admin creates a new user. Hook to {@see 'wpmu_new_user'} for events
1338 * that should affect all new users, but only on Multisite (otherwise
1339 * use {@see 'user_register'}).
1340 *
1341 * @since MU (3.0.0)
1342 *
1343 * @param string $user_name The new user's login name.
1344 * @param string $password The new user's password.
1345 * @param string $email The new user's email address.
1346 * @return int|false Returns false on failure, or int $user_id on success.
1347 */
1348function wpmu_create_user(
1349 $user_name,
1350 #[\SensitiveParameter]
1351 $password,
1352 $email
1353) {
1354 $user_name = preg_replace( '/\s+/', '', sanitize_user( $user_name, true ) );
1355
1356 $user_id = wp_create_user( $user_name, $password, $email );
1357 if ( is_wp_error( $user_id ) ) {
1358 return false;
1359 }
1360
1361 // Newly created users have no roles or caps until they are added to a blog.
1362 delete_user_option( $user_id, 'capabilities' );
1363 delete_user_option( $user_id, 'user_level' );
1364
1365 /**
1366 * Fires immediately after a new user is created.
1367 *
1368 * @since MU (3.0.0)
1369 *
1370 * @param int $user_id User ID.
1371 */
1372 do_action( 'wpmu_new_user', $user_id );
1373
1374 return $user_id;
1375}
1376
1377/**
1378 * Creates a site.
1379 *
1380 * This function runs when a user self-registers a new site as well
1381 * as when a Super Admin creates a new site. Hook to {@see 'wpmu_new_blog'}
1382 * for events that should affect all new sites.
1383 *
1384 * On subdirectory installations, $domain is the same as the main site's
1385 * domain, and the path is the subdirectory name (eg 'example.com'
1386 * and '/blog1/'). On subdomain installations, $domain is the new subdomain +
1387 * root domain (eg 'blog1.example.com'), and $path is '/'.
1388 *
1389 * @since MU (3.0.0)
1390 *
1391 * @param string $domain The new site's domain.
1392 * @param string $path The new site's path.
1393 * @param string $title The new site's title.
1394 * @param int $user_id The user ID of the new site's admin.
1395 * @param array $options Optional. Array of key=>value pairs used to set initial site options.
1396 * If valid status keys are included ('public', 'archived', 'mature',
1397 * 'spam', 'deleted', or 'lang_id') the given site status(es) will be
1398 * updated. Otherwise, keys and values will be used to set options for
1399 * the new site. Default empty array.
1400 * @param int $network_id Optional. Network ID. Only relevant on multi-network installations.
1401 * Default 1.
1402 * @return int|WP_Error Returns WP_Error object on failure, the new site ID on success.
1403 */
1404function wpmu_create_blog( $domain, $path, $title, $user_id, $options = array(), $network_id = 1 ) {
1405 $defaults = array(
1406 'public' => 0,
1407 );
1408 $options = wp_parse_args( $options, $defaults );
1409
1410 $title = strip_tags( $title );
1411 $user_id = (int) $user_id;
1412
1413 // Check if the domain has been used already. We should return an error message.
1414 if ( domain_exists( $domain, $path, $network_id ) ) {
1415 return new WP_Error( 'blog_taken', __( 'Sorry, that site already exists!' ) );
1416 }
1417
1418 if ( ! wp_installing() ) {
1419 wp_installing( true );
1420 }
1421
1422 $allowed_data_fields = array( 'public', 'archived', 'mature', 'spam', 'deleted', 'lang_id' );
1423
1424 $site_data = array_merge(
1425 array(
1426 'domain' => $domain,
1427 'path' => $path,
1428 'network_id' => $network_id,
1429 ),
1430 array_intersect_key( $options, array_flip( $allowed_data_fields ) )
1431 );
1432
1433 // Data to pass to wp_initialize_site().
1434 $site_initialization_data = array(
1435 'title' => $title,
1436 'user_id' => $user_id,
1437 'options' => array_diff_key( $options, array_flip( $allowed_data_fields ) ),
1438 );
1439
1440 $blog_id = wp_insert_site( array_merge( $site_data, $site_initialization_data ) );
1441
1442 if ( is_wp_error( $blog_id ) ) {
1443 return $blog_id;
1444 }
1445
1446 wp_cache_set_sites_last_changed();
1447
1448 return $blog_id;
1449}
1450
1451/**
1452 * Notifies the network admin that a new site has been activated.
1453 *
1454 * Filter {@see 'newblog_notify_siteadmin'} to change the content of
1455 * the notification email.
1456 *
1457 * @since MU (3.0.0)
1458 * @since 5.1.0 $blog_id now supports input from the {@see 'wp_initialize_site'} action.
1459 *
1460 * @param WP_Site|int $blog_id The new site's object or ID.
1461 * @param string $deprecated Not used.
1462 * @return bool
1463 */
1464function newblog_notify_siteadmin( $blog_id, $deprecated = '' ) {
1465 if ( is_object( $blog_id ) ) {
1466 $blog_id = $blog_id->blog_id;
1467 }
1468
1469 if ( 'yes' !== get_site_option( 'registrationnotification' ) ) {
1470 return false;
1471 }
1472
1473 $email = get_site_option( 'admin_email' );
1474
1475 if ( ! is_email( $email ) ) {
1476 return false;
1477 }
1478
1479 $options_site_url = esc_url( network_admin_url( 'settings.php' ) );
1480
1481 switch_to_blog( $blog_id );
1482 $blogname = get_option( 'blogname' );
1483 $siteurl = site_url();
1484 restore_current_blog();
1485
1486 $msg = sprintf(
1487 /* translators: New site notification email. 1: Site URL, 2: User IP address, 3: URL to Network Settings screen. */
1488 __(
1489 'New Site: %1$s
1490URL: %2$s
1491Remote IP address: %3$s
1492
1493Disable these notifications: %4$s'
1494 ),
1495 $blogname,
1496 $siteurl,
1497 wp_unslash( $_SERVER['REMOTE_ADDR'] ),
1498 $options_site_url
1499 );
1500 /**
1501 * Filters the message body of the new site activation email sent
1502 * to the network administrator.
1503 *
1504 * @since MU (3.0.0)
1505 * @since 5.4.0 The `$blog_id` parameter was added.
1506 *
1507 * @param string $msg Email body.
1508 * @param int|string $blog_id The new site's ID as an integer or numeric string.
1509 */
1510 $msg = apply_filters( 'newblog_notify_siteadmin', $msg, $blog_id );
1511
1512 /* translators: New site notification email subject. %s: New site URL. */
1513 wp_mail( $email, sprintf( __( 'New Site Registration: %s' ), $siteurl ), $msg );
1514
1515 return true;
1516}
1517
1518/**
1519 * Notifies the network admin that a new user has been activated.
1520 *
1521 * Filter {@see 'newuser_notify_siteadmin'} to change the content of
1522 * the notification email.
1523 *
1524 * @since MU (3.0.0)
1525 *
1526 * @param int $user_id The new user's ID.
1527 * @return bool
1528 */
1529function newuser_notify_siteadmin( $user_id ) {
1530 if ( 'yes' !== get_site_option( 'registrationnotification' ) ) {
1531 return false;
1532 }
1533
1534 $email = get_site_option( 'admin_email' );
1535
1536 if ( ! is_email( $email ) ) {
1537 return false;
1538 }
1539
1540 $user = get_userdata( $user_id );
1541
1542 $options_site_url = esc_url( network_admin_url( 'settings.php' ) );
1543
1544 $msg = sprintf(
1545 /* translators: New user notification email. 1: User login, 2: User IP address, 3: URL to Network Settings screen. */
1546 __(
1547 'New User: %1$s
1548Remote IP address: %2$s
1549
1550Disable these notifications: %3$s'
1551 ),
1552 $user->user_login,
1553 wp_unslash( $_SERVER['REMOTE_ADDR'] ),
1554 $options_site_url
1555 );
1556
1557 /**
1558 * Filters the message body of the new user activation email sent
1559 * to the network administrator.
1560 *
1561 * @since MU (3.0.0)
1562 *
1563 * @param string $msg Email body.
1564 * @param WP_User $user WP_User instance of the new user.
1565 */
1566 $msg = apply_filters( 'newuser_notify_siteadmin', $msg, $user );
1567
1568 /* translators: New user notification email subject. %s: User login. */
1569 wp_mail( $email, sprintf( __( 'New User Registration: %s' ), $user->user_login ), $msg );
1570
1571 return true;
1572}
1573
1574/**
1575 * Checks whether a site name is already taken.
1576 *
1577 * The name is the site's subdomain or the site's subdirectory
1578 * path depending on the network settings.
1579 *
1580 * Used during the new site registration process to ensure
1581 * that each site name is unique.
1582 *
1583 * @since MU (3.0.0)
1584 *
1585 * @param string $domain The domain to be checked.
1586 * @param string $path The path to be checked.
1587 * @param int $network_id Optional. Network ID. Only relevant on multi-network installations.
1588 * Default 1.
1589 * @return int|null The site ID if the site name exists, null otherwise.
1590 */
1591function domain_exists( $domain, $path, $network_id = 1 ) {
1592 $path = trailingslashit( $path );
1593 $args = array(
1594 'network_id' => $network_id,
1595 'domain' => $domain,
1596 'path' => $path,
1597 'fields' => 'ids',
1598 'number' => 1,
1599 'update_site_meta_cache' => false,
1600 );
1601 $result = get_sites( $args );
1602 $result = array_shift( $result );
1603
1604 /**
1605 * Filters whether a site name is taken.
1606 *
1607 * The name is the site's subdomain or the site's subdirectory
1608 * path depending on the network settings.
1609 *
1610 * @since 3.5.0
1611 *
1612 * @param int|null $result The site ID if the site name exists, null otherwise.
1613 * @param string $domain Domain to be checked.
1614 * @param string $path Path to be checked.
1615 * @param int $network_id Network ID. Only relevant on multi-network installations.
1616 */
1617 return apply_filters( 'domain_exists', $result, $domain, $path, $network_id );
1618}
1619
1620/**
1621 * Notifies the site administrator that their site activation was successful.
1622 *
1623 * Filter {@see 'wpmu_welcome_notification'} to disable or bypass.
1624 *
1625 * Filter {@see 'update_welcome_email'} and {@see 'update_welcome_subject'} to
1626 * modify the content and subject line of the notification email.
1627 *
1628 * @since MU (3.0.0)
1629 *
1630 * @param int $blog_id Site ID.
1631 * @param int $user_id User ID.
1632 * @param string $password User password, or "N/A" if the user account is not new.
1633 * @param string $title Site title.
1634 * @param array $meta Optional. Signup meta data. By default, contains the requested privacy setting and lang_id.
1635 * @return bool Whether the email notification was sent.
1636 */
1637function wpmu_welcome_notification(
1638 $blog_id,
1639 $user_id,
1640 #[\SensitiveParameter]
1641 $password,
1642 $title,
1643 $meta = array()
1644) {
1645 $current_network = get_network();
1646
1647 /**
1648 * Filters whether to bypass the welcome email sent to the site administrator after site activation.
1649 *
1650 * Returning false disables the welcome email.
1651 *
1652 * @since MU (3.0.0)
1653 *
1654 * @param int|false $blog_id Site ID, or false to prevent the email from sending.
1655 * @param int $user_id User ID of the site administrator.
1656 * @param string $password User password, or "N/A" if the user account is not new.
1657 * @param string $title Site title.
1658 * @param array $meta Signup meta data. By default, contains the requested privacy setting and lang_id.
1659 */
1660 if ( ! apply_filters( 'wpmu_welcome_notification', $blog_id, $user_id, $password, $title, $meta ) ) {
1661 return false;
1662 }
1663
1664 $user = get_userdata( $user_id );
1665
1666 $switched_locale = switch_to_user_locale( $user_id );
1667
1668 $welcome_email = get_site_option( 'welcome_email' );
1669
1670 if ( ! $welcome_email ) {
1671 /* translators: Do not translate USERNAME, SITE_NAME, BLOG_URL, PASSWORD: those are placeholders. */
1672 $welcome_email = __(
1673 'Howdy USERNAME,
1674
1675Your new SITE_NAME site has been successfully set up at:
1676BLOG_URL
1677
1678You can log in to the administrator account with the following information:
1679
1680Username: USERNAME
1681Password: PASSWORD
1682Log in here: BLOG_URLwp-login.php
1683
1684We hope you enjoy your new site. Thanks!
1685
1686--The Team @ SITE_NAME'
1687 );
1688 }
1689
1690 $url = get_blogaddress_by_id( $blog_id );
1691
1692 $welcome_email = str_replace( 'SITE_NAME', $current_network->site_name, $welcome_email );
1693 $welcome_email = str_replace( 'BLOG_TITLE', $title, $welcome_email );
1694 $welcome_email = str_replace( 'BLOG_URL', $url, $welcome_email );
1695 $welcome_email = str_replace( 'USERNAME', $user->user_login, $welcome_email );
1696 $welcome_email = str_replace( 'PASSWORD', $password, $welcome_email );
1697
1698 /**
1699 * Filters the content of the welcome email sent to the site administrator after site activation.
1700 *
1701 * Content should be formatted for transmission via wp_mail().
1702 *
1703 * @since MU (3.0.0)
1704 *
1705 * @param string $welcome_email Message body of the email.
1706 * @param int $blog_id Site ID.
1707 * @param int $user_id User ID of the site administrator.
1708 * @param string $password User password, or "N/A" if the user account is not new.
1709 * @param string $title Site title.
1710 * @param array $meta Signup meta data. By default, contains the requested privacy setting and lang_id.
1711 */
1712 $welcome_email = apply_filters( 'update_welcome_email', $welcome_email, $blog_id, $user_id, $password, $title, $meta );
1713
1714 $admin_email = get_site_option( 'admin_email' );
1715
1716 if ( '' === $admin_email ) {
1717 $admin_email = 'support@' . wp_parse_url( network_home_url(), PHP_URL_HOST );
1718 }
1719
1720 $from_name = ( '' !== get_site_option( 'site_name' ) ) ? esc_html( get_site_option( 'site_name' ) ) : 'WordPress';
1721 $message_headers = "From: \"{$from_name}\" <{$admin_email}>\n" . 'Content-Type: text/plain; charset="' . get_option( 'blog_charset' ) . "\"\n";
1722 $message = $welcome_email;
1723
1724 if ( empty( $current_network->site_name ) ) {
1725 $current_network->site_name = 'WordPress';
1726 }
1727
1728 /* translators: New site notification email subject. 1: Network title, 2: New site title. */
1729 $subject = __( 'New %1$s Site: %2$s' );
1730
1731 /**
1732 * Filters the subject of the welcome email sent to the site administrator after site activation.
1733 *
1734 * @since MU (3.0.0)
1735 *
1736 * @param string $subject Subject of the email.
1737 */
1738 $subject = apply_filters( 'update_welcome_subject', sprintf( $subject, $current_network->site_name, wp_unslash( $title ) ) );
1739
1740 wp_mail( $user->user_email, wp_specialchars_decode( $subject ), $message, $message_headers );
1741
1742 if ( $switched_locale ) {
1743 restore_previous_locale();
1744 }
1745
1746 return true;
1747}
1748
1749/**
1750 * Notifies the Multisite network administrator that a new site was created.
1751 *
1752 * Filter {@see 'send_new_site_email'} to disable or bypass.
1753 *
1754 * Filter {@see 'new_site_email'} to filter the contents.
1755 *
1756 * @since 5.6.0
1757 *
1758 * @param int $site_id Site ID of the new site.
1759 * @param int $user_id User ID of the administrator of the new site.
1760 * @return bool Whether the email notification was sent.
1761 */
1762function wpmu_new_site_admin_notification( $site_id, $user_id ) {
1763 $site = get_site( $site_id );
1764 $user = get_userdata( $user_id );
1765 $email = get_site_option( 'admin_email' );
1766
1767 if ( ! $site || ! $user || ! $email ) {
1768 return false;
1769 }
1770
1771 /**
1772 * Filters whether to send an email to the Multisite network administrator when a new site is created.
1773 *
1774 * Return false to disable sending the email.
1775 *
1776 * @since 5.6.0
1777 *
1778 * @param bool $send Whether to send the email.
1779 * @param WP_Site $site Site object of the new site.
1780 * @param WP_User $user User object of the administrator of the new site.
1781 */
1782 if ( ! apply_filters( 'send_new_site_email', true, $site, $user ) ) {
1783 return false;
1784 }
1785
1786 $switched_locale = false;
1787 $network_admin = get_user_by( 'email', $email );
1788
1789 if ( $network_admin ) {
1790 // If the network admin email address corresponds to a user, switch to their locale.
1791 $switched_locale = switch_to_user_locale( $network_admin->ID );
1792 } else {
1793 // Otherwise switch to the locale of the current site.
1794 $switched_locale = switch_to_locale( get_locale() );
1795 }
1796
1797 $subject = sprintf(
1798 /* translators: New site notification email subject. %s: Network title. */
1799 __( '[%s] New Site Created' ),
1800 get_network()->site_name
1801 );
1802
1803 $message = sprintf(
1804 /* translators: New site notification email. 1: User login, 2: Site URL, 3: Site title. */
1805 __(
1806 'New site created by %1$s
1807
1808Address: %2$s
1809Name: %3$s'
1810 ),
1811 $user->user_login,
1812 get_site_url( $site->id ),
1813 get_blog_option( $site->id, 'blogname' )
1814 );
1815
1816 $header = sprintf(
1817 'From: "%1$s" <%2$s>',
1818 _x( 'Site Admin', 'email "From" field' ),
1819 $email
1820 );
1821
1822 $new_site_email = array(
1823 'to' => $email,
1824 'subject' => $subject,
1825 'message' => $message,
1826 'headers' => $header,
1827 );
1828
1829 /**
1830 * Filters the content of the email sent to the Multisite network administrator when a new site is created.
1831 *
1832 * Content should be formatted for transmission via wp_mail().
1833 *
1834 * @since 5.6.0
1835 *
1836 * @param array $new_site_email {
1837 * Used to build wp_mail().
1838 *
1839 * @type string $to The email address of the recipient.
1840 * @type string $subject The subject of the email.
1841 * @type string $message The content of the email.
1842 * @type string $headers Headers.
1843 * }
1844 * @param WP_Site $site Site object of the new site.
1845 * @param WP_User $user User object of the administrator of the new site.
1846 */
1847 $new_site_email = apply_filters( 'new_site_email', $new_site_email, $site, $user );
1848
1849 wp_mail(
1850 $new_site_email['to'],
1851 wp_specialchars_decode( $new_site_email['subject'] ),
1852 $new_site_email['message'],
1853 $new_site_email['headers']
1854 );
1855
1856 if ( $switched_locale ) {
1857 restore_previous_locale();
1858 }
1859
1860 return true;
1861}
1862
1863/**
1864 * Notifies a user that their account activation has been successful.
1865 *
1866 * Filter {@see 'wpmu_welcome_user_notification'} to disable or bypass.
1867 *
1868 * Filter {@see 'update_welcome_user_email'} and {@see 'update_welcome_user_subject'} to
1869 * modify the content and subject line of the notification email.
1870 *
1871 * @since MU (3.0.0)
1872 *
1873 * @param int $user_id User ID.
1874 * @param string $password User password.
1875 * @param array $meta Optional. Signup meta data. Default empty array.
1876 * @return bool
1877 */
1878function wpmu_welcome_user_notification(
1879 $user_id,
1880 #[\SensitiveParameter]
1881 $password,
1882 $meta = array()
1883) {
1884 $current_network = get_network();
1885
1886 /**
1887 * Filters whether to bypass the welcome email after user activation.
1888 *
1889 * Returning false disables the welcome email.
1890 *
1891 * @since MU (3.0.0)
1892 *
1893 * @param int $user_id User ID.
1894 * @param string $password User password.
1895 * @param array $meta Signup meta data. Default empty array.
1896 */
1897 if ( ! apply_filters( 'wpmu_welcome_user_notification', $user_id, $password, $meta ) ) {
1898 return false;
1899 }
1900
1901 $welcome_email = get_site_option( 'welcome_user_email' );
1902
1903 $user = get_userdata( $user_id );
1904
1905 $switched_locale = switch_to_user_locale( $user_id );
1906
1907 /**
1908 * Filters the content of the welcome email after user activation.
1909 *
1910 * Content should be formatted for transmission via wp_mail().
1911 *
1912 * @since MU (3.0.0)
1913 *
1914 * @param string $welcome_email The message body of the account activation success email.
1915 * @param int $user_id User ID.
1916 * @param string $password User password.
1917 * @param array $meta Signup meta data. Default empty array.
1918 */
1919 $welcome_email = apply_filters( 'update_welcome_user_email', $welcome_email, $user_id, $password, $meta );
1920 $welcome_email = str_replace( 'SITE_NAME', $current_network->site_name, $welcome_email );
1921 $welcome_email = str_replace( 'USERNAME', $user->user_login, $welcome_email );
1922 $welcome_email = str_replace( 'PASSWORD', $password, $welcome_email );
1923 $welcome_email = str_replace( 'LOGINLINK', wp_login_url(), $welcome_email );
1924
1925 $admin_email = get_site_option( 'admin_email' );
1926
1927 if ( '' === $admin_email ) {
1928 $admin_email = 'support@' . wp_parse_url( network_home_url(), PHP_URL_HOST );
1929 }
1930
1931 $from_name = ( '' !== get_site_option( 'site_name' ) ) ? esc_html( get_site_option( 'site_name' ) ) : 'WordPress';
1932 $message_headers = "From: \"{$from_name}\" <{$admin_email}>\n" . 'Content-Type: text/plain; charset="' . get_option( 'blog_charset' ) . "\"\n";
1933 $message = $welcome_email;
1934
1935 if ( empty( $current_network->site_name ) ) {
1936 $current_network->site_name = 'WordPress';
1937 }
1938
1939 /* translators: New user notification email subject. 1: Network title, 2: New user login. */
1940 $subject = __( 'New %1$s User: %2$s' );
1941
1942 /**
1943 * Filters the subject of the welcome email after user activation.
1944 *
1945 * @since MU (3.0.0)
1946 *
1947 * @param string $subject Subject of the email.
1948 */
1949 $subject = apply_filters( 'update_welcome_user_subject', sprintf( $subject, $current_network->site_name, $user->user_login ) );
1950
1951 wp_mail( $user->user_email, wp_specialchars_decode( $subject ), $message, $message_headers );
1952
1953 if ( $switched_locale ) {
1954 restore_previous_locale();
1955 }
1956
1957 return true;
1958}
1959
1960/**
1961 * Gets the current network.
1962 *
1963 * Returns an object containing the 'id', 'domain', 'path', and 'site_name'
1964 * properties of the network being viewed.
1965 *
1966 * @see wpmu_current_site()
1967 *
1968 * @since MU (3.0.0)
1969 *
1970 * @global WP_Network $current_site The current network.
1971 *
1972 * @return WP_Network The current network.
1973 */
1974function get_current_site() {
1975 global $current_site;
1976 return $current_site;
1977}
1978
1979/**
1980 * Gets a user's most recent post.
1981 *
1982 * Walks through each of a user's blogs to find the post with
1983 * the most recent post_date_gmt.
1984 *
1985 * @since MU (3.0.0)
1986 *
1987 * @global wpdb $wpdb WordPress database abstraction object.
1988 *
1989 * @param int $user_id User ID.
1990 * @return array Contains the blog_id, post_id, post_date_gmt, and post_gmt_ts.
1991 */
1992function get_most_recent_post_of_user( $user_id ) {
1993 global $wpdb;
1994
1995 $user_blogs = get_blogs_of_user( (int) $user_id );
1996 $most_recent_post = array();
1997
1998 /*
1999 * Walk through each blog and get the most recent post
2000 * published by $user_id.
2001 */
2002 foreach ( (array) $user_blogs as $blog ) {
2003 $prefix = $wpdb->get_blog_prefix( $blog->userblog_id );
2004 $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 );
2005
2006 // Make sure we found a post.
2007 if ( isset( $recent_post['ID'] ) ) {
2008 $post_gmt_ts = strtotime( $recent_post['post_date_gmt'] );
2009
2010 /*
2011 * If this is the first post checked
2012 * or if this post is newer than the current recent post,
2013 * make it the new most recent post.
2014 */
2015 if ( ! isset( $most_recent_post['post_gmt_ts'] ) || ( $post_gmt_ts > $most_recent_post['post_gmt_ts'] ) ) {
2016 $most_recent_post = array(
2017 'blog_id' => $blog->userblog_id,
2018 'post_id' => $recent_post['ID'],
2019 'post_date_gmt' => $recent_post['post_date_gmt'],
2020 'post_gmt_ts' => $post_gmt_ts,
2021 );
2022 }
2023 }
2024 }
2025
2026 return $most_recent_post;
2027}
2028
2029//
2030// Misc functions.
2031//
2032
2033/**
2034 * Checks an array of MIME types against a list of allowed types.
2035 *
2036 * WordPress ships with a set of allowed upload filetypes,
2037 * which is defined in wp-includes/functions.php in
2038 * get_allowed_mime_types(). This function is used to filter
2039 * that list against the filetypes allowed provided by Multisite
2040 * Super Admins at wp-admin/network/settings.php.
2041 *
2042 * @since MU (3.0.0)
2043 *
2044 * @param array $mimes
2045 * @return array
2046 */
2047function check_upload_mimes( $mimes ) {
2048 $site_exts = explode( ' ', get_site_option( 'upload_filetypes', 'jpg jpeg png gif' ) );
2049 $site_mimes = array();
2050 foreach ( $site_exts as $ext ) {
2051 foreach ( $mimes as $ext_pattern => $mime ) {
2052 if ( '' !== $ext && str_contains( $ext_pattern, $ext ) ) {
2053 $site_mimes[ $ext_pattern ] = $mime;
2054 }
2055 }
2056 }
2057 return $site_mimes;
2058}
2059
2060/**
2061 * Updates a blog's post count.
2062 *
2063 * WordPress MS stores a blog's post count as an option so as
2064 * to avoid extraneous COUNTs when a blog's details are fetched
2065 * with get_site(). This function is called when posts are published
2066 * or unpublished to make sure the count stays current.
2067 *
2068 * @since MU (3.0.0)
2069 *
2070 * @global wpdb $wpdb WordPress database abstraction object.
2071 *
2072 * @param string $deprecated Not used.
2073 */
2074function update_posts_count( $deprecated = '' ) {
2075 global $wpdb;
2076 update_option( 'post_count', (int) $wpdb->get_var( "SELECT COUNT(ID) FROM {$wpdb->posts} WHERE post_status = 'publish' and post_type = 'post'" ), true );
2077}
2078
2079/**
2080 * Logs the user email, IP, and registration date of a new site.
2081 *
2082 * @since MU (3.0.0)
2083 * @since 5.1.0 Parameters now support input from the {@see 'wp_initialize_site'} action.
2084 *
2085 * @global wpdb $wpdb WordPress database abstraction object.
2086 *
2087 * @param WP_Site|int $blog_id The new site's object or ID.
2088 * @param int|array $user_id User ID, or array of arguments including 'user_id'.
2089 */
2090function wpmu_log_new_registrations( $blog_id, $user_id ) {
2091 global $wpdb;
2092
2093 if ( is_object( $blog_id ) ) {
2094 $blog_id = $blog_id->blog_id;
2095 }
2096
2097 if ( is_array( $user_id ) ) {
2098 $user_id = ! empty( $user_id['user_id'] ) ? $user_id['user_id'] : 0;
2099 }
2100
2101 $user = get_userdata( (int) $user_id );
2102 if ( $user ) {
2103 $wpdb->insert(
2104 $wpdb->registration_log,
2105 array(
2106 'email' => $user->user_email,
2107 'IP' => preg_replace( '/[^0-9., ]/', '', wp_unslash( $_SERVER['REMOTE_ADDR'] ) ),
2108 'blog_id' => $blog_id,
2109 'date_registered' => current_time( 'mysql' ),
2110 )
2111 );
2112 }
2113}
2114
2115/**
2116 * Ensures that the current site's domain is listed in the allowed redirect host list.
2117 *
2118 * @see wp_validate_redirect()
2119 * @since MU (3.0.0)
2120 *
2121 * @param array|string $deprecated Not used.
2122 * @return string[] {
2123 * An array containing the current site's domain.
2124 *
2125 * @type string $0 The current site's domain.
2126 * }
2127 */
2128function redirect_this_site( $deprecated = '' ) {
2129 return array( get_network()->domain );
2130}
2131
2132/**
2133 * Checks whether an upload is too big.
2134 *
2135 * @since MU (3.0.0)
2136 *
2137 * @param array $upload An array of information about the newly-uploaded file.
2138 * @return string|array If the upload is under the size limit, $upload is returned. Otherwise returns an error message.
2139 */
2140function upload_is_file_too_big( $upload ) {
2141 if ( ! is_array( $upload ) || defined( 'WP_IMPORTING' ) || get_site_option( 'upload_space_check_disabled' ) ) {
2142 return $upload;
2143 }
2144
2145 if ( strlen( $upload['bits'] ) > ( KB_IN_BYTES * get_site_option( 'fileupload_maxk', 1500 ) ) ) {
2146 /* translators: %s: Maximum allowed file size in kilobytes. */
2147 return sprintf( __( 'This file is too big. Files must be less than %s KB in size.' ) . '<br />', get_site_option( 'fileupload_maxk', 1500 ) );
2148 }
2149
2150 return $upload;
2151}
2152
2153/**
2154 * Adds a nonce field to the signup page.
2155 *
2156 * @since MU (3.0.0)
2157 */
2158function signup_nonce_fields() {
2159 $id = mt_rand();
2160 echo "<input type='hidden' name='signup_form_id' value='{$id}' />";
2161 wp_nonce_field( 'signup_form_' . $id, '_signup_form', false );
2162}
2163
2164/**
2165 * Processes the signup nonce created in signup_nonce_fields().
2166 *
2167 * @since MU (3.0.0)
2168 *
2169 * @param array $result
2170 * @return array
2171 */
2172function signup_nonce_check( $result ) {
2173 if ( ! strpos( $_SERVER['PHP_SELF'], 'wp-signup.php' ) ) {
2174 return $result;
2175 }
2176
2177 if ( ! wp_verify_nonce( $_POST['_signup_form'], 'signup_form_' . $_POST['signup_form_id'] ) ) {
2178 $result['errors']->add( 'invalid_nonce', __( 'Unable to submit this form, please try again.' ) );
2179 }
2180
2181 return $result;
2182}
2183
2184/**
2185 * Corrects 404 redirects when NOBLOGREDIRECT is defined.
2186 *
2187 * @since MU (3.0.0)
2188 */
2189function maybe_redirect_404() {
2190 if ( is_main_site() && is_404() && defined( 'NOBLOGREDIRECT' ) ) {
2191 /**
2192 * Filters the redirect URL for 404s on the main site.
2193 *
2194 * The filter is only evaluated if the NOBLOGREDIRECT constant is defined.
2195 *
2196 * @since 3.0.0
2197 *
2198 * @param string $no_blog_redirect The redirect URL defined in NOBLOGREDIRECT.
2199 */
2200 $destination = apply_filters( 'blog_redirect_404', NOBLOGREDIRECT );
2201
2202 if ( $destination ) {
2203 if ( '%siteurl%' === $destination ) {
2204 $destination = network_home_url();
2205 }
2206
2207 wp_redirect( $destination );
2208 exit;
2209 }
2210 }
2211}
2212
2213/**
2214 * Adds a new user to a blog by visiting /newbloguser/{key}/.
2215 *
2216 * This will only work when the user's details are saved as an option
2217 * keyed as 'new_user_{key}', where '{key}' is a hash generated for the user to be
2218 * added, as when a user is invited through the regular WP Add User interface.
2219 *
2220 * @since MU (3.0.0)
2221 */
2222function maybe_add_existing_user_to_blog() {
2223 if ( ! str_contains( $_SERVER['REQUEST_URI'], '/newbloguser/' ) ) {
2224 return;
2225 }
2226
2227 $parts = explode( '/', $_SERVER['REQUEST_URI'] );
2228 $key = array_pop( $parts );
2229
2230 if ( '' === $key ) {
2231 $key = array_pop( $parts );
2232 }
2233
2234 $details = get_option( 'new_user_' . $key );
2235 if ( ! empty( $details ) ) {
2236 delete_option( 'new_user_' . $key );
2237 }
2238
2239 if ( empty( $details ) || is_wp_error( add_existing_user_to_blog( $details ) ) ) {
2240 wp_die(
2241 sprintf(
2242 /* translators: %s: Home URL. */
2243 __( 'An error occurred adding you to this site. Go to the <a href="%s">homepage</a>.' ),
2244 home_url()
2245 )
2246 );
2247 }
2248
2249 wp_die(
2250 sprintf(
2251 /* translators: 1: Home URL, 2: Admin URL. */
2252 __( 'You have been added to this site. Please visit the <a href="%1$s">homepage</a> or <a href="%2$s">log in</a> using your username and password.' ),
2253 home_url(),
2254 admin_url()
2255 ),
2256 __( 'WordPress &rsaquo; Success' ),
2257 array( 'response' => 200 )
2258 );
2259}
2260
2261/**
2262 * Adds a user to a blog based on details from maybe_add_existing_user_to_blog().
2263 *
2264 * @since MU (3.0.0)
2265 *
2266 * @param array|false $details {
2267 * User details. Must at least contain values for the keys listed below.
2268 *
2269 * @type int $user_id The ID of the user being added to the current blog.
2270 * @type string $role The role to be assigned to the user.
2271 * }
2272 * @return true|WP_Error|void True on success or a WP_Error object if the user doesn't exist
2273 * or could not be added. Void if $details array was not provided.
2274 */
2275function add_existing_user_to_blog( $details = false ) {
2276 if ( is_array( $details ) ) {
2277 $blog_id = get_current_blog_id();
2278 $result = add_user_to_blog( $blog_id, $details['user_id'], $details['role'] );
2279
2280 /**
2281 * Fires immediately after an existing user is added to a site.
2282 *
2283 * @since MU (3.0.0)
2284 *
2285 * @param int $user_id User ID.
2286 * @param true|WP_Error $result True on success or a WP_Error object if the user doesn't exist
2287 * or could not be added.
2288 */
2289 do_action( 'added_existing_user', $details['user_id'], $result );
2290
2291 return $result;
2292 }
2293}
2294
2295/**
2296 * Adds a newly created user to the appropriate blog
2297 *
2298 * To add a user in general, use add_user_to_blog(). This function
2299 * is specifically hooked into the {@see 'wpmu_activate_user'} action.
2300 *
2301 * @since MU (3.0.0)
2302 *
2303 * @see add_user_to_blog()
2304 *
2305 * @param int $user_id User ID.
2306 * @param string $password User password. Ignored.
2307 * @param array $meta Signup meta data.
2308 */
2309function add_new_user_to_blog(
2310 $user_id,
2311 #[\SensitiveParameter]
2312 $password,
2313 $meta
2314) {
2315 if ( ! empty( $meta['add_to_blog'] ) ) {
2316 $blog_id = $meta['add_to_blog'];
2317 $role = $meta['new_role'];
2318 remove_user_from_blog( $user_id, get_network()->site_id ); // Remove user from main blog.
2319
2320 $result = add_user_to_blog( $blog_id, $user_id, $role );
2321
2322 if ( ! is_wp_error( $result ) ) {
2323 update_user_meta( $user_id, 'primary_blog', $blog_id );
2324 }
2325 }
2326}
2327
2328/**
2329 * Corrects From host on outgoing mail to match the site domain.
2330 *
2331 * @since MU (3.0.0)
2332 *
2333 * @param PHPMailer\PHPMailer\PHPMailer $phpmailer The PHPMailer instance (passed by reference).
2334 */
2335function fix_phpmailer_messageid( $phpmailer ) {
2336 $phpmailer->Hostname = get_network()->domain;
2337}
2338
2339/**
2340 * Determines whether a user is marked as a spammer, based on user login.
2341 *
2342 * @since MU (3.0.0)
2343 *
2344 * @param string|WP_User $user Optional. Defaults to current user. WP_User object,
2345 * or user login name as a string.
2346 * @return bool
2347 */
2348function is_user_spammy( $user = null ) {
2349 if ( ! ( $user instanceof WP_User ) ) {
2350 if ( $user ) {
2351 $user = get_user_by( 'login', $user );
2352 } else {
2353 $user = wp_get_current_user();
2354 }
2355 }
2356
2357 return $user && isset( $user->spam ) && '1' === $user->spam;
2358}
2359
2360/**
2361 * Updates this blog's 'public' setting in the global blogs table.
2362 *
2363 * Public blogs have a setting of 1, private blogs are 0.
2364 *
2365 * @since MU (3.0.0)
2366 *
2367 * @param int $old_value The old public value.
2368 * @param int $value The new public value.
2369 */
2370function update_blog_public( $old_value, $value ) {
2371 update_blog_status( get_current_blog_id(), 'public', (int) $value );
2372}
2373
2374/**
2375 * Determines whether users can self-register, based on Network settings.
2376 *
2377 * @since MU (3.0.0)
2378 *
2379 * @return bool
2380 */
2381function users_can_register_signup_filter() {
2382 $registration = get_site_option( 'registration' );
2383 return ( 'all' === $registration || 'user' === $registration );
2384}
2385
2386/**
2387 * Ensures that the welcome message is not empty. Currently unused.
2388 *
2389 * @since MU (3.0.0)
2390 *
2391 * @param string $text
2392 * @return string
2393 */
2394function welcome_user_msg_filter( $text ) {
2395 if ( ! $text ) {
2396 remove_filter( 'site_option_welcome_user_email', 'welcome_user_msg_filter' );
2397
2398 /* translators: Do not translate USERNAME, PASSWORD, LOGINLINK, SITE_NAME: those are placeholders. */
2399 $text = __(
2400 'Howdy USERNAME,
2401
2402Your new account is set up.
2403
2404You can log in with the following information:
2405Username: USERNAME
2406Password: PASSWORD
2407LOGINLINK
2408
2409Thanks!
2410
2411--The Team @ SITE_NAME'
2412 );
2413 update_site_option( 'welcome_user_email', $text );
2414 }
2415 return $text;
2416}
2417
2418/**
2419 * Determines whether to force SSL on content.
2420 *
2421 * @since 2.8.5
2422 *
2423 * @param bool|null $force Optional. Whether to force SSL in admin screens. Default null.
2424 * @return bool True if forced, false if not forced.
2425 */
2426function force_ssl_content( $force = null ) {
2427 static $forced_content = false;
2428
2429 if ( ! is_null( $force ) ) {
2430 $old_forced = $forced_content;
2431 $forced_content = (bool) $force;
2432 return $old_forced;
2433 }
2434
2435 return $forced_content;
2436}
2437
2438/**
2439 * Formats a URL to use https.
2440 *
2441 * Useful as a filter.
2442 *
2443 * @since 2.8.5
2444 *
2445 * @param string $url URL.
2446 * @return string URL with https as the scheme.
2447 */
2448function filter_SSL( $url ) { // phpcs:ignore WordPress.NamingConventions.ValidFunctionName.FunctionNameInvalid
2449 if ( ! is_string( $url ) ) {
2450 return get_bloginfo( 'url' ); // Return home site URL with proper scheme.
2451 }
2452
2453 if ( force_ssl_content() && is_ssl() ) {
2454 $url = set_url_scheme( $url, 'https' );
2455 }
2456
2457 return $url;
2458}
2459
2460/**
2461 * Schedules update of the network-wide counts for the current network.
2462 *
2463 * @since 3.1.0
2464 */
2465function wp_schedule_update_network_counts() {
2466 if ( ! is_main_site() ) {
2467 return;
2468 }
2469
2470 if ( ! wp_next_scheduled( 'update_network_counts' ) && ! wp_installing() ) {
2471 wp_schedule_event( time(), 'twicedaily', 'update_network_counts' );
2472 }
2473}
2474
2475/**
2476 * Updates the network-wide counts for the current network.
2477 *
2478 * @since 3.1.0
2479 * @since 4.8.0 The `$network_id` parameter has been added.
2480 *
2481 * @param int|null $network_id ID of the network. Default is the current network.
2482 */
2483function wp_update_network_counts( $network_id = null ) {
2484 wp_update_network_user_counts( $network_id );
2485 wp_update_network_site_counts( $network_id );
2486}
2487
2488/**
2489 * Updates the count of sites for the current network.
2490 *
2491 * If enabled through the {@see 'enable_live_network_counts'} filter, update the sites count
2492 * on a network when a site is created or its status is updated.
2493 *
2494 * @since 3.7.0
2495 * @since 4.8.0 The `$network_id` parameter has been added.
2496 *
2497 * @param int|null $network_id ID of the network. Default is the current network.
2498 */
2499function wp_maybe_update_network_site_counts( $network_id = null ) {
2500 $is_small_network = ! wp_is_large_network( 'sites', $network_id );
2501
2502 /**
2503 * Filters whether to update network site or user counts when a new site is created.
2504 *
2505 * @since 3.7.0
2506 *
2507 * @see wp_is_large_network()
2508 *
2509 * @param bool $small_network Whether the network is considered small.
2510 * @param string $context Context. Either 'users' or 'sites'.
2511 */
2512 if ( ! apply_filters( 'enable_live_network_counts', $is_small_network, 'sites' ) ) {
2513 return;
2514 }
2515
2516 wp_update_network_site_counts( $network_id );
2517}
2518
2519/**
2520 * Updates the network-wide users count.
2521 *
2522 * If enabled through the {@see 'enable_live_network_counts'} filter, update the users count
2523 * on a network when a user is created or its status is updated.
2524 *
2525 * @since 3.7.0
2526 * @since 4.8.0 The `$network_id` parameter has been added.
2527 *
2528 * @param int|null $network_id ID of the network. Default is the current network.
2529 */
2530function wp_maybe_update_network_user_counts( $network_id = null ) {
2531 $is_small_network = ! wp_is_large_network( 'users', $network_id );
2532
2533 /** This filter is documented in wp-includes/ms-functions.php */
2534 if ( ! apply_filters( 'enable_live_network_counts', $is_small_network, 'users' ) ) {
2535 return;
2536 }
2537
2538 wp_update_network_user_counts( $network_id );
2539}
2540
2541/**
2542 * Updates the network-wide site count.
2543 *
2544 * @since 3.7.0
2545 * @since 4.8.0 The `$network_id` parameter has been added.
2546 *
2547 * @param int|null $network_id ID of the network. Default is the current network.
2548 */
2549function wp_update_network_site_counts( $network_id = null ) {
2550 $network_id = (int) $network_id;
2551 if ( ! $network_id ) {
2552 $network_id = get_current_network_id();
2553 }
2554
2555 $count = get_sites(
2556 array(
2557 'network_id' => $network_id,
2558 'spam' => 0,
2559 'deleted' => 0,
2560 'archived' => 0,
2561 'count' => true,
2562 'update_site_meta_cache' => false,
2563 )
2564 );
2565
2566 update_network_option( $network_id, 'blog_count', $count );
2567}
2568
2569/**
2570 * Updates the network-wide user count.
2571 *
2572 * @since 3.7.0
2573 * @since 4.8.0 The `$network_id` parameter has been added.
2574 * @since 6.0.0 This function is now a wrapper for wp_update_user_counts().
2575 *
2576 * @param int|null $network_id ID of the network. Default is the current network.
2577 */
2578function wp_update_network_user_counts( $network_id = null ) {
2579 wp_update_user_counts( $network_id );
2580}
2581
2582/**
2583 * Returns the space used by the current site.
2584 *
2585 * @since 3.5.0
2586 *
2587 * @return int Used space in megabytes.
2588 */
2589function get_space_used() {
2590 /**
2591 * Filters the amount of storage space used by the current site, in megabytes.
2592 *
2593 * @since 3.5.0
2594 *
2595 * @param int|false $space_used The amount of used space, in megabytes. Default false.
2596 */
2597 $space_used = apply_filters( 'pre_get_space_used', false );
2598
2599 if ( false === $space_used ) {
2600 $upload_dir = wp_upload_dir();
2601 $space_used = get_dirsize( $upload_dir['basedir'] ) / MB_IN_BYTES;
2602 }
2603
2604 return $space_used;
2605}
2606
2607/**
2608 * Returns the upload quota for the current blog.
2609 *
2610 * @since MU (3.0.0)
2611 *
2612 * @return int Quota in megabytes.
2613 */
2614function get_space_allowed() {
2615 $space_allowed = get_option( 'blog_upload_space' );
2616
2617 if ( ! is_numeric( $space_allowed ) ) {
2618 $space_allowed = get_site_option( 'blog_upload_space' );
2619 }
2620
2621 if ( ! is_numeric( $space_allowed ) ) {
2622 $space_allowed = 100;
2623 }
2624
2625 /**
2626 * Filters the upload quota for the current site.
2627 *
2628 * @since 3.7.0
2629 *
2630 * @param int $space_allowed Upload quota in megabytes for the current blog.
2631 */
2632 return apply_filters( 'get_space_allowed', $space_allowed );
2633}
2634
2635/**
2636 * Determines if there is any upload space left in the current blog's quota.
2637 *
2638 * @since 3.0.0
2639 *
2640 * @return int of upload space available in bytes.
2641 */
2642function get_upload_space_available() {
2643 $allowed = get_space_allowed();
2644 if ( $allowed < 0 ) {
2645 $allowed = 0;
2646 }
2647 $space_allowed = $allowed * MB_IN_BYTES;
2648 if ( get_site_option( 'upload_space_check_disabled' ) ) {
2649 return $space_allowed;
2650 }
2651
2652 $space_used = get_space_used() * MB_IN_BYTES;
2653
2654 if ( ( $space_allowed - $space_used ) <= 0 ) {
2655 return 0;
2656 }
2657
2658 return $space_allowed - $space_used;
2659}
2660
2661/**
2662 * Determines if there is any upload space left in the current blog's quota.
2663 *
2664 * @since 3.0.0
2665 * @return bool True if space is available, false otherwise.
2666 */
2667function is_upload_space_available() {
2668 if ( get_site_option( 'upload_space_check_disabled' ) ) {
2669 return true;
2670 }
2671
2672 return (bool) get_upload_space_available();
2673}
2674
2675/**
2676 * Filters the maximum upload file size allowed, in bytes.
2677 *
2678 * @since 3.0.0
2679 *
2680 * @param int $size Upload size limit in bytes.
2681 * @return int Upload size limit in bytes.
2682 */
2683function upload_size_limit_filter( $size ) {
2684 $fileupload_maxk = (int) get_site_option( 'fileupload_maxk', 1500 );
2685 $max_fileupload_in_bytes = KB_IN_BYTES * $fileupload_maxk;
2686
2687 if ( get_site_option( 'upload_space_check_disabled' ) ) {
2688 return min( $size, $max_fileupload_in_bytes );
2689 }
2690
2691 return min( $size, $max_fileupload_in_bytes, get_upload_space_available() );
2692}
2693
2694/**
2695 * Determines whether or not we have a large network.
2696 *
2697 * The default criteria for a large network is either more than 10,000 users or more than 10,000 sites.
2698 * Plugins can alter this criteria using the {@see 'wp_is_large_network'} filter.
2699 *
2700 * @since 3.3.0
2701 * @since 4.8.0 The `$network_id` parameter has been added.
2702 *
2703 * @param string $using 'sites' or 'users'. Default is 'sites'.
2704 * @param int|null $network_id ID of the network. Default is the current network.
2705 * @return bool True if the network meets the criteria for large. False otherwise.
2706 */
2707function wp_is_large_network( $using = 'sites', $network_id = null ) {
2708 $network_id = (int) $network_id;
2709 if ( ! $network_id ) {
2710 $network_id = get_current_network_id();
2711 }
2712
2713 if ( 'users' === $using ) {
2714 $count = get_user_count( $network_id );
2715
2716 $is_large_network = wp_is_large_user_count( $network_id );
2717
2718 /**
2719 * Filters whether the network is considered large.
2720 *
2721 * @since 3.3.0
2722 * @since 4.8.0 The `$network_id` parameter has been added.
2723 *
2724 * @param bool $is_large_network Whether the network has more than 10000 users or sites.
2725 * @param string $component The component to count. Accepts 'users', or 'sites'.
2726 * @param int $count The count of items for the component.
2727 * @param int $network_id The ID of the network being checked.
2728 */
2729 return apply_filters( 'wp_is_large_network', $is_large_network, 'users', $count, $network_id );
2730 }
2731
2732 $count = get_blog_count( $network_id );
2733
2734 /** This filter is documented in wp-includes/ms-functions.php */
2735 return apply_filters( 'wp_is_large_network', $count > 10000, 'sites', $count, $network_id );
2736}
2737
2738/**
2739 * Retrieves a list of reserved site on a sub-directory Multisite installation.
2740 *
2741 * @since 4.4.0
2742 *
2743 * @return string[] Array of reserved names.
2744 */
2745function get_subdirectory_reserved_names() {
2746 $names = array(
2747 'page',
2748 'comments',
2749 'blog',
2750 'files',
2751 'feed',
2752 'wp-admin',
2753 'wp-content',
2754 'wp-includes',
2755 'wp-json',
2756 'embed',
2757 );
2758
2759 /**
2760 * Filters reserved site names on a sub-directory Multisite installation.
2761 *
2762 * @since 3.0.0
2763 * @since 4.4.0 'wp-admin', 'wp-content', 'wp-includes', 'wp-json', and 'embed' were added
2764 * to the reserved names list.
2765 *
2766 * @param string[] $subdirectory_reserved_names Array of reserved names.
2767 */
2768 return apply_filters( 'subdirectory_reserved_names', $names );
2769}
2770
2771/**
2772 * Sends a confirmation request email when a change of network admin email address is attempted.
2773 *
2774 * The new network admin address will not become active until confirmed.
2775 *
2776 * @since 4.9.0
2777 *
2778 * @param string $old_value The old network admin email address.
2779 * @param string $value The proposed new network admin email address.
2780 */
2781function update_network_option_new_admin_email( $old_value, $value ) {
2782 if ( get_site_option( 'admin_email' ) === $value || ! is_email( $value ) ) {
2783 return;
2784 }
2785
2786 $hash = md5( $value . time() . mt_rand() );
2787 $new_admin_email = array(
2788 'hash' => $hash,
2789 'newemail' => $value,
2790 );
2791 update_site_option( 'network_admin_hash', $new_admin_email );
2792
2793 $switched_locale = switch_to_user_locale( get_current_user_id() );
2794
2795 /* translators: Do not translate USERNAME, ADMIN_URL, EMAIL, SITENAME, SITEURL: those are placeholders. */
2796 $email_text = __(
2797 'Howdy ###USERNAME###,
2798
2799You recently requested to have the network admin email address on
2800your network changed.
2801
2802If this is correct, please click on the following link to change it:
2803###ADMIN_URL###
2804
2805You can safely ignore and delete this email if you do not want to
2806take this action.
2807
2808This email has been sent to ###EMAIL###
2809
2810Regards,
2811All at ###SITENAME###
2812###SITEURL###'
2813 );
2814
2815 /**
2816 * Filters the text of the email sent when a change of network admin email address is attempted.
2817 *
2818 * The following strings have a special meaning and will get replaced dynamically:
2819 *
2820 * - `###USERNAME###` The current user's username.
2821 * - `###ADMIN_URL###` The link to click on to confirm the email change.
2822 * - `###EMAIL###` The proposed new network admin email address.
2823 * - `###SITENAME###` The name of the network.
2824 * - `###SITEURL###` The URL to the network.
2825 *
2826 * @since 4.9.0
2827 *
2828 * @param string $email_text Text in the email.
2829 * @param array $new_admin_email {
2830 * Data relating to the new network admin email address.
2831 *
2832 * @type string $hash The secure hash used in the confirmation link URL.
2833 * @type string $newemail The proposed new network admin email address.
2834 * }
2835 */
2836 $content = apply_filters( 'new_network_admin_email_content', $email_text, $new_admin_email );
2837
2838 $current_user = wp_get_current_user();
2839 $content = str_replace( '###USERNAME###', $current_user->user_login, $content );
2840 $content = str_replace( '###ADMIN_URL###', esc_url( network_admin_url( 'settings.php?network_admin_hash=' . $hash ) ), $content );
2841 $content = str_replace( '###EMAIL###', $value, $content );
2842 $content = str_replace( '###SITENAME###', wp_specialchars_decode( get_site_option( 'site_name' ), ENT_QUOTES ), $content );
2843 $content = str_replace( '###SITEURL###', network_home_url(), $content );
2844
2845 wp_mail(
2846 $value,
2847 sprintf(
2848 /* translators: Email change notification email subject. %s: Network title. */
2849 __( '[%s] Network Admin Email Change Request' ),
2850 wp_specialchars_decode( get_site_option( 'site_name' ), ENT_QUOTES )
2851 ),
2852 $content
2853 );
2854
2855 if ( $switched_locale ) {
2856 restore_previous_locale();
2857 }
2858}
2859
2860/**
2861 * Sends an email to the old network admin email address when the network admin email address changes.
2862 *
2863 * @since 4.9.0
2864 *
2865 * @param string $option_name The relevant database option name.
2866 * @param string $new_email The new network admin email address.
2867 * @param string $old_email The old network admin email address.
2868 * @param int $network_id ID of the network.
2869 */
2870function wp_network_admin_email_change_notification( $option_name, $new_email, $old_email, $network_id ) {
2871 $send = true;
2872
2873 // Don't send the notification for an empty email address or the default 'admin_email' value.
2874 if ( empty( $old_email ) || 'you@example.com' === $old_email ) {
2875 $send = false;
2876 }
2877
2878 /**
2879 * Filters whether to send the network admin email change notification email.
2880 *
2881 * @since 4.9.0
2882 *
2883 * @param bool $send Whether to send the email notification.
2884 * @param string $old_email The old network admin email address.
2885 * @param string $new_email The new network admin email address.
2886 * @param int $network_id ID of the network.
2887 */
2888 $send = apply_filters( 'send_network_admin_email_change_email', $send, $old_email, $new_email, $network_id );
2889
2890 if ( ! $send ) {
2891 return;
2892 }
2893
2894 /* translators: Do not translate OLD_EMAIL, NEW_EMAIL, SITENAME, SITEURL: those are placeholders. */
2895 $email_change_text = __(
2896 'Hi,
2897
2898This notice confirms that the network admin email address was changed on ###SITENAME###.
2899
2900The new network admin email address is ###NEW_EMAIL###.
2901
2902This email has been sent to ###OLD_EMAIL###
2903
2904Regards,
2905All at ###SITENAME###
2906###SITEURL###'
2907 );
2908
2909 $email_change_email = array(
2910 'to' => $old_email,
2911 /* translators: Network admin email change notification email subject. %s: Network title. */
2912 'subject' => __( '[%s] Network Admin Email Changed' ),
2913 'message' => $email_change_text,
2914 'headers' => '',
2915 );
2916 // Get network name.
2917 $network_name = wp_specialchars_decode( get_site_option( 'site_name' ), ENT_QUOTES );
2918
2919 /**
2920 * Filters the contents of the email notification sent when the network admin email address is changed.
2921 *
2922 * @since 4.9.0
2923 *
2924 * @param array $email_change_email {
2925 * Used to build wp_mail().
2926 *
2927 * @type string $to The intended recipient.
2928 * @type string $subject The subject of the email.
2929 * @type string $message The content of the email.
2930 * The following strings have a special meaning and will get replaced dynamically:
2931 * - `###OLD_EMAIL###` The old network admin email address.
2932 * - `###NEW_EMAIL###` The new network admin email address.
2933 * - `###SITENAME###` The name of the network.
2934 * - `###SITEURL###` The URL to the site.
2935 * @type string $headers Headers.
2936 * }
2937 * @param string $old_email The old network admin email address.
2938 * @param string $new_email The new network admin email address.
2939 * @param int $network_id ID of the network.
2940 */
2941 $email_change_email = apply_filters( 'network_admin_email_change_email', $email_change_email, $old_email, $new_email, $network_id );
2942
2943 $email_change_email['message'] = str_replace( '###OLD_EMAIL###', $old_email, $email_change_email['message'] );
2944 $email_change_email['message'] = str_replace( '###NEW_EMAIL###', $new_email, $email_change_email['message'] );
2945 $email_change_email['message'] = str_replace( '###SITENAME###', $network_name, $email_change_email['message'] );
2946 $email_change_email['message'] = str_replace( '###SITEURL###', home_url(), $email_change_email['message'] );
2947
2948 wp_mail(
2949 $email_change_email['to'],
2950 sprintf(
2951 $email_change_email['subject'],
2952 $network_name
2953 ),
2954 $email_change_email['message'],
2955 $email_change_email['headers']
2956 );
2957}
2958
Ui Ux Design – Teachers Night Out

Get in Touch

© 2024 Teachers Night Out. All Rights Reserved.