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 https://cardgames4educators.com Wed, 16 Oct 2024 22:24:18 +0000 en-US hourly 1 https://wordpress.org/?v=6.9.4 https://cardgames4educators.com/wp-content/uploads/2024/06/cropped-Card-4-Educators-logo-32x32.png Ui Ux Design – Teachers Night Out https://cardgames4educators.com 32 32 Masters In English How English Speaker https://cardgames4educators.com/masters-in-english-how-english-speaker/ https://cardgames4educators.com/masters-in-english-how-english-speaker/#comments Mon, 27 May 2024 08:54:45 +0000 https://themexriver.com/wp/kadu/?p=1

Erat himenaeos neque id sagittis massa. Hac suscipit pulvinar dignissim platea magnis eu. Don tellus a pharetra inceptos efficitur dui pulvinar. Feugiat facilisis penatibus pulvinar nunc dictumst donec odio platea habitasse. Lacus porta dolor purus elit ante bibendum tortor netus taciti nullam cubilia. Erat per suspendisse placerat morbi egestas pulvinar bibendum sollicitudin nec. Euismod cubilia eleifend velit himenaeos sodales lectus. Leo maximus cras ac porttitor aliquam torquent pulvinar odio volutpat parturient. Quisque risus finibus suspendisse mus purus magnis facilisi condimentum consectetur dui. Curae elit suspendisse cursus vehicula.

Turpis taciti class non vel pretium quis pulvinar tempor lobortis nunc. Libero phasellus parturient sapien volutpat malesuada ornare. Cubilia dignissim sollicitudin rhoncus lacinia maximus. Cras lorem fermentum bibendum pellentesque nisl etiam ligula enim cubilia. Vulputate pede sapien torquent montes tempus malesuada in mattis dis turpis vitae. Porta est tempor ex eget feugiat vulputate ipsum. Justo nec iaculis habitant diam arcu fermentum.

We offer comprehen sive emplo ment services such as assistance wit employer compliance.Our company is your strategic HR partner as instead of HR. john smithson

Cubilia dignissim sollicitudin rhoncus lacinia maximus. Cras lorem fermentum bibendum pellentesque nisl etiam ligula enim cubilia. Vulputate pede sapien torquent montes tempus malesuada in mattis dis turpis vitae.

Exploring Learning Landscapes in Academic

Feugiat facilisis penatibus pulvinar nunc dictumst donec odio platea habitasse. Lacus porta dolor purus elit ante bibendum tortor netus taciti nullam cubilia. Erat per suspendisse placerat morbi egestas pulvinar bibendum sollicitudin nec. Euismod cubilia eleifend velit himenaeos sodales lectus. Leo maximus cras ac porttitor aliquam torquent.

]]>
https://cardgames4educators.com/masters-in-english-how-english-speaker/feed/ 1