at path:ROOT / wp-includes / user.php
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
📄user.php
1<?php
2/**
3 * Core User API
4 *
5 * @package WordPress
6 * @subpackage Users
7 */
8
9/**
10 * Authenticates and logs a user in with 'remember' capability.
11 *
12 * The credentials is an array that has 'user_login', 'user_password', and
13 * 'remember' indices. If the credentials is not given, then the log in form
14 * will be assumed and used if set.
15 *
16 * The various authentication cookies will be set by this function and will be
17 * set for a longer period depending on if the 'remember' credential is set to
18 * true.
19 *
20 * Note: wp_signon() doesn't handle setting the current user. This means that if the
21 * function is called before the {@see 'init'} hook is fired, is_user_logged_in() will
22 * evaluate as false until that point. If is_user_logged_in() is needed in conjunction
23 * with wp_signon(), wp_set_current_user() should be called explicitly.
24 *
25 * @since 2.5.0
26 *
27 * @global string $auth_secure_cookie
28 * @global wpdb $wpdb WordPress database abstraction object.
29 *
30 * @param array $credentials {
31 * Optional. User info in order to sign on.
32 *
33 * @type string $user_login Username.
34 * @type string $user_password User password.
35 * @type bool $remember Whether to 'remember' the user. Increases the time
36 * that the cookie will be kept. Default false.
37 * }
38 * @param string|bool $secure_cookie Optional. Whether to use secure cookie.
39 * @return WP_User|WP_Error WP_User on success, WP_Error on failure.
40 */
41function wp_signon( $credentials = array(), $secure_cookie = '' ) {
42 global $auth_secure_cookie, $wpdb;
43
44 if ( empty( $credentials ) ) {
45 $credentials = array(
46 'user_login' => '',
47 'user_password' => '',
48 'remember' => false,
49 );
50
51 if ( ! empty( $_POST['log'] ) && is_string( $_POST['log'] ) ) {
52 $credentials['user_login'] = wp_unslash( $_POST['log'] );
53 }
54 if ( ! empty( $_POST['pwd'] ) && is_string( $_POST['pwd'] ) ) {
55 $credentials['user_password'] = $_POST['pwd'];
56 }
57 if ( ! empty( $_POST['rememberme'] ) ) {
58 $credentials['remember'] = $_POST['rememberme'];
59 }
60 }
61
62 if ( ! empty( $credentials['remember'] ) ) {
63 $credentials['remember'] = true;
64 } else {
65 $credentials['remember'] = false;
66 }
67
68 /**
69 * Fires before the user is authenticated.
70 *
71 * The variables passed to the callbacks are passed by reference,
72 * and can be modified by callback functions.
73 *
74 * @since 1.5.1
75 *
76 * @todo Decide whether to deprecate the wp_authenticate action.
77 *
78 * @param string $user_login Username (passed by reference).
79 * @param string $user_password User password (passed by reference).
80 */
81 do_action_ref_array( 'wp_authenticate', array( &$credentials['user_login'], &$credentials['user_password'] ) );
82
83 if ( '' === $secure_cookie ) {
84 $secure_cookie = is_ssl();
85 }
86
87 /**
88 * Filters whether to use a secure sign-on cookie.
89 *
90 * @since 3.1.0
91 *
92 * @param bool $secure_cookie Whether to use a secure sign-on cookie.
93 * @param array $credentials {
94 * Array of entered sign-on data.
95 *
96 * @type string $user_login Username.
97 * @type string $user_password Password entered.
98 * @type bool $remember Whether to 'remember' the user. Increases the time
99 * that the cookie will be kept. Default false.
100 * }
101 */
102 $secure_cookie = apply_filters( 'secure_signon_cookie', $secure_cookie, $credentials );
103
104 // XXX ugly hack to pass this to wp_authenticate_cookie().
105 $auth_secure_cookie = $secure_cookie;
106
107 add_filter( 'authenticate', 'wp_authenticate_cookie', 30, 3 );
108
109 $user = wp_authenticate( $credentials['user_login'], $credentials['user_password'] );
110
111 if ( is_wp_error( $user ) ) {
112 return $user;
113 }
114
115 wp_set_auth_cookie( $user->ID, $credentials['remember'], $secure_cookie );
116
117 // Clear `user_activation_key` after a successful login.
118 if ( ! empty( $user->user_activation_key ) ) {
119 $wpdb->update(
120 $wpdb->users,
121 array(
122 'user_activation_key' => '',
123 ),
124 array( 'ID' => $user->ID )
125 );
126
127 $user->user_activation_key = '';
128 }
129
130 /**
131 * Fires after the user has successfully logged in.
132 *
133 * @since 1.5.0
134 *
135 * @param string $user_login Username.
136 * @param WP_User $user WP_User object of the logged-in user.
137 */
138 do_action( 'wp_login', $user->user_login, $user );
139
140 return $user;
141}
142
143/**
144 * Authenticates a user, confirming the username and password are valid.
145 *
146 * @since 2.8.0
147 *
148 * @param WP_User|WP_Error|null $user WP_User or WP_Error object from a previous callback. Default null.
149 * @param string $username Username for authentication.
150 * @param string $password Password for authentication.
151 * @return WP_User|WP_Error WP_User on success, WP_Error on failure.
152 */
153function wp_authenticate_username_password(
154 $user,
155 $username,
156 #[\SensitiveParameter]
157 $password
158) {
159 if ( $user instanceof WP_User ) {
160 return $user;
161 }
162
163 if ( empty( $username ) || empty( $password ) ) {
164 if ( is_wp_error( $user ) ) {
165 return $user;
166 }
167
168 $error = new WP_Error();
169
170 if ( empty( $username ) ) {
171 $error->add( 'empty_username', __( '<strong>Error:</strong> The username field is empty.' ) );
172 }
173
174 if ( empty( $password ) ) {
175 $error->add( 'empty_password', __( '<strong>Error:</strong> The password field is empty.' ) );
176 }
177
178 return $error;
179 }
180
181 $user = get_user_by( 'login', $username );
182
183 if ( ! $user ) {
184 return new WP_Error(
185 'invalid_username',
186 sprintf(
187 /* translators: %s: User name. */
188 __( '<strong>Error:</strong> The username <strong>%s</strong> is not registered on this site. If you are unsure of your username, try your email address instead.' ),
189 $username
190 )
191 );
192 }
193
194 /**
195 * Filters whether the given user can be authenticated with the provided password.
196 *
197 * @since 2.5.0
198 *
199 * @param WP_User|WP_Error $user WP_User or WP_Error object if a previous
200 * callback failed authentication.
201 * @param string $password Password to check against the user.
202 */
203 $user = apply_filters( 'wp_authenticate_user', $user, $password );
204 if ( is_wp_error( $user ) ) {
205 return $user;
206 }
207
208 $valid = wp_check_password( $password, $user->user_pass, $user->ID );
209
210 if ( ! $valid ) {
211 return new WP_Error(
212 'incorrect_password',
213 sprintf(
214 /* translators: %s: User name. */
215 __( '<strong>Error:</strong> The password you entered for the username %s is incorrect.' ),
216 '<strong>' . $username . '</strong>'
217 ) .
218 ' <a href="' . wp_lostpassword_url() . '">' .
219 __( 'Lost your password?' ) .
220 '</a>'
221 );
222 }
223
224 if ( wp_password_needs_rehash( $user->user_pass, $user->ID ) ) {
225 wp_set_password( $password, $user->ID );
226 }
227
228 return $user;
229}
230
231/**
232 * Authenticates a user using the email and password.
233 *
234 * @since 4.5.0
235 *
236 * @param WP_User|WP_Error|null $user WP_User or WP_Error object if a previous
237 * callback failed authentication.
238 * @param string $email Email address for authentication.
239 * @param string $password Password for authentication.
240 * @return WP_User|WP_Error WP_User on success, WP_Error on failure.
241 */
242function wp_authenticate_email_password(
243 $user,
244 $email,
245 #[\SensitiveParameter]
246 $password
247) {
248 if ( $user instanceof WP_User ) {
249 return $user;
250 }
251
252 if ( empty( $email ) || empty( $password ) ) {
253 if ( is_wp_error( $user ) ) {
254 return $user;
255 }
256
257 $error = new WP_Error();
258
259 if ( empty( $email ) ) {
260 // Uses 'empty_username' for back-compat with wp_signon().
261 $error->add( 'empty_username', __( '<strong>Error:</strong> The email field is empty.' ) );
262 }
263
264 if ( empty( $password ) ) {
265 $error->add( 'empty_password', __( '<strong>Error:</strong> The password field is empty.' ) );
266 }
267
268 return $error;
269 }
270
271 if ( ! is_email( $email ) ) {
272 return $user;
273 }
274
275 $user = get_user_by( 'email', $email );
276
277 if ( ! $user ) {
278 return new WP_Error(
279 'invalid_email',
280 __( 'Unknown email address. Check again or try your username.' )
281 );
282 }
283
284 /** This filter is documented in wp-includes/user.php */
285 $user = apply_filters( 'wp_authenticate_user', $user, $password );
286
287 if ( is_wp_error( $user ) ) {
288 return $user;
289 }
290
291 $valid = wp_check_password( $password, $user->user_pass, $user->ID );
292
293 if ( ! $valid ) {
294 return new WP_Error(
295 'incorrect_password',
296 sprintf(
297 /* translators: %s: Email address. */
298 __( '<strong>Error:</strong> The password you entered for the email address %s is incorrect.' ),
299 '<strong>' . $email . '</strong>'
300 ) .
301 ' <a href="' . wp_lostpassword_url() . '">' .
302 __( 'Lost your password?' ) .
303 '</a>'
304 );
305 }
306
307 if ( wp_password_needs_rehash( $user->user_pass, $user->ID ) ) {
308 wp_set_password( $password, $user->ID );
309 }
310
311 return $user;
312}
313
314/**
315 * Authenticates the user using the WordPress auth cookie.
316 *
317 * @since 2.8.0
318 *
319 * @global string $auth_secure_cookie
320 *
321 * @param WP_User|WP_Error|null $user WP_User or WP_Error object from a previous callback. Default null.
322 * @param string $username Username. If not empty, cancels the cookie authentication.
323 * @param string $password Password. If not empty, cancels the cookie authentication.
324 * @return WP_User|WP_Error WP_User on success, WP_Error on failure.
325 */
326function wp_authenticate_cookie(
327 $user,
328 $username,
329 #[\SensitiveParameter]
330 $password
331) {
332 global $auth_secure_cookie;
333
334 if ( $user instanceof WP_User ) {
335 return $user;
336 }
337
338 if ( empty( $username ) && empty( $password ) ) {
339 $user_id = wp_validate_auth_cookie();
340 if ( $user_id ) {
341 return new WP_User( $user_id );
342 }
343
344 if ( $auth_secure_cookie ) {
345 $auth_cookie = SECURE_AUTH_COOKIE;
346 } else {
347 $auth_cookie = AUTH_COOKIE;
348 }
349
350 if ( ! empty( $_COOKIE[ $auth_cookie ] ) ) {
351 return new WP_Error( 'expired_session', __( 'Please log in again.' ) );
352 }
353
354 // If the cookie is not set, be silent.
355 }
356
357 return $user;
358}
359
360/**
361 * Authenticates the user using an application password.
362 *
363 * @since 5.6.0
364 *
365 * @param WP_User|WP_Error|null $input_user WP_User or WP_Error object if a previous
366 * callback failed authentication.
367 * @param string $username Username for authentication.
368 * @param string $password Password for authentication.
369 * @return WP_User|WP_Error|null WP_User on success, WP_Error on failure, null if
370 * null is passed in and this isn't an API request.
371 */
372function wp_authenticate_application_password(
373 $input_user,
374 $username,
375 #[\SensitiveParameter]
376 $password
377) {
378 if ( $input_user instanceof WP_User ) {
379 return $input_user;
380 }
381
382 if ( ! WP_Application_Passwords::is_in_use() ) {
383 return $input_user;
384 }
385
386 // The 'REST_REQUEST' check here may happen too early for the constant to be available.
387 $is_api_request = ( ( defined( 'XMLRPC_REQUEST' ) && XMLRPC_REQUEST ) || ( defined( 'REST_REQUEST' ) && REST_REQUEST ) );
388
389 /**
390 * Filters whether this is an API request that Application Passwords can be used on.
391 *
392 * By default, Application Passwords is available for the REST API and XML-RPC.
393 *
394 * @since 5.6.0
395 *
396 * @param bool $is_api_request If this is an acceptable API request.
397 */
398 $is_api_request = apply_filters( 'application_password_is_api_request', $is_api_request );
399
400 if ( ! $is_api_request ) {
401 return $input_user;
402 }
403
404 $error = null;
405 $user = get_user_by( 'login', $username );
406
407 if ( ! $user && is_email( $username ) ) {
408 $user = get_user_by( 'email', $username );
409 }
410
411 // If the login name is invalid, short circuit.
412 if ( ! $user ) {
413 if ( is_email( $username ) ) {
414 $error = new WP_Error(
415 'invalid_email',
416 __( '<strong>Error:</strong> Unknown email address. Check again or try your username.' )
417 );
418 } else {
419 $error = new WP_Error(
420 'invalid_username',
421 __( '<strong>Error:</strong> Unknown username. Check again or try your email address.' )
422 );
423 }
424 } elseif ( ! wp_is_application_passwords_available() ) {
425 $error = new WP_Error(
426 'application_passwords_disabled',
427 __( 'Application passwords are not available.' )
428 );
429 } elseif ( ! wp_is_application_passwords_available_for_user( $user ) ) {
430 $error = new WP_Error(
431 'application_passwords_disabled_for_user',
432 __( 'Application passwords are not available for your account. Please contact the site administrator for assistance.' )
433 );
434 }
435
436 if ( $error ) {
437 /**
438 * Fires when an application password failed to authenticate the user.
439 *
440 * @since 5.6.0
441 *
442 * @param WP_Error $error The authentication error.
443 */
444 do_action( 'application_password_failed_authentication', $error );
445
446 return $error;
447 }
448
449 /*
450 * Strips out anything non-alphanumeric. This is so passwords can be used with
451 * or without spaces to indicate the groupings for readability.
452 *
453 * Generated application passwords are exclusively alphanumeric.
454 */
455 $password = preg_replace( '/[^a-z\d]/i', '', $password );
456
457 $hashed_passwords = WP_Application_Passwords::get_user_application_passwords( $user->ID );
458
459 foreach ( $hashed_passwords as $key => $item ) {
460 if ( ! WP_Application_Passwords::check_password( $password, $item['password'] ) ) {
461 continue;
462 }
463
464 $error = new WP_Error();
465
466 /**
467 * Fires when an application password has been successfully checked as valid.
468 *
469 * This allows for plugins to add additional constraints to prevent an application password from being used.
470 *
471 * @since 5.6.0
472 *
473 * @param WP_Error $error The error object.
474 * @param WP_User $user The user authenticating.
475 * @param array $item The details about the application password.
476 * @param string $password The raw supplied password.
477 */
478 do_action( 'wp_authenticate_application_password_errors', $error, $user, $item, $password );
479
480 if ( $error->has_errors() ) {
481 /** This action is documented in wp-includes/user.php */
482 do_action( 'application_password_failed_authentication', $error );
483
484 return $error;
485 }
486
487 WP_Application_Passwords::record_application_password_usage( $user->ID, $item['uuid'] );
488
489 /**
490 * Fires after an application password was used for authentication.
491 *
492 * @since 5.6.0
493 *
494 * @param WP_User $user The user who was authenticated.
495 * @param array $item The application password used.
496 */
497 do_action( 'application_password_did_authenticate', $user, $item );
498
499 return $user;
500 }
501
502 $error = new WP_Error(
503 'incorrect_password',
504 __( 'The provided password is an invalid application password.' )
505 );
506
507 /** This action is documented in wp-includes/user.php */
508 do_action( 'application_password_failed_authentication', $error );
509
510 return $error;
511}
512
513/**
514 * Validates the application password credentials passed via Basic Authentication.
515 *
516 * @since 5.6.0
517 *
518 * @param int|false $input_user User ID if one has been determined, false otherwise.
519 * @return int|false The authenticated user ID if successful, false otherwise.
520 */
521function wp_validate_application_password( $input_user ) {
522 // Don't authenticate twice.
523 if ( ! empty( $input_user ) ) {
524 return $input_user;
525 }
526
527 if ( ! wp_is_application_passwords_available() ) {
528 return $input_user;
529 }
530
531 // Both $_SERVER['PHP_AUTH_USER'] and $_SERVER['PHP_AUTH_PW'] must be set in order to attempt authentication.
532 if ( ! isset( $_SERVER['PHP_AUTH_USER'], $_SERVER['PHP_AUTH_PW'] ) ) {
533 return $input_user;
534 }
535
536 $authenticated = wp_authenticate_application_password( null, $_SERVER['PHP_AUTH_USER'], $_SERVER['PHP_AUTH_PW'] );
537
538 if ( $authenticated instanceof WP_User ) {
539 return $authenticated->ID;
540 }
541
542 // If it wasn't a user what got returned, just pass on what we had received originally.
543 return $input_user;
544}
545
546/**
547 * For Multisite blogs, checks if the authenticated user has been marked as a
548 * spammer, or if the user's primary blog has been marked as spam.
549 *
550 * @since 3.7.0
551 *
552 * @param WP_User|WP_Error|null $user WP_User or WP_Error object from a previous callback. Default null.
553 * @return WP_User|WP_Error WP_User on success, WP_Error if the user is considered a spammer.
554 */
555function wp_authenticate_spam_check( $user ) {
556 if ( $user instanceof WP_User && is_multisite() ) {
557 /**
558 * Filters whether the user has been marked as a spammer.
559 *
560 * @since 3.7.0
561 *
562 * @param bool $spammed Whether the user is considered a spammer.
563 * @param WP_User $user User to check against.
564 */
565 $spammed = apply_filters( 'check_is_user_spammed', is_user_spammy( $user ), $user );
566
567 if ( $spammed ) {
568 return new WP_Error( 'spammer_account', __( '<strong>Error:</strong> Your account has been marked as a spammer.' ) );
569 }
570 }
571 return $user;
572}
573
574/**
575 * Validates the logged-in cookie.
576 *
577 * Checks the logged-in cookie if the previous auth cookie could not be
578 * validated and parsed.
579 *
580 * This is a callback for the {@see 'determine_current_user'} filter, rather than API.
581 *
582 * @since 3.9.0
583 *
584 * @param int|false $user_id The user ID (or false) as received from
585 * the `determine_current_user` filter.
586 * @return int|false User ID if validated, false otherwise. If a user ID from
587 * an earlier filter callback is received, that value is returned.
588 */
589function wp_validate_logged_in_cookie( $user_id ) {
590 if ( $user_id ) {
591 return $user_id;
592 }
593
594 if ( is_blog_admin() || is_network_admin() || empty( $_COOKIE[ LOGGED_IN_COOKIE ] ) ) {
595 return false;
596 }
597
598 return wp_validate_auth_cookie( $_COOKIE[ LOGGED_IN_COOKIE ], 'logged_in' );
599}
600
601/**
602 * Gets the number of posts a user has written.
603 *
604 * @since 3.0.0
605 * @since 4.1.0 Added `$post_type` argument.
606 * @since 4.3.0 Added `$public_only` argument. Added the ability to pass an array
607 * of post types to `$post_type`.
608 *
609 * @global wpdb $wpdb WordPress database abstraction object.
610 *
611 * @param int $userid User ID.
612 * @param array|string $post_type Optional. Single post type or array of post types to count the number of posts for. Default 'post'.
613 * @param bool $public_only Optional. Whether to only return counts for public posts. Default false.
614 * @return string Number of posts the user has written in this post type.
615 */
616function count_user_posts( $userid, $post_type = 'post', $public_only = false ) {
617 global $wpdb;
618
619 $post_type = array_unique( (array) $post_type );
620 sort( $post_type );
621
622 $where = get_posts_by_author_sql( $post_type, true, $userid, $public_only );
623 $query = "SELECT COUNT(*) FROM $wpdb->posts $where";
624
625 $last_changed = wp_cache_get_last_changed( 'posts' );
626 $cache_key = 'count_user_posts:' . md5( $query );
627 $count = wp_cache_get_salted( $cache_key, 'post-queries', $last_changed );
628 if ( false === $count ) {
629 $count = $wpdb->get_var( $query );
630 wp_cache_set_salted( $cache_key, $count, 'post-queries', $last_changed );
631 }
632
633 /**
634 * Filters the number of posts a user has written.
635 *
636 * @since 2.7.0
637 * @since 4.1.0 Added `$post_type` argument.
638 * @since 4.3.1 Added `$public_only` argument.
639 *
640 * @param string $count The user's post count as a numeric string.
641 * @param int $userid User ID.
642 * @param string|array $post_type Single post type or array of post types to count the number of posts for.
643 * @param bool $public_only Whether to limit counted posts to public posts.
644 */
645 return apply_filters( 'get_usernumposts', $count, $userid, $post_type, $public_only );
646}
647
648/**
649 * Gets the number of posts written by a list of users.
650 *
651 * @since 3.0.0
652 * @since 6.9.0 The results are now cached.
653 *
654 * @global wpdb $wpdb WordPress database abstraction object.
655 *
656 * @param int[] $users Array of user IDs.
657 * @param string|string[] $post_type Optional. Single post type or array of post types to check. Defaults to 'post'.
658 * @param bool $public_only Optional. Only return counts for public posts. Defaults to false.
659 * @return array<int, string> Amount of posts each user has written, as strings, keyed by user ID.
660 */
661function count_many_users_posts( $users, $post_type = 'post', $public_only = false ) {
662 global $wpdb;
663
664 if ( empty( $users ) || ! is_array( $users ) ) {
665 return array();
666 }
667
668 /**
669 * Filters whether to short-circuit performing the post counts.
670 *
671 * When filtering, return an array of posts counts as strings, keyed
672 * by the user ID.
673 *
674 * @since 6.8.0
675 *
676 * @param string[]|null $count The post counts. Return a non-null value to short-circuit.
677 * @param int[] $users Array of user IDs.
678 * @param string|string[] $post_type Single post type or array of post types to check.
679 * @param bool $public_only Whether to only return counts for public posts.
680 */
681 $pre = apply_filters( 'pre_count_many_users_posts', null, $users, $post_type, $public_only );
682 if ( null !== $pre ) {
683 return $pre;
684 }
685
686 // Cleanup the users array. Remove duplicates and sort for consistent ordering.
687 $users = array_unique( array_filter( array_map( 'intval', $users ) ) );
688 sort( $users );
689
690 // Cleanup the post type argument. Remove duplicates and sort for consistent ordering.
691 $post_type = array_unique( (array) $post_type );
692 sort( $post_type );
693
694 $userlist = implode( ',', $users );
695 $where = get_posts_by_author_sql( $post_type, true, null, $public_only );
696 $query = "SELECT post_author, COUNT(*) FROM $wpdb->posts $where AND post_author IN ($userlist) GROUP BY post_author";
697 $cache_key = 'count_many_users_posts:' . md5( $query );
698 $cache_salts = array( wp_cache_get_last_changed( 'posts' ), wp_cache_get_last_changed( 'users' ) );
699 $count = wp_cache_get_salted( $cache_key, 'post-queries', $cache_salts );
700
701 if ( false === $count ) {
702 $result = $wpdb->get_results( $query, ARRAY_N );
703
704 $count = array_fill_keys( $users, 0 );
705 foreach ( $result as $row ) {
706 $count[ $row[0] ] = $row[1];
707 }
708
709 wp_cache_set_salted( $cache_key, $count, 'post-queries', $cache_salts, HOUR_IN_SECONDS );
710 }
711
712 return $count;
713}
714
715//
716// User option functions.
717//
718
719/**
720 * Gets the current user's ID.
721 *
722 * @since MU (3.0.0)
723 *
724 * @return int The current user's ID, or 0 if no user is logged in.
725 */
726function get_current_user_id() {
727 if ( ! function_exists( 'wp_get_current_user' ) ) {
728 return 0;
729 }
730 $user = wp_get_current_user();
731 return ( isset( $user->ID ) ? (int) $user->ID : 0 );
732}
733
734/**
735 * Retrieves user option that can be either per Site or per Network.
736 *
737 * If the user ID is not given, then the current user will be used instead. If
738 * the user ID is given, then the user data will be retrieved. The filter for
739 * the result, will also pass the original option name and finally the user data
740 * object as the third parameter.
741 *
742 * The option will first check for the per site name and then the per Network name.
743 *
744 * @since 2.0.0
745 *
746 * @global wpdb $wpdb WordPress database abstraction object.
747 *
748 * @param string $option User option name.
749 * @param int $user Optional. User ID.
750 * @param string $deprecated Use get_option() to check for an option in the options table.
751 * @return mixed User option value on success, false on failure.
752 */
753function get_user_option( $option, $user = 0, $deprecated = '' ) {
754 global $wpdb;
755
756 if ( ! empty( $deprecated ) ) {
757 _deprecated_argument( __FUNCTION__, '3.0.0' );
758 }
759
760 if ( empty( $user ) ) {
761 $user = get_current_user_id();
762 }
763
764 $user = get_userdata( $user );
765 if ( ! $user ) {
766 return false;
767 }
768
769 $prefix = $wpdb->get_blog_prefix();
770 if ( $user->has_prop( $prefix . $option ) ) { // Blog-specific.
771 $result = $user->get( $prefix . $option );
772 } elseif ( $user->has_prop( $option ) ) { // User-specific and cross-blog.
773 $result = $user->get( $option );
774 } else {
775 $result = false;
776 }
777
778 /**
779 * Filters a specific user option value.
780 *
781 * The dynamic portion of the hook name, `$option`, refers to the user option name.
782 *
783 * @since 2.5.0
784 *
785 * @param mixed $result Value for the user's option.
786 * @param string $option Name of the option being retrieved.
787 * @param WP_User $user WP_User object of the user whose option is being retrieved.
788 */
789 return apply_filters( "get_user_option_{$option}", $result, $option, $user );
790}
791
792/**
793 * Updates user option with global blog capability.
794 *
795 * User options are just like user metadata except that they have support for
796 * global blog options. If the 'is_global' parameter is false, which it is by default,
797 * it will prepend the WordPress table prefix to the option name.
798 *
799 * Deletes the user option if $newvalue is empty.
800 *
801 * @since 2.0.0
802 *
803 * @global wpdb $wpdb WordPress database abstraction object.
804 *
805 * @param int $user_id User ID.
806 * @param string $option_name User option name.
807 * @param mixed $newvalue User option value.
808 * @param bool $is_global Optional. Whether option name is global or blog specific.
809 * Default false (blog specific).
810 * @return int|bool User meta ID if the option didn't exist, true on successful update,
811 * false on failure.
812 */
813function update_user_option( $user_id, $option_name, $newvalue, $is_global = false ) {
814 global $wpdb;
815
816 if ( ! $is_global ) {
817 $option_name = $wpdb->get_blog_prefix() . $option_name;
818 }
819
820 return update_user_meta( $user_id, $option_name, $newvalue );
821}
822
823/**
824 * Deletes user option with global blog capability.
825 *
826 * User options are just like user metadata except that they have support for
827 * global blog options. If the 'is_global' parameter is false, which it is by default,
828 * it will prepend the WordPress table prefix to the option name.
829 *
830 * @since 3.0.0
831 *
832 * @global wpdb $wpdb WordPress database abstraction object.
833 *
834 * @param int $user_id User ID
835 * @param string $option_name User option name.
836 * @param bool $is_global Optional. Whether option name is global or blog specific.
837 * Default false (blog specific).
838 * @return bool True on success, false on failure.
839 */
840function delete_user_option( $user_id, $option_name, $is_global = false ) {
841 global $wpdb;
842
843 if ( ! $is_global ) {
844 $option_name = $wpdb->get_blog_prefix() . $option_name;
845 }
846
847 return delete_user_meta( $user_id, $option_name );
848}
849
850/**
851 * Retrieves user info by user ID.
852 *
853 * @since 6.7.0
854 *
855 * @param int $user_id User ID.
856 *
857 * @return WP_User|false WP_User object on success, false on failure.
858 */
859function get_user( $user_id ) {
860 return get_user_by( 'id', $user_id );
861}
862
863/**
864 * Retrieves list of users matching criteria.
865 *
866 * @since 3.1.0
867 *
868 * @see WP_User_Query
869 *
870 * @param array $args Optional. Arguments to retrieve users. See WP_User_Query::prepare_query()
871 * for more information on accepted arguments.
872 * @return array List of users.
873 */
874function get_users( $args = array() ) {
875
876 $args = wp_parse_args( $args );
877 $args['count_total'] = false;
878
879 $user_search = new WP_User_Query( $args );
880
881 return (array) $user_search->get_results();
882}
883
884/**
885 * Lists all the users of the site, with several options available.
886 *
887 * @since 5.9.0
888 *
889 * @param string|array $args {
890 * Optional. Array or string of default arguments.
891 *
892 * @type string $orderby How to sort the users. Accepts 'nicename', 'email', 'url', 'registered',
893 * 'user_nicename', 'user_email', 'user_url', 'user_registered', 'name',
894 * 'display_name', 'post_count', 'ID', 'meta_value', 'user_login'. Default 'name'.
895 * @type string $order Sorting direction for $orderby. Accepts 'ASC', 'DESC'. Default 'ASC'.
896 * @type int $number Maximum users to return or display. Default empty (all users).
897 * @type bool $exclude_admin Whether to exclude the 'admin' account, if it exists. Default false.
898 * @type bool $show_fullname Whether to show the user's full name. Default false.
899 * @type string $feed If not empty, show a link to the user's feed and use this text as the alt
900 * parameter of the link. Default empty.
901 * @type string $feed_image If not empty, show a link to the user's feed and use this image URL as
902 * clickable anchor. Default empty.
903 * @type string $feed_type The feed type to link to, such as 'rss2'. Defaults to default feed type.
904 * @type bool $echo Whether to output the result or instead return it. Default true.
905 * @type string $style If 'list', each user is wrapped in an `<li>` element, otherwise the users
906 * will be separated by commas.
907 * @type bool $html Whether to list the items in HTML form or plaintext. Default true.
908 * @type string $exclude An array, comma-, or space-separated list of user IDs to exclude. Default empty.
909 * @type string $include An array, comma-, or space-separated list of user IDs to include. Default empty.
910 * }
911 * @return string|null The output if echo is false. Otherwise null.
912 */
913function wp_list_users( $args = array() ) {
914 $defaults = array(
915 'orderby' => 'name',
916 'order' => 'ASC',
917 'number' => '',
918 'exclude_admin' => true,
919 'show_fullname' => false,
920 'feed' => '',
921 'feed_image' => '',
922 'feed_type' => '',
923 'echo' => true,
924 'style' => 'list',
925 'html' => true,
926 'exclude' => '',
927 'include' => '',
928 );
929
930 $parsed_args = wp_parse_args( $args, $defaults );
931
932 $return = '';
933
934 $query_args = wp_array_slice_assoc( $parsed_args, array( 'orderby', 'order', 'number', 'exclude', 'include' ) );
935 $query_args['fields'] = 'ids';
936
937 /**
938 * Filters the query arguments for the list of all users of the site.
939 *
940 * @since 6.1.0
941 *
942 * @param array $query_args The query arguments for get_users().
943 * @param array $parsed_args The arguments passed to wp_list_users() combined with the defaults.
944 */
945 $query_args = apply_filters( 'wp_list_users_args', $query_args, $parsed_args );
946
947 $users = get_users( $query_args );
948
949 foreach ( $users as $user_id ) {
950 $user = get_userdata( $user_id );
951
952 if ( $parsed_args['exclude_admin'] && 'admin' === $user->display_name ) {
953 continue;
954 }
955
956 if ( $parsed_args['show_fullname'] && '' !== $user->first_name && '' !== $user->last_name ) {
957 $name = sprintf(
958 /* translators: 1: User's first name, 2: Last name. */
959 _x( '%1$s %2$s', 'Display name based on first name and last name' ),
960 $user->first_name,
961 $user->last_name
962 );
963 } else {
964 $name = $user->display_name;
965 }
966
967 if ( ! $parsed_args['html'] ) {
968 $return .= $name . ', ';
969
970 continue; // No need to go further to process HTML.
971 }
972
973 if ( 'list' === $parsed_args['style'] ) {
974 $return .= '<li>';
975 }
976
977 $row = $name;
978
979 if ( ! empty( $parsed_args['feed_image'] ) || ! empty( $parsed_args['feed'] ) ) {
980 $row .= ' ';
981 if ( empty( $parsed_args['feed_image'] ) ) {
982 $row .= '(';
983 }
984
985 $row .= '<a href="' . get_author_feed_link( $user->ID, $parsed_args['feed_type'] ) . '"';
986
987 $alt = '';
988 if ( ! empty( $parsed_args['feed'] ) ) {
989 $alt = ' alt="' . esc_attr( $parsed_args['feed'] ) . '"';
990 $name = $parsed_args['feed'];
991 }
992
993 $row .= '>';
994
995 if ( ! empty( $parsed_args['feed_image'] ) ) {
996 $row .= '<img src="' . esc_url( $parsed_args['feed_image'] ) . '" style="border: none;"' . $alt . ' />';
997 } else {
998 $row .= $name;
999 }
1000
1001 $row .= '</a>';
1002
1003 if ( empty( $parsed_args['feed_image'] ) ) {
1004 $row .= ')';
1005 }
1006 }
1007
1008 $return .= $row;
1009 $return .= ( 'list' === $parsed_args['style'] ) ? '</li>' : ', ';
1010 }
1011
1012 $return = rtrim( $return, ', ' );
1013
1014 if ( ! $parsed_args['echo'] ) {
1015 return $return;
1016 }
1017 echo $return;
1018}
1019
1020/**
1021 * Gets the sites a user belongs to.
1022 *
1023 * @since 3.0.0
1024 * @since 4.7.0 Converted to use `get_sites()`.
1025 *
1026 * @global wpdb $wpdb WordPress database abstraction object.
1027 *
1028 * @param int $user_id User ID
1029 * @param bool $all Whether to retrieve all sites, or only sites that are not
1030 * marked as deleted, archived, or spam.
1031 * @return object[] A list of the user's sites. An empty array if the user doesn't exist
1032 * or belongs to no sites.
1033 */
1034function get_blogs_of_user( $user_id, $all = false ) {
1035 global $wpdb;
1036
1037 $user_id = (int) $user_id;
1038
1039 // Logged out users can't have sites.
1040 if ( empty( $user_id ) ) {
1041 return array();
1042 }
1043
1044 /**
1045 * Filters the list of a user's sites before it is populated.
1046 *
1047 * Returning a non-null value from the filter will effectively short circuit
1048 * get_blogs_of_user(), returning that value instead.
1049 *
1050 * @since 4.6.0
1051 *
1052 * @param null|object[] $sites An array of site objects of which the user is a member.
1053 * @param int $user_id User ID.
1054 * @param bool $all Whether the returned array should contain all sites, including
1055 * those marked 'deleted', 'archived', or 'spam'. Default false.
1056 */
1057 $sites = apply_filters( 'pre_get_blogs_of_user', null, $user_id, $all );
1058
1059 if ( null !== $sites ) {
1060 return $sites;
1061 }
1062
1063 $keys = get_user_meta( $user_id );
1064 if ( empty( $keys ) ) {
1065 return array();
1066 }
1067
1068 if ( ! is_multisite() ) {
1069 $site_id = get_current_blog_id();
1070 $sites = array( $site_id => new stdClass() );
1071 $sites[ $site_id ]->userblog_id = $site_id;
1072 $sites[ $site_id ]->blogname = get_option( 'blogname' );
1073 $sites[ $site_id ]->domain = '';
1074 $sites[ $site_id ]->path = '';
1075 $sites[ $site_id ]->site_id = 1;
1076 $sites[ $site_id ]->siteurl = get_option( 'siteurl' );
1077 $sites[ $site_id ]->archived = 0;
1078 $sites[ $site_id ]->spam = 0;
1079 $sites[ $site_id ]->deleted = 0;
1080 return $sites;
1081 }
1082
1083 $site_ids = array();
1084
1085 if ( isset( $keys[ $wpdb->base_prefix . 'capabilities' ] ) && defined( 'MULTISITE' ) ) {
1086 $site_ids[] = 1;
1087 unset( $keys[ $wpdb->base_prefix . 'capabilities' ] );
1088 }
1089
1090 $keys = array_keys( $keys );
1091
1092 foreach ( $keys as $key ) {
1093 if ( ! str_ends_with( $key, 'capabilities' ) ) {
1094 continue;
1095 }
1096 if ( $wpdb->base_prefix && ! str_starts_with( $key, $wpdb->base_prefix ) ) {
1097 continue;
1098 }
1099 $site_id = str_replace( array( $wpdb->base_prefix, '_capabilities' ), '', $key );
1100 if ( ! is_numeric( $site_id ) ) {
1101 continue;
1102 }
1103
1104 $site_ids[] = (int) $site_id;
1105 }
1106
1107 $sites = array();
1108
1109 if ( ! empty( $site_ids ) ) {
1110 $args = array(
1111 'number' => '',
1112 'site__in' => $site_ids,
1113 );
1114 if ( ! $all ) {
1115 $args['archived'] = 0;
1116 $args['spam'] = 0;
1117 $args['deleted'] = 0;
1118 }
1119
1120 $_sites = get_sites( $args );
1121
1122 foreach ( $_sites as $site ) {
1123 $sites[ $site->id ] = (object) array(
1124 'userblog_id' => $site->id,
1125 'blogname' => $site->blogname,
1126 'domain' => $site->domain,
1127 'path' => $site->path,
1128 'site_id' => $site->network_id,
1129 'siteurl' => $site->siteurl,
1130 'archived' => $site->archived,
1131 'mature' => $site->mature,
1132 'spam' => $site->spam,
1133 'deleted' => $site->deleted,
1134 );
1135 }
1136 }
1137
1138 /**
1139 * Filters the list of sites a user belongs to.
1140 *
1141 * @since MU (3.0.0)
1142 *
1143 * @param object[] $sites An array of site objects belonging to the user.
1144 * @param int $user_id User ID.
1145 * @param bool $all Whether the returned sites array should contain all sites, including
1146 * those flagged for deletion, archived, or marked as spam.
1147 */
1148 return apply_filters( 'get_blogs_of_user', $sites, $user_id, $all );
1149}
1150
1151/**
1152 * Finds out whether a user is a member of a given blog.
1153 *
1154 * @since MU (3.0.0)
1155 *
1156 * @global wpdb $wpdb WordPress database abstraction object.
1157 *
1158 * @param int $user_id Optional. The unique ID of the user. Defaults to the current user.
1159 * @param int $blog_id Optional. ID of the blog to check. Defaults to the current site.
1160 * @return bool
1161 */
1162function is_user_member_of_blog( $user_id = 0, $blog_id = 0 ) {
1163 global $wpdb;
1164
1165 $user_id = (int) $user_id;
1166 $blog_id = (int) $blog_id;
1167
1168 if ( empty( $user_id ) ) {
1169 $user_id = get_current_user_id();
1170 }
1171
1172 /*
1173 * Technically not needed, but does save calls to get_site() and get_user_meta()
1174 * in the event that the function is called when a user isn't logged in.
1175 */
1176 if ( empty( $user_id ) ) {
1177 return false;
1178 } else {
1179 $user = get_userdata( $user_id );
1180 if ( ! $user instanceof WP_User ) {
1181 return false;
1182 }
1183 }
1184
1185 if ( ! is_multisite() ) {
1186 return true;
1187 }
1188
1189 if ( empty( $blog_id ) ) {
1190 $blog_id = get_current_blog_id();
1191 }
1192
1193 $blog = get_site( $blog_id );
1194
1195 if ( ! $blog || ! isset( $blog->domain ) || $blog->archived || $blog->spam || $blog->deleted ) {
1196 return false;
1197 }
1198
1199 if ( 1 === $blog_id ) {
1200 $capabilities_key = $wpdb->base_prefix . 'capabilities';
1201 } else {
1202 $capabilities_key = $wpdb->base_prefix . $blog_id . '_capabilities';
1203 }
1204 $has_cap = get_user_meta( $user_id, $capabilities_key, true );
1205
1206 return is_array( $has_cap );
1207}
1208
1209/**
1210 * Adds meta data to a user.
1211 *
1212 * For historical reasons both the meta key and the meta value are expected to be "slashed" (slashes escaped) on input.
1213 *
1214 * @since 3.0.0
1215 *
1216 * @param int $user_id User ID.
1217 * @param string $meta_key Metadata name.
1218 * @param mixed $meta_value Metadata value. Arrays and objects are stored as serialized data and
1219 * will be returned as the same type when retrieved. Other data types will
1220 * be stored as strings in the database:
1221 * - false is stored and retrieved as an empty string ('')
1222 * - true is stored and retrieved as '1'
1223 * - numbers (both integer and float) are stored and retrieved as strings
1224 * Must be serializable if non-scalar.
1225 * @param bool $unique Optional. Whether the same key should not be added.
1226 * Default false.
1227 * @return int|false Meta ID on success, false on failure.
1228 */
1229function add_user_meta( $user_id, $meta_key, $meta_value, $unique = false ) {
1230 return add_metadata( 'user', $user_id, $meta_key, $meta_value, $unique );
1231}
1232
1233/**
1234 * Removes metadata matching criteria from a user.
1235 *
1236 * You can match based on the key, or key and value. Removing based on key and
1237 * value, will keep from removing duplicate metadata with the same key. It also
1238 * allows removing all metadata matching key, if needed.
1239 *
1240 * For historical reasons both the meta key and the meta value are expected to be "slashed" (slashes escaped) on input.
1241 *
1242 * @since 3.0.0
1243 *
1244 * @link https://developer.wordpress.org/reference/functions/delete_user_meta/
1245 *
1246 * @param int $user_id User ID
1247 * @param string $meta_key Metadata name.
1248 * @param mixed $meta_value Optional. Metadata value. If provided,
1249 * rows will only be removed that match the value.
1250 * Must be serializable if non-scalar. Default empty.
1251 * @return bool True on success, false on failure.
1252 */
1253function delete_user_meta( $user_id, $meta_key, $meta_value = '' ) {
1254 return delete_metadata( 'user', $user_id, $meta_key, $meta_value );
1255}
1256
1257/**
1258 * Retrieves user meta field for a user.
1259 *
1260 * @since 3.0.0
1261 *
1262 * @link https://developer.wordpress.org/reference/functions/get_user_meta/
1263 *
1264 * @param int $user_id User ID.
1265 * @param string $key Optional. The meta key to retrieve. By default,
1266 * returns data for all keys.
1267 * @param bool $single Optional. Whether to return a single value.
1268 * This parameter has no effect if `$key` is not specified.
1269 * Default false.
1270 * @return mixed An array of values if `$single` is false.
1271 * The value of meta data field if `$single` is true.
1272 * False for an invalid `$user_id` (non-numeric, zero, or negative value).
1273 * An empty array if a valid but non-existing user ID is passed and `$single` is false.
1274 * An empty string if a valid but non-existing user ID is passed and `$single` is true.
1275 * Note: Non-serialized values are returned as strings:
1276 * - false values are returned as empty strings ('')
1277 * - true values are returned as '1'
1278 * - numbers (both integer and float) are returned as strings
1279 * Arrays and objects retain their original type.
1280 */
1281function get_user_meta( $user_id, $key = '', $single = false ) {
1282 return get_metadata( 'user', $user_id, $key, $single );
1283}
1284
1285/**
1286 * Updates user meta field based on user ID.
1287 *
1288 * Use the $prev_value parameter to differentiate between meta fields with the
1289 * same key and user ID.
1290 *
1291 * If the meta field for the user does not exist, it will be added.
1292 *
1293 * For historical reasons both the meta key and the meta value are expected to be "slashed" (slashes escaped) on input.
1294 *
1295 * @since 3.0.0
1296 *
1297 * @link https://developer.wordpress.org/reference/functions/update_user_meta/
1298 *
1299 * @param int $user_id User ID.
1300 * @param string $meta_key Metadata key.
1301 * @param mixed $meta_value Metadata value. Must be serializable if non-scalar.
1302 * @param mixed $prev_value Optional. Previous value to check before updating.
1303 * If specified, only update existing metadata entries with
1304 * this value. Otherwise, update all entries. Default empty.
1305 * @return int|bool Meta ID if the key didn't exist, true on successful update,
1306 * false on failure or if the value passed to the function
1307 * is the same as the one that is already in the database.
1308 */
1309function update_user_meta( $user_id, $meta_key, $meta_value, $prev_value = '' ) {
1310 return update_metadata( 'user', $user_id, $meta_key, $meta_value, $prev_value );
1311}
1312
1313/**
1314 * Counts number of users who have each of the user roles.
1315 *
1316 * Assumes there are neither duplicated nor orphaned capabilities meta_values.
1317 * Assumes role names are unique phrases. Same assumption made by WP_User_Query::prepare_query()
1318 * Using $strategy = 'time' this is CPU-intensive and should handle around 10^7 users.
1319 * Using $strategy = 'memory' this is memory-intensive and should handle around 10^5 users, but see WP Bug #12257.
1320 *
1321 * @since 3.0.0
1322 * @since 4.4.0 The number of users with no role is now included in the `none` element.
1323 * @since 4.9.0 The `$site_id` parameter was added to support multisite.
1324 *
1325 * @global wpdb $wpdb WordPress database abstraction object.
1326 *
1327 * @param string $strategy Optional. The computational strategy to use when counting the users.
1328 * Accepts either 'time' or 'memory'. Default 'time'.
1329 * @param int|null $site_id Optional. The site ID to count users for. Defaults to the current site.
1330 * @return array {
1331 * User counts.
1332 *
1333 * @type int $total_users Total number of users on the site.
1334 * @type int[] $avail_roles Array of user counts keyed by user role.
1335 * }
1336 */
1337function count_users( $strategy = 'time', $site_id = null ) {
1338 global $wpdb;
1339
1340 // Initialize.
1341 if ( ! $site_id ) {
1342 $site_id = get_current_blog_id();
1343 }
1344
1345 /**
1346 * Filters the user count before queries are run.
1347 *
1348 * Return a non-null value to cause count_users() to return early.
1349 *
1350 * @since 5.1.0
1351 *
1352 * @param null|array $result The value to return instead. Default null to continue with the query.
1353 * @param string $strategy Optional. The computational strategy to use when counting the users.
1354 * Accepts either 'time' or 'memory'. Default 'time'.
1355 * @param int $site_id The site ID to count users for.
1356 */
1357 $pre = apply_filters( 'pre_count_users', null, $strategy, $site_id );
1358
1359 if ( null !== $pre ) {
1360 return $pre;
1361 }
1362
1363 $blog_prefix = $wpdb->get_blog_prefix( $site_id );
1364 $result = array();
1365
1366 if ( 'time' === $strategy ) {
1367 if ( is_multisite() && get_current_blog_id() !== $site_id ) {
1368 switch_to_blog( $site_id );
1369 $avail_roles = wp_roles()->get_names();
1370 restore_current_blog();
1371 } else {
1372 $avail_roles = wp_roles()->get_names();
1373 }
1374
1375 // Build a CPU-intensive query that will return concise information.
1376 $select_count = array();
1377 foreach ( $avail_roles as $this_role => $name ) {
1378 $select_count[] = $wpdb->prepare( 'COUNT(NULLIF(`meta_value` LIKE %s, false))', '%' . $wpdb->esc_like( '"' . $this_role . '"' ) . '%' );
1379 }
1380 $select_count[] = "COUNT(NULLIF(`meta_value` = 'a:0:{}', false))";
1381 $select_count = implode( ', ', $select_count );
1382
1383 // Add the meta_value index to the selection list, then run the query.
1384 $row = $wpdb->get_row(
1385 "
1386 SELECT {$select_count}, COUNT(*)
1387 FROM {$wpdb->usermeta}
1388 INNER JOIN {$wpdb->users} ON user_id = ID
1389 WHERE meta_key = '{$blog_prefix}capabilities'
1390 ",
1391 ARRAY_N
1392 );
1393
1394 // Run the previous loop again to associate results with role names.
1395 $col = 0;
1396 $role_counts = array();
1397 foreach ( $avail_roles as $this_role => $name ) {
1398 $count = (int) $row[ $col++ ];
1399 if ( $count > 0 ) {
1400 $role_counts[ $this_role ] = $count;
1401 }
1402 }
1403
1404 $role_counts['none'] = (int) $row[ $col++ ];
1405
1406 // Get the meta_value index from the end of the result set.
1407 $total_users = (int) $row[ $col ];
1408
1409 $result['total_users'] = $total_users;
1410 $result['avail_roles'] =& $role_counts;
1411 } else {
1412 $avail_roles = array(
1413 'none' => 0,
1414 );
1415
1416 $users_of_blog = $wpdb->get_col(
1417 "
1418 SELECT meta_value
1419 FROM {$wpdb->usermeta}
1420 INNER JOIN {$wpdb->users} ON user_id = ID
1421 WHERE meta_key = '{$blog_prefix}capabilities'
1422 "
1423 );
1424
1425 foreach ( $users_of_blog as $caps_meta ) {
1426 $b_roles = maybe_unserialize( $caps_meta );
1427 if ( ! is_array( $b_roles ) ) {
1428 continue;
1429 }
1430 if ( empty( $b_roles ) ) {
1431 ++$avail_roles['none'];
1432 }
1433 foreach ( $b_roles as $b_role => $val ) {
1434 if ( isset( $avail_roles[ $b_role ] ) ) {
1435 ++$avail_roles[ $b_role ];
1436 } else {
1437 $avail_roles[ $b_role ] = 1;
1438 }
1439 }
1440 }
1441
1442 $result['total_users'] = count( $users_of_blog );
1443 $result['avail_roles'] =& $avail_roles;
1444 }
1445
1446 return $result;
1447}
1448
1449/**
1450 * Returns the number of active users in your installation.
1451 *
1452 * Note that on a large site the count may be cached and only updated twice daily.
1453 *
1454 * @since MU (3.0.0)
1455 * @since 4.8.0 The `$network_id` parameter has been added.
1456 * @since 6.0.0 Moved to wp-includes/user.php.
1457 *
1458 * @param int|null $network_id ID of the network. Defaults to the current network.
1459 * @return int Number of active users on the network.
1460 */
1461function get_user_count( $network_id = null ) {
1462 if ( ! is_multisite() && null !== $network_id ) {
1463 _doing_it_wrong(
1464 __FUNCTION__,
1465 sprintf(
1466 /* translators: %s: $network_id */
1467 __( 'Unable to pass %s if not using multisite.' ),
1468 '<code>$network_id</code>'
1469 ),
1470 '6.0.0'
1471 );
1472 }
1473
1474 return (int) get_network_option( $network_id, 'user_count', -1 );
1475}
1476
1477/**
1478 * Updates the total count of users on the site if live user counting is enabled.
1479 *
1480 * @since 6.0.0
1481 *
1482 * @param int|null $network_id ID of the network. Defaults to the current network.
1483 * @return bool Whether the update was successful.
1484 */
1485function wp_maybe_update_user_counts( $network_id = null ) {
1486 if ( ! is_multisite() && null !== $network_id ) {
1487 _doing_it_wrong(
1488 __FUNCTION__,
1489 sprintf(
1490 /* translators: %s: $network_id */
1491 __( 'Unable to pass %s if not using multisite.' ),
1492 '<code>$network_id</code>'
1493 ),
1494 '6.0.0'
1495 );
1496 }
1497
1498 $is_small_network = ! wp_is_large_user_count( $network_id );
1499 /** This filter is documented in wp-includes/ms-functions.php */
1500 if ( ! apply_filters( 'enable_live_network_counts', $is_small_network, 'users' ) ) {
1501 return false;
1502 }
1503
1504 return wp_update_user_counts( $network_id );
1505}
1506
1507/**
1508 * Updates the total count of users on the site.
1509 *
1510 * @global wpdb $wpdb WordPress database abstraction object.
1511 * @since 6.0.0
1512 *
1513 * @param int|null $network_id ID of the network. Defaults to the current network.
1514 * @return bool Whether the update was successful.
1515 */
1516function wp_update_user_counts( $network_id = null ) {
1517 global $wpdb;
1518
1519 if ( ! is_multisite() && null !== $network_id ) {
1520 _doing_it_wrong(
1521 __FUNCTION__,
1522 sprintf(
1523 /* translators: %s: $network_id */
1524 __( 'Unable to pass %s if not using multisite.' ),
1525 '<code>$network_id</code>'
1526 ),
1527 '6.0.0'
1528 );
1529 }
1530
1531 $query = "SELECT COUNT(ID) as c FROM $wpdb->users";
1532 if ( is_multisite() ) {
1533 $query .= " WHERE spam = '0' AND deleted = '0'";
1534 }
1535
1536 $count = $wpdb->get_var( $query );
1537
1538 return update_network_option( $network_id, 'user_count', $count );
1539}
1540
1541/**
1542 * Schedules a recurring recalculation of the total count of users.
1543 *
1544 * @since 6.0.0
1545 */
1546function wp_schedule_update_user_counts() {
1547 if ( ! is_main_site() ) {
1548 return;
1549 }
1550
1551 if ( ! wp_next_scheduled( 'wp_update_user_counts' ) && ! wp_installing() ) {
1552 wp_schedule_event( time(), 'twicedaily', 'wp_update_user_counts' );
1553 }
1554}
1555
1556/**
1557 * Determines whether the site has a large number of users.
1558 *
1559 * The default criteria for a large site is more than 10,000 users.
1560 *
1561 * @since 6.0.0
1562 *
1563 * @param int|null $network_id ID of the network. Defaults to the current network.
1564 * @return bool Whether the site has a large number of users.
1565 */
1566function wp_is_large_user_count( $network_id = null ) {
1567 if ( ! is_multisite() && null !== $network_id ) {
1568 _doing_it_wrong(
1569 __FUNCTION__,
1570 sprintf(
1571 /* translators: %s: $network_id */
1572 __( 'Unable to pass %s if not using multisite.' ),
1573 '<code>$network_id</code>'
1574 ),
1575 '6.0.0'
1576 );
1577 }
1578
1579 $count = get_user_count( $network_id );
1580
1581 /**
1582 * Filters whether the site is considered large, based on its number of users.
1583 *
1584 * @since 6.0.0
1585 *
1586 * @param bool $is_large_user_count Whether the site has a large number of users.
1587 * @param int $count The total number of users.
1588 * @param int|null $network_id ID of the network. `null` represents the current network.
1589 */
1590 return apply_filters( 'wp_is_large_user_count', $count > 10000, $count, $network_id );
1591}
1592
1593//
1594// Private helper functions.
1595//
1596
1597/**
1598 * Sets up global user vars.
1599 *
1600 * Used by wp_set_current_user() for back compat. Might be deprecated in the future.
1601 *
1602 * @since 2.0.4
1603 *
1604 * @global string $user_login The user username for logging in
1605 * @global WP_User $userdata User data.
1606 * @global int $user_level The level of the user
1607 * @global int $user_ID The ID of the user
1608 * @global string $user_email The email address of the user
1609 * @global string $user_url The url in the user's profile
1610 * @global string $user_identity The display name of the user
1611 *
1612 * @param int $for_user_id Optional. User ID to set up global data. Default 0.
1613 */
1614function setup_userdata( $for_user_id = 0 ) {
1615 global $user_login, $userdata, $user_level, $user_ID, $user_email, $user_url, $user_identity;
1616
1617 if ( ! $for_user_id ) {
1618 $for_user_id = get_current_user_id();
1619 }
1620 $user = get_userdata( $for_user_id );
1621
1622 if ( ! $user ) {
1623 $user_ID = 0;
1624 $user_level = 0;
1625 $userdata = null;
1626 $user_login = '';
1627 $user_email = '';
1628 $user_url = '';
1629 $user_identity = '';
1630 return;
1631 }
1632
1633 $user_ID = (int) $user->ID;
1634 $user_level = (int) $user->user_level;
1635 $userdata = $user;
1636 $user_login = $user->user_login;
1637 $user_email = $user->user_email;
1638 $user_url = $user->user_url;
1639 $user_identity = $user->display_name;
1640}
1641
1642/**
1643 * Creates dropdown HTML content of users.
1644 *
1645 * The content can either be displayed, which it is by default, or retrieved by
1646 * setting the 'echo' argument to false. The 'include' and 'exclude' arguments
1647 * are optional; if they are not specified, all users will be displayed. Only one
1648 * can be used in a single call, either 'include' or 'exclude', but not both.
1649 *
1650 * @since 2.3.0
1651 * @since 4.5.0 Added the 'display_name_with_login' value for 'show'.
1652 * @since 4.7.0 Added the 'role', 'role__in', and 'role__not_in' parameters.
1653 * @since 5.9.0 Added the 'capability', 'capability__in', and 'capability__not_in' parameters.
1654 * Deprecated the 'who' parameter.
1655 *
1656 * @param array|string $args {
1657 * Optional. Array or string of arguments to generate a drop-down of users.
1658 * See WP_User_Query::prepare_query() for additional available arguments.
1659 *
1660 * @type string $show_option_all Text to show as the drop-down default (all).
1661 * Default empty.
1662 * @type string $show_option_none Text to show as the drop-down default when no
1663 * users were found. Default empty.
1664 * @type int|string $option_none_value Value to use for `$show_option_none` when no users
1665 * were found. Default -1.
1666 * @type string $hide_if_only_one_author Whether to skip generating the drop-down
1667 * if only one user was found. Default empty.
1668 * @type string $orderby Field to order found users by. Accepts user fields.
1669 * Default 'display_name'.
1670 * @type string $order Whether to order users in ascending or descending
1671 * order. Accepts 'ASC' (ascending) or 'DESC' (descending).
1672 * Default 'ASC'.
1673 * @type int[]|string $include Array or comma-separated list of user IDs to include.
1674 * Default empty.
1675 * @type int[]|string $exclude Array or comma-separated list of user IDs to exclude.
1676 * Default empty.
1677 * @type bool|int $multi Whether to skip the ID attribute on the 'select' element.
1678 * Accepts 1|true or 0|false. Default 0|false.
1679 * @type string $show User data to display. If the selected item is empty
1680 * then the 'user_login' will be displayed in parentheses.
1681 * Accepts any user field, or 'display_name_with_login' to show
1682 * the display name with user_login in parentheses.
1683 * Default 'display_name'.
1684 * @type int|bool $echo Whether to echo or return the drop-down. Accepts 1|true (echo)
1685 * or 0|false (return). Default 1|true.
1686 * @type int $selected Which user ID should be selected. Default 0.
1687 * @type bool $include_selected Whether to always include the selected user ID in the drop-
1688 * down. Default false.
1689 * @type string $name Name attribute of select element. Default 'user'.
1690 * @type string $id ID attribute of the select element. Default is the value of `$name`.
1691 * @type string $class Class attribute of the select element. Default empty.
1692 * @type int $blog_id ID of blog (Multisite only). Default is ID of the current blog.
1693 * @type string $who Deprecated, use `$capability` instead.
1694 * Which type of users to query. Accepts only an empty string or
1695 * 'authors'. Default empty (all users).
1696 * @type string|string[] $role An array or a comma-separated list of role names that users
1697 * must match to be included in results. Note that this is
1698 * an inclusive list: users must match *each* role. Default empty.
1699 * @type string[] $role__in An array of role names. Matched users must have at least one
1700 * of these roles. Default empty array.
1701 * @type string[] $role__not_in An array of role names to exclude. Users matching one or more
1702 * of these roles will not be included in results. Default empty array.
1703 * @type string|string[] $capability An array or a comma-separated list of capability names that users
1704 * must match to be included in results. Note that this is
1705 * an inclusive list: users must match *each* capability.
1706 * Does NOT work for capabilities not in the database or filtered
1707 * via {@see 'map_meta_cap'}. Default empty.
1708 * @type string[] $capability__in An array of capability names. Matched users must have at least one
1709 * of these capabilities.
1710 * Does NOT work for capabilities not in the database or filtered
1711 * via {@see 'map_meta_cap'}. Default empty array.
1712 * @type string[] $capability__not_in An array of capability names to exclude. Users matching one or more
1713 * of these capabilities will not be included in results.
1714 * Does NOT work for capabilities not in the database or filtered
1715 * via {@see 'map_meta_cap'}. Default empty array.
1716 * }
1717 * @return string HTML dropdown list of users.
1718 */
1719function wp_dropdown_users( $args = '' ) {
1720 $defaults = array(
1721 'show_option_all' => '',
1722 'show_option_none' => '',
1723 'hide_if_only_one_author' => '',
1724 'orderby' => 'display_name',
1725 'order' => 'ASC',
1726 'include' => '',
1727 'exclude' => '',
1728 'multi' => 0,
1729 'show' => 'display_name',
1730 'echo' => 1,
1731 'selected' => 0,
1732 'name' => 'user',
1733 'class' => '',
1734 'id' => '',
1735 'blog_id' => get_current_blog_id(),
1736 'who' => '',
1737 'include_selected' => false,
1738 'option_none_value' => -1,
1739 'role' => '',
1740 'role__in' => array(),
1741 'role__not_in' => array(),
1742 'capability' => '',
1743 'capability__in' => array(),
1744 'capability__not_in' => array(),
1745 );
1746
1747 $defaults['selected'] = is_author() ? get_query_var( 'author' ) : 0;
1748
1749 $parsed_args = wp_parse_args( $args, $defaults );
1750
1751 $query_args = wp_array_slice_assoc(
1752 $parsed_args,
1753 array(
1754 'blog_id',
1755 'include',
1756 'exclude',
1757 'orderby',
1758 'order',
1759 'who',
1760 'role',
1761 'role__in',
1762 'role__not_in',
1763 'capability',
1764 'capability__in',
1765 'capability__not_in',
1766 )
1767 );
1768
1769 $fields = array( 'ID', 'user_login' );
1770
1771 $show = ! empty( $parsed_args['show'] ) ? $parsed_args['show'] : 'display_name';
1772 if ( 'display_name_with_login' === $show ) {
1773 $fields[] = 'display_name';
1774 } else {
1775 $fields[] = $show;
1776 }
1777
1778 $query_args['fields'] = $fields;
1779
1780 $show_option_all = $parsed_args['show_option_all'];
1781 $show_option_none = $parsed_args['show_option_none'];
1782 $option_none_value = $parsed_args['option_none_value'];
1783
1784 /**
1785 * Filters the query arguments for the list of users in the dropdown.
1786 *
1787 * @since 4.4.0
1788 *
1789 * @param array $query_args The query arguments for get_users().
1790 * @param array $parsed_args The arguments passed to wp_dropdown_users() combined with the defaults.
1791 */
1792 $query_args = apply_filters( 'wp_dropdown_users_args', $query_args, $parsed_args );
1793
1794 $users = get_users( $query_args );
1795
1796 $output = '';
1797 if ( ! empty( $users ) && ( empty( $parsed_args['hide_if_only_one_author'] ) || count( $users ) > 1 ) ) {
1798 $name = esc_attr( $parsed_args['name'] );
1799 if ( $parsed_args['multi'] && ! $parsed_args['id'] ) {
1800 $id = '';
1801 } else {
1802 $id = $parsed_args['id'] ? " id='" . esc_attr( $parsed_args['id'] ) . "'" : " id='$name'";
1803 }
1804 $output = "<select name='{$name}'{$id} class='" . $parsed_args['class'] . "'>\n";
1805
1806 if ( $show_option_all ) {
1807 $output .= "\t<option value='0'>$show_option_all</option>\n";
1808 }
1809
1810 if ( $show_option_none ) {
1811 $_selected = selected( $option_none_value, $parsed_args['selected'], false );
1812 $output .= "\t<option value='" . esc_attr( $option_none_value ) . "'$_selected>$show_option_none</option>\n";
1813 }
1814
1815 if ( $parsed_args['include_selected'] && ( $parsed_args['selected'] > 0 ) ) {
1816 $found_selected = false;
1817 $parsed_args['selected'] = (int) $parsed_args['selected'];
1818
1819 foreach ( (array) $users as $user ) {
1820 $user->ID = (int) $user->ID;
1821 if ( $user->ID === $parsed_args['selected'] ) {
1822 $found_selected = true;
1823 }
1824 }
1825
1826 if ( ! $found_selected ) {
1827 $selected_user = get_userdata( $parsed_args['selected'] );
1828 if ( $selected_user ) {
1829 $users[] = $selected_user;
1830 }
1831 }
1832 }
1833
1834 foreach ( (array) $users as $user ) {
1835 if ( 'display_name_with_login' === $show ) {
1836 /* translators: 1: User's display name, 2: User login. */
1837 $display = sprintf( _x( '%1$s (%2$s)', 'user dropdown' ), $user->display_name, $user->user_login );
1838 } elseif ( ! empty( $user->$show ) ) {
1839 $display = $user->$show;
1840 } else {
1841 $display = '(' . $user->user_login . ')';
1842 }
1843
1844 $_selected = selected( $user->ID, $parsed_args['selected'], false );
1845 $output .= "\t<option value='$user->ID'$_selected>" . esc_html( $display ) . "</option>\n";
1846 }
1847
1848 $output .= '</select>';
1849 }
1850
1851 /**
1852 * Filters the wp_dropdown_users() HTML output.
1853 *
1854 * @since 2.3.0
1855 *
1856 * @param string $output HTML output generated by wp_dropdown_users().
1857 */
1858 $html = apply_filters( 'wp_dropdown_users', $output );
1859
1860 if ( $parsed_args['echo'] ) {
1861 echo $html;
1862 }
1863 return $html;
1864}
1865
1866/**
1867 * Sanitizes user field based on context.
1868 *
1869 * Possible context values are: 'raw', 'edit', 'db', 'display', 'attribute' and 'js'. The
1870 * 'display' context is used by default. 'attribute' and 'js' contexts are treated like 'display'
1871 * when calling filters.
1872 *
1873 * @since 2.3.0
1874 *
1875 * @param string $field The user Object field name.
1876 * @param mixed $value The user Object value.
1877 * @param int $user_id User ID.
1878 * @param string $context How to sanitize user fields. Looks for 'raw', 'edit', 'db', 'display',
1879 * 'attribute' and 'js'.
1880 * @return mixed Sanitized value.
1881 */
1882function sanitize_user_field( $field, $value, $user_id, $context ) {
1883 $int_fields = array( 'ID' );
1884 if ( in_array( $field, $int_fields, true ) ) {
1885 $value = (int) $value;
1886 }
1887
1888 if ( 'raw' === $context ) {
1889 return $value;
1890 }
1891
1892 if ( ! is_string( $value ) && ! is_numeric( $value ) ) {
1893 return $value;
1894 }
1895
1896 $prefixed = str_contains( $field, 'user_' );
1897
1898 if ( 'edit' === $context ) {
1899 if ( $prefixed ) {
1900
1901 /** This filter is documented in wp-includes/post.php */
1902 $value = apply_filters( "edit_{$field}", $value, $user_id );
1903 } else {
1904
1905 /**
1906 * Filters a user field value in the 'edit' context.
1907 *
1908 * The dynamic portion of the hook name, `$field`, refers to the prefixed user
1909 * field being filtered, such as 'user_login', 'user_email', 'first_name', etc.
1910 *
1911 * @since 2.9.0
1912 *
1913 * @param mixed $value Value of the prefixed user field.
1914 * @param int $user_id User ID.
1915 */
1916 $value = apply_filters( "edit_user_{$field}", $value, $user_id );
1917 }
1918
1919 if ( 'description' === $field ) {
1920 $value = esc_html( $value ); // textarea_escaped?
1921 } else {
1922 $value = esc_attr( $value );
1923 }
1924 } elseif ( 'db' === $context ) {
1925 if ( $prefixed ) {
1926 /** This filter is documented in wp-includes/post.php */
1927 $value = apply_filters( "pre_{$field}", $value );
1928 } else {
1929
1930 /**
1931 * Filters the value of a user field in the 'db' context.
1932 *
1933 * The dynamic portion of the hook name, `$field`, refers to the prefixed user
1934 * field being filtered, such as 'user_login', 'user_email', 'first_name', etc.
1935 *
1936 * @since 2.9.0
1937 *
1938 * @param mixed $value Value of the prefixed user field.
1939 */
1940 $value = apply_filters( "pre_user_{$field}", $value );
1941 }
1942 } else {
1943 // Use display filters by default.
1944 if ( $prefixed ) {
1945
1946 /** This filter is documented in wp-includes/post.php */
1947 $value = apply_filters( "{$field}", $value, $user_id, $context );
1948 } else {
1949
1950 /**
1951 * Filters the value of a user field in a standard context.
1952 *
1953 * The dynamic portion of the hook name, `$field`, refers to the prefixed user
1954 * field being filtered, such as 'user_login', 'user_email', 'first_name', etc.
1955 *
1956 * @since 2.9.0
1957 *
1958 * @param mixed $value The user object value to sanitize.
1959 * @param int $user_id User ID.
1960 * @param string $context The context to filter within.
1961 */
1962 $value = apply_filters( "user_{$field}", $value, $user_id, $context );
1963 }
1964 }
1965
1966 if ( 'user_url' === $field ) {
1967 $value = esc_url( $value );
1968 }
1969
1970 if ( 'attribute' === $context ) {
1971 $value = esc_attr( $value );
1972 } elseif ( 'js' === $context ) {
1973 $value = esc_js( $value );
1974 }
1975
1976 // Restore the type for integer fields after esc_attr().
1977 if ( in_array( $field, $int_fields, true ) ) {
1978 $value = (int) $value;
1979 }
1980
1981 return $value;
1982}
1983
1984/**
1985 * Updates all user caches.
1986 *
1987 * @since 3.0.0
1988 *
1989 * @param object|WP_User $user User object or database row to be cached
1990 * @return void|false Void on success, false on failure.
1991 */
1992function update_user_caches( $user ) {
1993 if ( $user instanceof WP_User ) {
1994 if ( ! $user->exists() ) {
1995 return false;
1996 }
1997
1998 $user = $user->data;
1999 }
2000
2001 wp_cache_add( $user->ID, $user, 'users' );
2002 wp_cache_add( $user->user_login, $user->ID, 'userlogins' );
2003 wp_cache_add( $user->user_nicename, $user->ID, 'userslugs' );
2004
2005 if ( ! empty( $user->user_email ) ) {
2006 wp_cache_add( $user->user_email, $user->ID, 'useremail' );
2007 }
2008}
2009
2010/**
2011 * Cleans all user caches.
2012 *
2013 * @since 3.0.0
2014 * @since 4.4.0 'clean_user_cache' action was added.
2015 * @since 6.2.0 User metadata caches are now cleared.
2016 *
2017 * @param WP_User|int $user User object or ID to be cleaned from the cache
2018 */
2019function clean_user_cache( $user ) {
2020 if ( is_numeric( $user ) ) {
2021 $user = new WP_User( $user );
2022 }
2023
2024 if ( ! $user->exists() ) {
2025 return;
2026 }
2027
2028 wp_cache_delete( $user->ID, 'users' );
2029 wp_cache_delete( $user->user_login, 'userlogins' );
2030 wp_cache_delete( $user->user_nicename, 'userslugs' );
2031
2032 if ( ! empty( $user->user_email ) ) {
2033 wp_cache_delete( $user->user_email, 'useremail' );
2034 }
2035
2036 wp_cache_delete( $user->ID, 'user_meta' );
2037 wp_cache_set_users_last_changed();
2038
2039 /**
2040 * Fires immediately after the given user's cache is cleaned.
2041 *
2042 * @since 4.4.0
2043 *
2044 * @param int $user_id User ID.
2045 * @param WP_User $user User object.
2046 */
2047 do_action( 'clean_user_cache', $user->ID, $user );
2048}
2049
2050/**
2051 * Determines whether the given username exists.
2052 *
2053 * For more information on this and similar theme functions, check out
2054 * the {@link https://developer.wordpress.org/themes/basics/conditional-tags/
2055 * Conditional Tags} article in the Theme Developer Handbook.
2056 *
2057 * @since 2.0.0
2058 *
2059 * @param string $username The username to check for existence.
2060 * @return int|false The user ID on success, false on failure.
2061 */
2062function username_exists( $username ) {
2063 $user = get_user_by( 'login', $username );
2064 if ( $user ) {
2065 $user_id = $user->ID;
2066 } else {
2067 $user_id = false;
2068 }
2069
2070 /**
2071 * Filters whether the given username exists.
2072 *
2073 * @since 4.9.0
2074 *
2075 * @param int|false $user_id The user ID associated with the username,
2076 * or false if the username does not exist.
2077 * @param string $username The username to check for existence.
2078 */
2079 return apply_filters( 'username_exists', $user_id, $username );
2080}
2081
2082/**
2083 * Determines whether the given email exists.
2084 *
2085 * For more information on this and similar theme functions, check out
2086 * the {@link https://developer.wordpress.org/themes/basics/conditional-tags/
2087 * Conditional Tags} article in the Theme Developer Handbook.
2088 *
2089 * @since 2.1.0
2090 *
2091 * @param string $email The email to check for existence.
2092 * @return int|false The user ID on success, false on failure.
2093 */
2094function email_exists( $email ) {
2095 $user = get_user_by( 'email', $email );
2096 if ( $user ) {
2097 $user_id = $user->ID;
2098 } else {
2099 $user_id = false;
2100 }
2101
2102 /**
2103 * Filters whether the given email exists.
2104 *
2105 * @since 5.6.0
2106 *
2107 * @param int|false $user_id The user ID associated with the email,
2108 * or false if the email does not exist.
2109 * @param string $email The email to check for existence.
2110 */
2111 return apply_filters( 'email_exists', $user_id, $email );
2112}
2113
2114/**
2115 * Checks whether a username is valid.
2116 *
2117 * @since 2.0.1
2118 * @since 4.4.0 Empty sanitized usernames are now considered invalid.
2119 *
2120 * @param string $username Username.
2121 * @return bool Whether username given is valid.
2122 */
2123function validate_username( $username ) {
2124 $sanitized = sanitize_user( $username, true );
2125 $valid = ( $sanitized === $username && ! empty( $sanitized ) );
2126
2127 /**
2128 * Filters whether the provided username is valid.
2129 *
2130 * @since 2.0.1
2131 *
2132 * @param bool $valid Whether given username is valid.
2133 * @param string $username Username to check.
2134 */
2135 return apply_filters( 'validate_username', $valid, $username );
2136}
2137
2138/**
2139 * Inserts a user into the database.
2140 *
2141 * Most of the `$userdata` array fields have filters associated with the values. Exceptions are
2142 * 'ID', 'rich_editing', 'syntax_highlighting', 'comment_shortcuts', 'admin_color', 'use_ssl',
2143 * 'user_registered', 'user_activation_key', 'spam', and 'role'. The filters have the prefix
2144 * 'pre_user_' followed by the field name. An example using 'description' would have the filter
2145 * called 'pre_user_description' that can be hooked into.
2146 *
2147 * @since 2.0.0
2148 * @since 3.6.0 The `aim`, `jabber`, and `yim` fields were removed as default user contact
2149 * methods for new installations. See wp_get_user_contact_methods().
2150 * @since 4.7.0 The `locale` field can be passed to `$userdata`.
2151 * @since 5.3.0 The `user_activation_key` field can be passed to `$userdata`.
2152 * @since 5.3.0 The `spam` field can be passed to `$userdata` (Multisite only).
2153 * @since 5.9.0 The `meta_input` field can be passed to `$userdata` to allow addition of user meta data.
2154 *
2155 * @global wpdb $wpdb WordPress database abstraction object.
2156 *
2157 * @param array|object|WP_User $userdata {
2158 * An array, object, or WP_User object of user data arguments.
2159 *
2160 * @type int $ID User ID. If supplied, the user will be updated.
2161 * @type string $user_pass The plain-text user password for new users.
2162 * Hashed password for existing users.
2163 * @type string $user_login The user's login username.
2164 * @type string $user_nicename The URL-friendly user name.
2165 * @type string $user_url The user URL.
2166 * @type string $user_email The user email address.
2167 * @type string $display_name The user's display name.
2168 * Default is the user's username.
2169 * @type string $nickname The user's nickname.
2170 * Default is the user's username.
2171 * @type string $first_name The user's first name. For new users, will be used
2172 * to build the first part of the user's display name
2173 * if `$display_name` is not specified.
2174 * @type string $last_name The user's last name. For new users, will be used
2175 * to build the second part of the user's display name
2176 * if `$display_name` is not specified.
2177 * @type string $description The user's biographical description.
2178 * @type string $rich_editing Whether to enable the rich-editor for the user.
2179 * Accepts 'true' or 'false' as a string literal,
2180 * not boolean. Default 'true'.
2181 * @type string $syntax_highlighting Whether to enable the rich code editor for the user.
2182 * Accepts 'true' or 'false' as a string literal,
2183 * not boolean. Default 'true'.
2184 * @type string $comment_shortcuts Whether to enable comment moderation keyboard
2185 * shortcuts for the user. Accepts 'true' or 'false'
2186 * as a string literal, not boolean. Default 'false'.
2187 * @type string $admin_color Admin color scheme for the user. Default 'fresh'.
2188 * @type bool $use_ssl Whether the user should always access the admin over
2189 * https. Default false.
2190 * @type string $user_registered Date the user registered in UTC. Format is 'Y-m-d H:i:s'.
2191 * @type string $user_activation_key Password reset key. Default empty.
2192 * @type bool $spam Multisite only. Whether the user is marked as spam.
2193 * Default false.
2194 * @type string $show_admin_bar_front Whether to display the Admin Bar for the user
2195 * on the site's front end. Accepts 'true' or 'false'
2196 * as a string literal, not boolean. Default 'true'.
2197 * @type string $role User's role.
2198 * @type string $locale User's locale. Default empty.
2199 * @type array $meta_input Array of custom user meta values keyed by meta key.
2200 * Default empty.
2201 * }
2202 * @return int|WP_Error The newly created user's ID or a WP_Error object if the user could not
2203 * be created.
2204 */
2205function wp_insert_user( $userdata ) {
2206 global $wpdb;
2207
2208 if ( $userdata instanceof stdClass ) {
2209 $userdata = get_object_vars( $userdata );
2210 } elseif ( $userdata instanceof WP_User ) {
2211 $userdata = $userdata->to_array();
2212 }
2213
2214 // Are we updating or creating?
2215 if ( ! empty( $userdata['ID'] ) ) {
2216 $user_id = (int) $userdata['ID'];
2217 $update = true;
2218 $old_user_data = get_userdata( $user_id );
2219
2220 if ( ! $old_user_data ) {
2221 return new WP_Error( 'invalid_user_id', __( 'Invalid user ID.' ) );
2222 }
2223
2224 // Slash current user email to compare it later with slashed new user email.
2225 $old_user_data->user_email = wp_slash( $old_user_data->user_email );
2226
2227 // Hashed in wp_update_user(), plaintext if called directly.
2228 $user_pass = ! empty( $userdata['user_pass'] ) ? $userdata['user_pass'] : $old_user_data->user_pass;
2229 } else {
2230 $update = false;
2231
2232 if ( empty( $userdata['user_pass'] ) ) {
2233 wp_trigger_error(
2234 __FUNCTION__,
2235 __( 'The user_pass field is required when creating a new user. The user will need to reset their password before logging in.' ),
2236 E_USER_WARNING
2237 );
2238
2239 // Set the password as an empty string to force the password reset flow.
2240 $userdata['user_pass'] = '';
2241 }
2242
2243 // Hash the password.
2244 $user_pass = wp_hash_password( $userdata['user_pass'] );
2245 }
2246
2247 $sanitized_user_login = sanitize_user( $userdata['user_login'], true );
2248
2249 /**
2250 * Filters a username after it has been sanitized.
2251 *
2252 * This filter is called before the user is created or updated.
2253 *
2254 * @since 2.0.3
2255 *
2256 * @param string $sanitized_user_login Username after it has been sanitized.
2257 */
2258 $pre_user_login = apply_filters( 'pre_user_login', $sanitized_user_login );
2259
2260 // Remove any non-printable chars from the login string to see if we have ended up with an empty username.
2261 $user_login = trim( $pre_user_login );
2262
2263 // user_login must be between 0 and 60 characters.
2264 if ( empty( $user_login ) ) {
2265 return new WP_Error( 'empty_user_login', __( 'Cannot create a user with an empty login name.' ) );
2266 } elseif ( mb_strlen( $user_login ) > 60 ) {
2267 return new WP_Error( 'user_login_too_long', __( 'Username may not be longer than 60 characters.' ) );
2268 }
2269
2270 if ( ! $update && username_exists( $user_login ) ) {
2271 return new WP_Error( 'existing_user_login', __( 'Sorry, that username already exists!' ) );
2272 }
2273
2274 /**
2275 * Filters the list of disallowed usernames.
2276 *
2277 * @since 4.4.0
2278 *
2279 * @param array $usernames Array of disallowed usernames.
2280 */
2281 $illegal_logins = (array) apply_filters( 'illegal_user_logins', array() );
2282
2283 if ( in_array( strtolower( $user_login ), array_map( 'strtolower', $illegal_logins ), true ) ) {
2284 return new WP_Error( 'invalid_username', __( 'Sorry, that username is not allowed.' ) );
2285 }
2286
2287 /*
2288 * If a nicename is provided, remove unsafe user characters before using it.
2289 * Otherwise build a nicename from the user_login.
2290 */
2291 if ( ! empty( $userdata['user_nicename'] ) ) {
2292 $user_nicename = sanitize_user( $userdata['user_nicename'], true );
2293 } else {
2294 $user_nicename = mb_substr( $user_login, 0, 50 );
2295 }
2296
2297 $user_nicename = sanitize_title( $user_nicename );
2298
2299 /**
2300 * Filters a user's nicename before the user is created or updated.
2301 *
2302 * @since 2.0.3
2303 *
2304 * @param string $user_nicename The user's nicename.
2305 */
2306 $user_nicename = apply_filters( 'pre_user_nicename', $user_nicename );
2307
2308 // Check if the sanitized nicename is empty.
2309 if ( empty( $user_nicename ) ) {
2310 return new WP_Error( 'empty_user_nicename', __( 'Cannot create a user with an empty nicename.' ) );
2311 } elseif ( mb_strlen( $user_nicename ) > 50 ) {
2312 return new WP_Error( 'user_nicename_too_long', __( 'Nicename may not be longer than 50 characters.' ) );
2313 }
2314
2315 $user_nicename_check = $wpdb->get_var( $wpdb->prepare( "SELECT ID FROM $wpdb->users WHERE user_nicename = %s AND user_login != %s LIMIT 1", $user_nicename, $user_login ) );
2316
2317 if ( $user_nicename_check ) {
2318 $suffix = 2;
2319 while ( $user_nicename_check ) {
2320 // user_nicename allows 50 chars. Subtract one for a hyphen, plus the length of the suffix.
2321 $base_length = 49 - mb_strlen( $suffix );
2322 $alt_user_nicename = mb_substr( $user_nicename, 0, $base_length ) . "-$suffix";
2323 $user_nicename_check = $wpdb->get_var( $wpdb->prepare( "SELECT ID FROM $wpdb->users WHERE user_nicename = %s AND user_login != %s LIMIT 1", $alt_user_nicename, $user_login ) );
2324 ++$suffix;
2325 }
2326 $user_nicename = $alt_user_nicename;
2327 }
2328
2329 $raw_user_email = empty( $userdata['user_email'] ) ? '' : $userdata['user_email'];
2330
2331 /**
2332 * Filters a user's email before the user is created or updated.
2333 *
2334 * @since 2.0.3
2335 *
2336 * @param string $raw_user_email The user's email.
2337 */
2338 $user_email = apply_filters( 'pre_user_email', $raw_user_email );
2339
2340 /*
2341 * If there is no update, just check for `email_exists`. If there is an update,
2342 * check if current email and new email are the same, and check `email_exists`
2343 * accordingly.
2344 */
2345 if ( ( ! $update || ( ! empty( $old_user_data ) && 0 !== strcasecmp( $user_email, $old_user_data->user_email ) ) )
2346 && ! defined( 'WP_IMPORTING' )
2347 && email_exists( $user_email )
2348 ) {
2349 return new WP_Error( 'existing_user_email', __( 'Sorry, that email address is already used!' ) );
2350 }
2351
2352 $raw_user_url = empty( $userdata['user_url'] ) ? '' : $userdata['user_url'];
2353
2354 /**
2355 * Filters a user's URL before the user is created or updated.
2356 *
2357 * @since 2.0.3
2358 *
2359 * @param string $raw_user_url The user's URL.
2360 */
2361 $user_url = apply_filters( 'pre_user_url', $raw_user_url );
2362
2363 if ( mb_strlen( $user_url ) > 100 ) {
2364 return new WP_Error( 'user_url_too_long', __( 'User URL may not be longer than 100 characters.' ) );
2365 }
2366
2367 $user_registered = empty( $userdata['user_registered'] ) ? gmdate( 'Y-m-d H:i:s' ) : $userdata['user_registered'];
2368
2369 $user_activation_key = empty( $userdata['user_activation_key'] ) ? '' : $userdata['user_activation_key'];
2370
2371 if ( ! empty( $userdata['spam'] ) && ! is_multisite() ) {
2372 return new WP_Error( 'no_spam', __( 'Sorry, marking a user as spam is only supported on Multisite.' ) );
2373 }
2374
2375 $spam = empty( $userdata['spam'] ) ? 0 : (bool) $userdata['spam'];
2376
2377 // Store values to save in user meta.
2378 $meta = array();
2379
2380 $nickname = empty( $userdata['nickname'] ) ? $user_login : $userdata['nickname'];
2381
2382 /**
2383 * Filters a user's nickname before the user is created or updated.
2384 *
2385 * @since 2.0.3
2386 *
2387 * @param string $nickname The user's nickname.
2388 */
2389 $meta['nickname'] = apply_filters( 'pre_user_nickname', $nickname );
2390
2391 $first_name = empty( $userdata['first_name'] ) ? '' : $userdata['first_name'];
2392
2393 /**
2394 * Filters a user's first name before the user is created or updated.
2395 *
2396 * @since 2.0.3
2397 *
2398 * @param string $first_name The user's first name.
2399 */
2400 $meta['first_name'] = apply_filters( 'pre_user_first_name', $first_name );
2401
2402 $last_name = empty( $userdata['last_name'] ) ? '' : $userdata['last_name'];
2403
2404 /**
2405 * Filters a user's last name before the user is created or updated.
2406 *
2407 * @since 2.0.3
2408 *
2409 * @param string $last_name The user's last name.
2410 */
2411 $meta['last_name'] = apply_filters( 'pre_user_last_name', $last_name );
2412
2413 if ( empty( $userdata['display_name'] ) ) {
2414 if ( $update ) {
2415 $display_name = $user_login;
2416 } elseif ( $meta['first_name'] && $meta['last_name'] ) {
2417 $display_name = sprintf(
2418 /* translators: 1: User's first name, 2: Last name. */
2419 _x( '%1$s %2$s', 'Display name based on first name and last name' ),
2420 $meta['first_name'],
2421 $meta['last_name']
2422 );
2423 } elseif ( $meta['first_name'] ) {
2424 $display_name = $meta['first_name'];
2425 } elseif ( $meta['last_name'] ) {
2426 $display_name = $meta['last_name'];
2427 } else {
2428 $display_name = $user_login;
2429 }
2430 } else {
2431 $display_name = $userdata['display_name'];
2432 }
2433
2434 /**
2435 * Filters a user's display name before the user is created or updated.
2436 *
2437 * @since 2.0.3
2438 *
2439 * @param string $display_name The user's display name.
2440 */
2441 $display_name = apply_filters( 'pre_user_display_name', $display_name );
2442
2443 $description = empty( $userdata['description'] ) ? '' : $userdata['description'];
2444
2445 /**
2446 * Filters a user's description before the user is created or updated.
2447 *
2448 * @since 2.0.3
2449 *
2450 * @param string $description The user's description.
2451 */
2452 $meta['description'] = apply_filters( 'pre_user_description', $description );
2453
2454 $meta['rich_editing'] = empty( $userdata['rich_editing'] ) ? 'true' : $userdata['rich_editing'];
2455
2456 $meta['syntax_highlighting'] = empty( $userdata['syntax_highlighting'] ) ? 'true' : $userdata['syntax_highlighting'];
2457
2458 $meta['comment_shortcuts'] = empty( $userdata['comment_shortcuts'] ) || 'false' === $userdata['comment_shortcuts'] ? 'false' : 'true';
2459
2460 $admin_color = empty( $userdata['admin_color'] ) ? 'fresh' : $userdata['admin_color'];
2461 $meta['admin_color'] = preg_replace( '|[^a-z0-9 _.\-@]|i', '', $admin_color );
2462
2463 $meta['use_ssl'] = empty( $userdata['use_ssl'] ) ? '0' : '1';
2464
2465 $meta['show_admin_bar_front'] = empty( $userdata['show_admin_bar_front'] ) ? 'true' : $userdata['show_admin_bar_front'];
2466
2467 $meta['locale'] = isset( $userdata['locale'] ) ? $userdata['locale'] : '';
2468
2469 $compacted = compact( 'user_pass', 'user_nicename', 'user_email', 'user_url', 'user_registered', 'user_activation_key', 'display_name' );
2470 $data = wp_unslash( $compacted );
2471
2472 if ( ! $update ) {
2473 $data = $data + compact( 'user_login' );
2474 }
2475
2476 if ( is_multisite() ) {
2477 $data = $data + compact( 'spam' );
2478 }
2479
2480 /**
2481 * Filters user data before the record is created or updated.
2482 *
2483 * It only includes data in the users table, not any user metadata.
2484 *
2485 * @since 4.9.0
2486 * @since 5.8.0 The `$userdata` parameter was added.
2487 * @since 6.8.0 The user's password is now hashed using bcrypt by default instead of phpass.
2488 *
2489 * @param array $data {
2490 * Values and keys for the user.
2491 *
2492 * @type string $user_login The user's login. Only included if $update == false
2493 * @type string $user_pass The user's password.
2494 * @type string $user_email The user's email.
2495 * @type string $user_url The user's url.
2496 * @type string $user_nicename The user's nice name. Defaults to a URL-safe version of user's login.
2497 * @type string $display_name The user's display name.
2498 * @type string $user_registered MySQL timestamp describing the moment when the user registered. Defaults to
2499 * the current UTC timestamp.
2500 * }
2501 * @param bool $update Whether the user is being updated rather than created.
2502 * @param int|null $user_id ID of the user to be updated, or NULL if the user is being created.
2503 * @param array $userdata The raw array of data passed to wp_insert_user().
2504 */
2505 $data = apply_filters( 'wp_pre_insert_user_data', $data, $update, ( $update ? $user_id : null ), $userdata );
2506
2507 if ( empty( $data ) || ! is_array( $data ) ) {
2508 return new WP_Error( 'empty_data', __( 'Not enough data to create this user.' ) );
2509 }
2510
2511 if ( $update ) {
2512 if ( $user_email !== $old_user_data->user_email || $user_pass !== $old_user_data->user_pass ) {
2513 $data['user_activation_key'] = '';
2514 }
2515 $wpdb->update( $wpdb->users, $data, array( 'ID' => $user_id ) );
2516 } else {
2517 $wpdb->insert( $wpdb->users, $data );
2518 $user_id = (int) $wpdb->insert_id;
2519 }
2520
2521 $user = new WP_User( $user_id );
2522
2523 if ( ! $update ) {
2524 /** This action is documented in wp-includes/pluggable.php */
2525 do_action( 'wp_set_password', $userdata['user_pass'], $user_id, $user );
2526 }
2527
2528 /**
2529 * Filters a user's meta values and keys immediately after the user is created or updated
2530 * and before any user meta is inserted or updated.
2531 *
2532 * Does not include contact methods. These are added using `wp_get_user_contact_methods( $user )`.
2533 *
2534 * For custom meta fields, see the {@see 'insert_custom_user_meta'} filter.
2535 *
2536 * @since 4.4.0
2537 * @since 5.8.0 The `$userdata` parameter was added.
2538 *
2539 * @param array $meta {
2540 * Default meta values and keys for the user.
2541 *
2542 * @type string $nickname The user's nickname. Default is the user's username.
2543 * @type string $first_name The user's first name.
2544 * @type string $last_name The user's last name.
2545 * @type string $description The user's description.
2546 * @type string $rich_editing Whether to enable the rich-editor for the user. Default 'true'.
2547 * @type string $syntax_highlighting Whether to enable the rich code editor for the user. Default 'true'.
2548 * @type string $comment_shortcuts Whether to enable keyboard shortcuts for the user. Default 'false'.
2549 * @type string $admin_color The color scheme for a user's admin screen. Default 'fresh'.
2550 * @type int|bool $use_ssl Whether to force SSL on the user's admin area. 0|false if SSL
2551 * is not forced.
2552 * @type string $show_admin_bar_front Whether to show the admin bar on the front end for the user.
2553 * Default 'true'.
2554 * @type string $locale User's locale. Default empty.
2555 * }
2556 * @param WP_User $user User object.
2557 * @param bool $update Whether the user is being updated rather than created.
2558 * @param array $userdata The raw array of data passed to wp_insert_user().
2559 */
2560 $meta = apply_filters( 'insert_user_meta', $meta, $user, $update, $userdata );
2561
2562 $custom_meta = array();
2563 if ( array_key_exists( 'meta_input', $userdata ) && is_array( $userdata['meta_input'] ) && ! empty( $userdata['meta_input'] ) ) {
2564 $custom_meta = $userdata['meta_input'];
2565 }
2566
2567 /**
2568 * Filters a user's custom meta values and keys immediately after the user is created or updated
2569 * and before any user meta is inserted or updated.
2570 *
2571 * For non-custom meta fields, see the {@see 'insert_user_meta'} filter.
2572 *
2573 * @since 5.9.0
2574 *
2575 * @param array $custom_meta Array of custom user meta values keyed by meta key.
2576 * @param WP_User $user User object.
2577 * @param bool $update Whether the user is being updated rather than created.
2578 * @param array $userdata The raw array of data passed to wp_insert_user().
2579 */
2580 $custom_meta = apply_filters( 'insert_custom_user_meta', $custom_meta, $user, $update, $userdata );
2581
2582 $meta = array_merge( $meta, $custom_meta );
2583
2584 if ( $update ) {
2585 // Update user meta.
2586 foreach ( $meta as $key => $value ) {
2587 update_user_meta( $user_id, $key, $value );
2588 }
2589 } else {
2590 // Add user meta.
2591 foreach ( $meta as $key => $value ) {
2592 add_user_meta( $user_id, $key, $value );
2593 }
2594 }
2595
2596 foreach ( wp_get_user_contact_methods( $user ) as $key => $value ) {
2597 if ( isset( $userdata[ $key ] ) ) {
2598 update_user_meta( $user_id, $key, $userdata[ $key ] );
2599 }
2600 }
2601
2602 if ( isset( $userdata['role'] ) ) {
2603 $user->set_role( $userdata['role'] );
2604 } elseif ( ! $update ) {
2605 $user->set_role( get_option( 'default_role' ) );
2606 }
2607
2608 clean_user_cache( $user_id );
2609
2610 if ( $update ) {
2611 /**
2612 * Fires immediately after an existing user is updated.
2613 *
2614 * @since 2.0.0
2615 * @since 5.8.0 The `$userdata` parameter was added.
2616 *
2617 * @param int $user_id User ID.
2618 * @param WP_User $old_user_data Object containing user's data prior to update.
2619 * @param array $userdata The raw array of data passed to wp_insert_user().
2620 */
2621 do_action( 'profile_update', $user_id, $old_user_data, $userdata );
2622
2623 if ( isset( $userdata['spam'] ) && $userdata['spam'] !== $old_user_data->spam ) {
2624 if ( '1' === $userdata['spam'] ) {
2625 /**
2626 * Fires after the user is marked as a SPAM user.
2627 *
2628 * @since 3.0.0
2629 *
2630 * @param int $user_id ID of the user marked as SPAM.
2631 */
2632 do_action( 'make_spam_user', $user_id );
2633 } else {
2634 /**
2635 * Fires after the user is marked as a HAM user. Opposite of SPAM.
2636 *
2637 * @since 3.0.0
2638 *
2639 * @param int $user_id ID of the user marked as HAM.
2640 */
2641 do_action( 'make_ham_user', $user_id );
2642 }
2643 }
2644 } else {
2645 /**
2646 * Fires immediately after a new user is registered.
2647 *
2648 * @since 1.5.0
2649 * @since 5.8.0 The `$userdata` parameter was added.
2650 *
2651 * @param int $user_id User ID.
2652 * @param array $userdata The raw array of data passed to wp_insert_user().
2653 */
2654 do_action( 'user_register', $user_id, $userdata );
2655 }
2656
2657 return $user_id;
2658}
2659
2660/**
2661 * Updates a user in the database.
2662 *
2663 * It is possible to update a user's password by specifying the 'user_pass'
2664 * value in the $userdata parameter array.
2665 *
2666 * If current user's password is being updated, then the cookies will be
2667 * cleared.
2668 *
2669 * @since 2.0.0
2670 *
2671 * @see wp_insert_user() For what fields can be set in $userdata.
2672 *
2673 * @param array|object|WP_User $userdata An array of user data or a user object of type stdClass or WP_User.
2674 * @return int|WP_Error The updated user's ID or a WP_Error object if the user could not be updated.
2675 */
2676function wp_update_user( $userdata ) {
2677 if ( $userdata instanceof stdClass ) {
2678 $userdata = get_object_vars( $userdata );
2679 } elseif ( $userdata instanceof WP_User ) {
2680 $userdata = $userdata->to_array();
2681 }
2682
2683 $userdata_raw = $userdata;
2684
2685 $user_id = isset( $userdata['ID'] ) ? (int) $userdata['ID'] : 0;
2686 if ( ! $user_id ) {
2687 return new WP_Error( 'invalid_user_id', __( 'Invalid user ID.' ) );
2688 }
2689
2690 // First, get all of the original fields.
2691 $user_obj = get_userdata( $user_id );
2692 if ( ! $user_obj ) {
2693 return new WP_Error( 'invalid_user_id', __( 'Invalid user ID.' ) );
2694 }
2695
2696 $user = $user_obj->to_array();
2697
2698 // Add additional custom fields.
2699 foreach ( _get_additional_user_keys( $user_obj ) as $key ) {
2700 $user[ $key ] = get_user_meta( $user_id, $key, true );
2701 }
2702
2703 // Escape data pulled from DB.
2704 $user = add_magic_quotes( $user );
2705
2706 if ( ! empty( $userdata['user_pass'] ) && $userdata['user_pass'] !== $user_obj->user_pass ) {
2707 // If password is changing, hash it now.
2708 $plaintext_pass = $userdata['user_pass'];
2709 $userdata['user_pass'] = wp_hash_password( $userdata['user_pass'] );
2710
2711 /** This action is documented in wp-includes/pluggable.php */
2712 do_action( 'wp_set_password', $plaintext_pass, $user_id, $user_obj );
2713
2714 /**
2715 * Filters whether to send the password change email.
2716 *
2717 * @since 4.3.0
2718 *
2719 * @see wp_insert_user() For `$user` and `$userdata` fields.
2720 *
2721 * @param bool $send Whether to send the email.
2722 * @param array $user The original user array.
2723 * @param array $userdata The updated user array.
2724 */
2725 $send_password_change_email = apply_filters( 'send_password_change_email', true, $user, $userdata );
2726 }
2727
2728 if ( isset( $userdata['user_email'] ) && $user['user_email'] !== $userdata['user_email'] ) {
2729 /**
2730 * Filters whether to send the email change email.
2731 *
2732 * @since 4.3.0
2733 *
2734 * @see wp_insert_user() For `$user` and `$userdata` fields.
2735 *
2736 * @param bool $send Whether to send the email.
2737 * @param array $user The original user array.
2738 * @param array $userdata The updated user array.
2739 */
2740 $send_email_change_email = apply_filters( 'send_email_change_email', true, $user, $userdata );
2741 }
2742
2743 clean_user_cache( $user_obj );
2744
2745 // Merge old and new fields with new fields overwriting old ones.
2746 $userdata = array_merge( $user, $userdata );
2747 $user_id = wp_insert_user( $userdata );
2748
2749 if ( is_wp_error( $user_id ) ) {
2750 return $user_id;
2751 }
2752
2753 $blog_name = wp_specialchars_decode( get_option( 'blogname' ), ENT_QUOTES );
2754
2755 $switched_locale = false;
2756 if ( ! empty( $send_password_change_email ) || ! empty( $send_email_change_email ) ) {
2757 $switched_locale = switch_to_user_locale( $user_id );
2758 }
2759
2760 if ( ! empty( $send_password_change_email ) ) {
2761 /* translators: Do not translate USERNAME, ADMIN_EMAIL, EMAIL, SITENAME, SITEURL: those are placeholders. */
2762 $pass_change_text = __(
2763 'Hi ###USERNAME###,
2764
2765This notice confirms that your password was changed on ###SITENAME###.
2766
2767If you did not change your password, please contact the Site Administrator at
2768###ADMIN_EMAIL###
2769
2770This email has been sent to ###EMAIL###
2771
2772Regards,
2773All at ###SITENAME###
2774###SITEURL###'
2775 );
2776
2777 $pass_change_email = array(
2778 'to' => $user['user_email'],
2779 /* translators: Password change notification email subject. %s: Site title. */
2780 'subject' => __( '[%s] Password Changed' ),
2781 'message' => $pass_change_text,
2782 'headers' => '',
2783 );
2784
2785 /**
2786 * Filters the contents of the email sent when the user's password is changed.
2787 *
2788 * @since 4.3.0
2789 *
2790 * @param array $pass_change_email {
2791 * Used to build wp_mail().
2792 *
2793 * @type string $to The intended recipients. Add emails in a comma separated string.
2794 * @type string $subject The subject of the email.
2795 * @type string $message The content of the email.
2796 * The following strings have a special meaning and will get replaced dynamically:
2797 * - `###USERNAME###` The current user's username.
2798 * - `###ADMIN_EMAIL###` The admin email in case this was unexpected.
2799 * - `###EMAIL###` The user's email address.
2800 * - `###SITENAME###` The name of the site.
2801 * - `###SITEURL###` The URL to the site.
2802 * @type string $headers Headers. Add headers in a newline (\r\n) separated string.
2803 * }
2804 * @param array $user The original user array.
2805 * @param array $userdata The updated user array.
2806 */
2807 $pass_change_email = apply_filters( 'password_change_email', $pass_change_email, $user, $userdata );
2808
2809 $pass_change_email['message'] = str_replace( '###USERNAME###', $user['user_login'], $pass_change_email['message'] );
2810 $pass_change_email['message'] = str_replace( '###ADMIN_EMAIL###', get_option( 'admin_email' ), $pass_change_email['message'] );
2811 $pass_change_email['message'] = str_replace( '###EMAIL###', $user['user_email'], $pass_change_email['message'] );
2812 $pass_change_email['message'] = str_replace( '###SITENAME###', $blog_name, $pass_change_email['message'] );
2813 $pass_change_email['message'] = str_replace( '###SITEURL###', home_url(), $pass_change_email['message'] );
2814
2815 wp_mail( $pass_change_email['to'], sprintf( $pass_change_email['subject'], $blog_name ), $pass_change_email['message'], $pass_change_email['headers'] );
2816 }
2817
2818 if ( ! empty( $send_email_change_email ) ) {
2819 /* translators: Do not translate USERNAME, ADMIN_EMAIL, NEW_EMAIL, EMAIL, SITENAME, SITEURL: those are placeholders. */
2820 $email_change_text = __(
2821 'Hi ###USERNAME###,
2822
2823This notice confirms that your email address on ###SITENAME### was changed to ###NEW_EMAIL###.
2824
2825If you did not change your email, please contact the Site Administrator at
2826###ADMIN_EMAIL###
2827
2828This email has been sent to ###EMAIL###
2829
2830Regards,
2831All at ###SITENAME###
2832###SITEURL###'
2833 );
2834
2835 $email_change_email = array(
2836 'to' => $user['user_email'],
2837 /* translators: Email change notification email subject. %s: Site title. */
2838 'subject' => __( '[%s] Email Changed' ),
2839 'message' => $email_change_text,
2840 'headers' => '',
2841 );
2842
2843 /**
2844 * Filters the contents of the email sent when the user's email is changed.
2845 *
2846 * @since 4.3.0
2847 *
2848 * @param array $email_change_email {
2849 * Used to build wp_mail().
2850 *
2851 * @type string $to The intended recipients.
2852 * @type string $subject The subject of the email.
2853 * @type string $message The content of the email.
2854 * The following strings have a special meaning and will get replaced dynamically:
2855 * - `###USERNAME###` The current user's username.
2856 * - `###ADMIN_EMAIL###` The admin email in case this was unexpected.
2857 * - `###NEW_EMAIL###` The new email address.
2858 * - `###EMAIL###` The old email address.
2859 * - `###SITENAME###` The name of the site.
2860 * - `###SITEURL###` The URL to the site.
2861 * @type string $headers Headers.
2862 * }
2863 * @param array $user The original user array.
2864 * @param array $userdata The updated user array.
2865 */
2866 $email_change_email = apply_filters( 'email_change_email', $email_change_email, $user, $userdata );
2867
2868 $email_change_email['message'] = str_replace( '###USERNAME###', $user['user_login'], $email_change_email['message'] );
2869 $email_change_email['message'] = str_replace( '###ADMIN_EMAIL###', get_option( 'admin_email' ), $email_change_email['message'] );
2870 $email_change_email['message'] = str_replace( '###NEW_EMAIL###', $userdata['user_email'], $email_change_email['message'] );
2871 $email_change_email['message'] = str_replace( '###EMAIL###', $user['user_email'], $email_change_email['message'] );
2872 $email_change_email['message'] = str_replace( '###SITENAME###', $blog_name, $email_change_email['message'] );
2873 $email_change_email['message'] = str_replace( '###SITEURL###', home_url(), $email_change_email['message'] );
2874
2875 wp_mail( $email_change_email['to'], sprintf( $email_change_email['subject'], $blog_name ), $email_change_email['message'], $email_change_email['headers'] );
2876 }
2877
2878 if ( $switched_locale ) {
2879 restore_previous_locale();
2880 }
2881
2882 // Update the cookies if the password changed.
2883 $current_user = wp_get_current_user();
2884 if ( $current_user->ID === $user_id ) {
2885 if ( isset( $plaintext_pass ) ) {
2886 /*
2887 * Here we calculate the expiration length of the current auth cookie and compare it to the default expiration.
2888 * If it's greater than this, then we know the user checked 'Remember Me' when they logged in.
2889 */
2890 $logged_in_cookie = wp_parse_auth_cookie( '', 'logged_in' );
2891 /** This filter is documented in wp-includes/pluggable.php */
2892 $default_cookie_life = apply_filters( 'auth_cookie_expiration', ( 2 * DAY_IN_SECONDS ), $user_id, false );
2893
2894 wp_clear_auth_cookie();
2895
2896 $remember = false;
2897 $token = '';
2898
2899 if ( false !== $logged_in_cookie ) {
2900 $token = $logged_in_cookie['token'];
2901 }
2902
2903 if ( false !== $logged_in_cookie && ( (int) $logged_in_cookie['expiration'] - time() ) > $default_cookie_life ) {
2904 $remember = true;
2905 }
2906
2907 wp_set_auth_cookie( $user_id, $remember, '', $token );
2908 }
2909 }
2910
2911 /**
2912 * Fires after the user has been updated and emails have been sent.
2913 *
2914 * @since 6.3.0
2915 *
2916 * @param int $user_id The ID of the user that was just updated.
2917 * @param array $userdata The array of user data that was updated.
2918 * @param array $userdata_raw The unedited array of user data that was updated.
2919 */
2920 do_action( 'wp_update_user', $user_id, $userdata, $userdata_raw );
2921
2922 return $user_id;
2923}
2924
2925/**
2926 * Provides a simpler way of inserting a user into the database.
2927 *
2928 * Creates a new user with just the username, password, and email. For more
2929 * complex user creation use wp_insert_user() to specify more information.
2930 *
2931 * @since 2.0.0
2932 *
2933 * @see wp_insert_user() More complete way to create a new user.
2934 *
2935 * @param string $username The user's username.
2936 * @param string $password The user's password.
2937 * @param string $email Optional. The user's email. Default empty.
2938 * @return int|WP_Error The newly created user's ID or a WP_Error object if the user could not
2939 * be created.
2940 */
2941function wp_create_user(
2942 $username,
2943 #[\SensitiveParameter]
2944 $password,
2945 $email = ''
2946) {
2947 $user_login = wp_slash( $username );
2948 $user_email = wp_slash( $email );
2949 $user_pass = $password;
2950
2951 $userdata = compact( 'user_login', 'user_email', 'user_pass' );
2952 return wp_insert_user( $userdata );
2953}
2954
2955/**
2956 * Returns a list of meta keys to be (maybe) populated in wp_update_user().
2957 *
2958 * The list of keys returned via this function are dependent on the presence
2959 * of those keys in the user meta data to be set.
2960 *
2961 * @since 3.3.0
2962 * @access private
2963 *
2964 * @param WP_User $user WP_User instance.
2965 * @return string[] List of user keys to be populated in wp_update_user().
2966 */
2967function _get_additional_user_keys( $user ) {
2968 $keys = array( 'first_name', 'last_name', 'nickname', 'description', 'rich_editing', 'syntax_highlighting', 'comment_shortcuts', 'admin_color', 'use_ssl', 'show_admin_bar_front', 'locale' );
2969 return array_merge( $keys, array_keys( wp_get_user_contact_methods( $user ) ) );
2970}
2971
2972/**
2973 * Sets up the user contact methods.
2974 *
2975 * Default contact methods were removed for new installations in WordPress 3.6
2976 * and completely removed from the codebase in WordPress 6.9.
2977 *
2978 * Use the {@see 'user_contactmethods'} filter to add or remove contact methods.
2979 *
2980 * @since 3.7.0
2981 * @since 6.9.0 Removed references to `aim`, `jabber`, and `yim` contact methods.
2982 *
2983 * @param WP_User|null $user Optional. WP_User object.
2984 * @return string[] Array of contact method labels keyed by contact method.
2985 */
2986function wp_get_user_contact_methods( $user = null ) {
2987 $methods = array();
2988
2989 /**
2990 * Filters the user contact methods.
2991 *
2992 * @since 2.9.0
2993 *
2994 * @param string[] $methods Array of contact method labels keyed by contact method.
2995 * @param WP_User|null $user WP_User object or null if none was provided.
2996 */
2997 return apply_filters( 'user_contactmethods', $methods, $user );
2998}
2999
3000/**
3001 * The old private function for setting up user contact methods.
3002 *
3003 * Use wp_get_user_contact_methods() instead.
3004 *
3005 * @since 2.9.0
3006 * @access private
3007 *
3008 * @param WP_User|null $user Optional. WP_User object. Default null.
3009 * @return string[] Array of contact method labels keyed by contact method.
3010 */
3011function _wp_get_user_contactmethods( $user = null ) {
3012 return wp_get_user_contact_methods( $user );
3013}
3014
3015/**
3016 * Gets the text suggesting how to create strong passwords.
3017 *
3018 * @since 4.1.0
3019 *
3020 * @return string The password hint text.
3021 */
3022function wp_get_password_hint() {
3023 $hint = __( 'Hint: The password should be at least twelve characters long. To make it stronger, use upper and lower case letters, numbers, and symbols like ! " ? $ % ^ &amp; ).' );
3024
3025 /**
3026 * Filters the text describing the site's password complexity policy.
3027 *
3028 * @since 4.1.0
3029 *
3030 * @param string $hint The password hint text.
3031 */
3032 return apply_filters( 'password_hint', $hint );
3033}
3034
3035/**
3036 * Creates, stores, then returns a password reset key for user.
3037 *
3038 * @since 4.4.0
3039 *
3040 * @param WP_User $user User to retrieve password reset key for.
3041 * @return string|WP_Error Password reset key on success. WP_Error on error.
3042 */
3043function get_password_reset_key( $user ) {
3044 if ( ! ( $user instanceof WP_User ) ) {
3045 return new WP_Error( 'invalidcombo', __( '<strong>Error:</strong> There is no account with that username or email address.' ) );
3046 }
3047
3048 /**
3049 * Fires before a new password is retrieved.
3050 *
3051 * Use the {@see 'retrieve_password'} hook instead.
3052 *
3053 * @since 1.5.0
3054 * @deprecated 1.5.1 Misspelled. Use {@see 'retrieve_password'} hook instead.
3055 *
3056 * @param string $user_login The user login name.
3057 */
3058 do_action_deprecated( 'retreive_password', array( $user->user_login ), '1.5.1', 'retrieve_password' );
3059
3060 /**
3061 * Fires before a new password is retrieved.
3062 *
3063 * @since 1.5.1
3064 *
3065 * @param string $user_login The user login name.
3066 */
3067 do_action( 'retrieve_password', $user->user_login );
3068
3069 $password_reset_allowed = wp_is_password_reset_allowed_for_user( $user );
3070 if ( ! $password_reset_allowed ) {
3071 return new WP_Error( 'no_password_reset', __( 'Password reset is not allowed for this user' ) );
3072 } elseif ( is_wp_error( $password_reset_allowed ) ) {
3073 return $password_reset_allowed;
3074 }
3075
3076 // Generate something random for a password reset key.
3077 $key = wp_generate_password( 20, false );
3078
3079 /**
3080 * Fires when a password reset key is generated.
3081 *
3082 * @since 2.5.0
3083 *
3084 * @param string $user_login The username for the user.
3085 * @param string $key The generated password reset key.
3086 */
3087 do_action( 'retrieve_password_key', $user->user_login, $key );
3088
3089 $hashed = time() . ':' . wp_fast_hash( $key );
3090
3091 $key_saved = wp_update_user(
3092 array(
3093 'ID' => $user->ID,
3094 'user_activation_key' => $hashed,
3095 )
3096 );
3097
3098 if ( is_wp_error( $key_saved ) ) {
3099 return $key_saved;
3100 }
3101
3102 return $key;
3103}
3104
3105/**
3106 * Retrieves a user row based on password reset key and login.
3107 *
3108 * A key is considered 'expired' if it exactly matches the value of the
3109 * user_activation_key field, rather than being matched after going through the
3110 * hashing process. This field is now hashed; old values are no longer accepted
3111 * but have a different WP_Error code so good user feedback can be provided.
3112 *
3113 * @since 3.1.0
3114 *
3115 * @param string $key The password reset key.
3116 * @param string $login The user login.
3117 * @return WP_User|WP_Error WP_User object on success, WP_Error object for invalid or expired keys.
3118 */
3119function check_password_reset_key(
3120 #[\SensitiveParameter]
3121 $key,
3122 $login
3123) {
3124 $key = preg_replace( '/[^a-z0-9]/i', '', $key );
3125
3126 if ( empty( $key ) || ! is_string( $key ) ) {
3127 return new WP_Error( 'invalid_key', __( 'Invalid key.' ) );
3128 }
3129
3130 if ( empty( $login ) || ! is_string( $login ) ) {
3131 return new WP_Error( 'invalid_key', __( 'Invalid key.' ) );
3132 }
3133
3134 $user = get_user_by( 'login', $login );
3135
3136 if ( ! $user ) {
3137 return new WP_Error( 'invalid_key', __( 'Invalid key.' ) );
3138 }
3139
3140 /**
3141 * Filters the expiration time of password reset keys.
3142 *
3143 * @since 4.3.0
3144 *
3145 * @param int $expiration The expiration time in seconds.
3146 */
3147 $expiration_duration = apply_filters( 'password_reset_expiration', DAY_IN_SECONDS );
3148
3149 if ( str_contains( $user->user_activation_key, ':' ) ) {
3150 list( $pass_request_time, $pass_key ) = explode( ':', $user->user_activation_key, 2 );
3151 $expiration_time = $pass_request_time + $expiration_duration;
3152 } else {
3153 $pass_key = $user->user_activation_key;
3154 $expiration_time = false;
3155 }
3156
3157 if ( ! $pass_key ) {
3158 return new WP_Error( 'invalid_key', __( 'Invalid key.' ) );
3159 }
3160
3161 $hash_is_correct = wp_verify_fast_hash( $key, $pass_key );
3162
3163 if ( $hash_is_correct && $expiration_time && time() < $expiration_time ) {
3164 return $user;
3165 } elseif ( $hash_is_correct && $expiration_time ) {
3166 // Key has an expiration time that's passed.
3167 return new WP_Error( 'expired_key', __( 'Invalid key.' ) );
3168 }
3169
3170 if ( hash_equals( $user->user_activation_key, $key ) || ( $hash_is_correct && ! $expiration_time ) ) {
3171 $return = new WP_Error( 'expired_key', __( 'Invalid key.' ) );
3172 $user_id = $user->ID;
3173
3174 /**
3175 * Filters the return value of check_password_reset_key() when an
3176 * old-style key or an expired key is used.
3177 *
3178 * @since 3.7.0 Previously plain-text keys were stored in the database.
3179 * @since 4.3.0 Previously key hashes were stored without an expiration time.
3180 *
3181 * @param WP_Error $return A WP_Error object denoting an expired key.
3182 * Return a WP_User object to validate the key.
3183 * @param int $user_id The matched user ID.
3184 */
3185 return apply_filters( 'password_reset_key_expired', $return, $user_id );
3186 }
3187
3188 return new WP_Error( 'invalid_key', __( 'Invalid key.' ) );
3189}
3190
3191/**
3192 * Handles sending a password retrieval email to a user.
3193 *
3194 * @since 2.5.0
3195 * @since 5.7.0 Added `$user_login` parameter.
3196 *
3197 * @global wpdb $wpdb WordPress database abstraction object.
3198 *
3199 * @param string $user_login Optional. Username to send a password retrieval email for.
3200 * Defaults to `$_POST['user_login']` if not set.
3201 * @return true|WP_Error True when finished, WP_Error object on error.
3202 */
3203function retrieve_password( $user_login = '' ) {
3204 $errors = new WP_Error();
3205 $user_data = false;
3206
3207 // Use the passed $user_login if available, otherwise use $_POST['user_login'].
3208 if ( ! $user_login && ! empty( $_POST['user_login'] ) && is_string( $_POST['user_login'] ) ) {
3209 $user_login = $_POST['user_login'];
3210 }
3211
3212 $user_login = trim( wp_unslash( $user_login ) );
3213
3214 if ( empty( $user_login ) ) {
3215 $errors->add( 'empty_username', __( '<strong>Error:</strong> Please enter a username or email address.' ) );
3216 } elseif ( strpos( $user_login, '@' ) ) {
3217 $user_data = get_user_by( 'email', $user_login );
3218
3219 if ( empty( $user_data ) ) {
3220 $user_data = get_user_by( 'login', $user_login );
3221 }
3222
3223 if ( empty( $user_data ) ) {
3224 $errors->add( 'invalid_email', __( '<strong>Error:</strong> There is no account with that username or email address.' ) );
3225 }
3226 } else {
3227 $user_data = get_user_by( 'login', $user_login );
3228 }
3229
3230 /**
3231 * Filters the user data during a password reset request.
3232 *
3233 * Allows, for example, custom validation using data other than username or email address.
3234 *
3235 * @since 5.7.0
3236 *
3237 * @param WP_User|false $user_data WP_User object if found, false if the user does not exist.
3238 * @param WP_Error $errors A WP_Error object containing any errors generated
3239 * by using invalid credentials.
3240 */
3241 $user_data = apply_filters( 'lostpassword_user_data', $user_data, $errors );
3242
3243 /**
3244 * Fires before errors are returned from a password reset request.
3245 *
3246 * @since 2.1.0
3247 * @since 4.4.0 Added the `$errors` parameter.
3248 * @since 5.4.0 Added the `$user_data` parameter.
3249 *
3250 * @param WP_Error $errors A WP_Error object containing any errors generated
3251 * by using invalid credentials.
3252 * @param WP_User|false $user_data WP_User object if found, false if the user does not exist.
3253 */
3254 do_action( 'lostpassword_post', $errors, $user_data );
3255
3256 /**
3257 * Filters the errors encountered on a password reset request.
3258 *
3259 * The filtered WP_Error object may, for example, contain errors for an invalid
3260 * username or email address. A WP_Error object should always be returned,
3261 * but may or may not contain errors.
3262 *
3263 * If any errors are present in $errors, this will abort the password reset request.
3264 *
3265 * @since 5.5.0
3266 *
3267 * @param WP_Error $errors A WP_Error object containing any errors generated
3268 * by using invalid credentials.
3269 * @param WP_User|false $user_data WP_User object if found, false if the user does not exist.
3270 */
3271 $errors = apply_filters( 'lostpassword_errors', $errors, $user_data );
3272
3273 if ( $errors->has_errors() ) {
3274 return $errors;
3275 }
3276
3277 if ( ! $user_data ) {
3278 $errors->add( 'invalidcombo', __( '<strong>Error:</strong> There is no account with that username or email address.' ) );
3279 return $errors;
3280 }
3281
3282 /**
3283 * Filters whether to send the retrieve password email.
3284 *
3285 * Return false to disable sending the email.
3286 *
3287 * @since 6.0.0
3288 *
3289 * @param bool $send Whether to send the email.
3290 * @param string $user_login The username for the user.
3291 * @param WP_User $user_data WP_User object.
3292 */
3293 if ( ! apply_filters( 'send_retrieve_password_email', true, $user_login, $user_data ) ) {
3294 return true;
3295 }
3296
3297 // Redefining user_login ensures we return the right case in the email.
3298 $user_login = $user_data->user_login;
3299 $user_email = $user_data->user_email;
3300 $key = get_password_reset_key( $user_data );
3301
3302 if ( is_wp_error( $key ) ) {
3303 return $key;
3304 }
3305
3306 // Localize password reset message content for user.
3307 $locale = get_user_locale( $user_data );
3308
3309 $switched_locale = switch_to_user_locale( $user_data->ID );
3310
3311 if ( is_multisite() ) {
3312 $site_name = get_network()->site_name;
3313 } else {
3314 /*
3315 * The blogname option is escaped with esc_html on the way into the database
3316 * in sanitize_option. We want to reverse this for the plain text arena of emails.
3317 */
3318 $site_name = wp_specialchars_decode( get_option( 'blogname' ), ENT_QUOTES );
3319 }
3320
3321 $message = __( 'Someone has requested a password reset for the following account:' ) . "\r\n\r\n";
3322 /* translators: %s: Site name. */
3323 $message .= sprintf( __( 'Site Name: %s' ), $site_name ) . "\r\n\r\n";
3324 /* translators: %s: User login. */
3325 $message .= sprintf( __( 'Username: %s' ), $user_login ) . "\r\n\r\n";
3326 $message .= __( 'If this was a mistake, ignore this email and nothing will happen.' ) . "\r\n\r\n";
3327 $message .= __( 'To reset your password, visit the following address:' ) . "\r\n\r\n";
3328
3329 /*
3330 * Since some user login names end in a period, this could produce ambiguous URLs that
3331 * end in a period. To avoid the ambiguity, ensure that the login is not the last query
3332 * arg in the URL. If moving it to the end, a trailing period will need to be escaped.
3333 *
3334 * @see https://core.trac.wordpress.org/tickets/42957
3335 */
3336 $message .= network_site_url( 'wp-login.php?login=' . rawurlencode( $user_login ) . "&key=$key&action=rp", 'login' ) . '&wp_lang=' . $locale . "\r\n\r\n";
3337
3338 if ( ! is_user_logged_in() ) {
3339 $requester_ip = $_SERVER['REMOTE_ADDR'];
3340 if ( $requester_ip ) {
3341 $message .= sprintf(
3342 /* translators: %s: IP address of password reset requester. */
3343 __( 'This password reset request originated from the IP address %s.' ),
3344 $requester_ip
3345 ) . "\r\n";
3346 }
3347 }
3348
3349 /* translators: Password reset notification email subject. %s: Site title. */
3350 $title = sprintf( __( '[%s] Password Reset' ), $site_name );
3351
3352 /**
3353 * Filters the subject of the password reset email.
3354 *
3355 * @since 2.8.0
3356 * @since 4.4.0 Added the `$user_login` and `$user_data` parameters.
3357 *
3358 * @param string $title Email subject.
3359 * @param string $user_login The username for the user.
3360 * @param WP_User $user_data WP_User object.
3361 */
3362 $title = apply_filters( 'retrieve_password_title', $title, $user_login, $user_data );
3363
3364 /**
3365 * Filters the message body of the password reset mail.
3366 *
3367 * If the filtered message is empty, the password reset email will not be sent.
3368 *
3369 * @since 2.8.0
3370 * @since 4.1.0 Added `$user_login` and `$user_data` parameters.
3371 *
3372 * @param string $message Email message.
3373 * @param string $key The activation key.
3374 * @param string $user_login The username for the user.
3375 * @param WP_User $user_data WP_User object.
3376 */
3377 $message = apply_filters( 'retrieve_password_message', $message, $key, $user_login, $user_data );
3378
3379 // Short-circuit on falsey $message value for backwards compatibility.
3380 if ( ! $message ) {
3381 return true;
3382 }
3383
3384 /*
3385 * Wrap the single notification email arguments in an array
3386 * to pass them to the retrieve_password_notification_email filter.
3387 */
3388 $defaults = array(
3389 'to' => $user_email,
3390 'subject' => $title,
3391 'message' => $message,
3392 'headers' => '',
3393 );
3394
3395 /**
3396 * Filters the contents of the reset password notification email sent to the user.
3397 *
3398 * @since 6.0.0
3399 *
3400 * @param array $defaults {
3401 * The default notification email arguments. Used to build wp_mail().
3402 *
3403 * @type string $to The intended recipient - user email address.
3404 * @type string $subject The subject of the email.
3405 * @type string $message The body of the email.
3406 * @type string $headers The headers of the email.
3407 * }
3408 * @param string $key The activation key.
3409 * @param string $user_login The username for the user.
3410 * @param WP_User $user_data WP_User object.
3411 */
3412 $notification_email = apply_filters( 'retrieve_password_notification_email', $defaults, $key, $user_login, $user_data );
3413
3414 if ( $switched_locale ) {
3415 restore_previous_locale();
3416 }
3417
3418 if ( is_array( $notification_email ) ) {
3419 // Force key order and merge defaults in case any value is missing in the filtered array.
3420 $notification_email = array_merge( $defaults, $notification_email );
3421 } else {
3422 $notification_email = $defaults;
3423 }
3424
3425 list( $to, $subject, $message, $headers ) = array_values( $notification_email );
3426
3427 $subject = wp_specialchars_decode( $subject );
3428
3429 if ( ! wp_mail( $to, $subject, $message, $headers ) ) {
3430 $errors->add(
3431 'retrieve_password_email_failure',
3432 sprintf(
3433 /* translators: %s: Documentation URL. */
3434 __( '<strong>Error:</strong> The email could not be sent. Your site may not be correctly configured to send emails. <a href="%s">Get support for resetting your password</a>.' ),
3435 esc_url( __( 'https://wordpress.org/documentation/article/reset-your-password/' ) )
3436 )
3437 );
3438 return $errors;
3439 }
3440
3441 return true;
3442}
3443
3444/**
3445 * Handles resetting the user's password.
3446 *
3447 * @since 2.5.0
3448 *
3449 * @param WP_User $user The user
3450 * @param string $new_pass New password for the user in plaintext
3451 */
3452function reset_password(
3453 $user,
3454 #[\SensitiveParameter]
3455 $new_pass
3456) {
3457 /**
3458 * Fires before the user's password is reset.
3459 *
3460 * @since 1.5.0
3461 *
3462 * @param WP_User $user The user.
3463 * @param string $new_pass New user password.
3464 */
3465 do_action( 'password_reset', $user, $new_pass );
3466
3467 wp_set_password( $new_pass, $user->ID );
3468 update_user_meta( $user->ID, 'default_password_nag', false );
3469
3470 /**
3471 * Fires after the user's password is reset.
3472 *
3473 * @since 4.4.0
3474 *
3475 * @param WP_User $user The user.
3476 * @param string $new_pass New user password.
3477 */
3478 do_action( 'after_password_reset', $user, $new_pass );
3479}
3480
3481/**
3482 * Handles registering a new user.
3483 *
3484 * @since 2.5.0
3485 *
3486 * @param string $user_login User's username for logging in
3487 * @param string $user_email User's email address to send password and add
3488 * @return int|WP_Error Either user's ID or error on failure.
3489 */
3490function register_new_user( $user_login, $user_email ) {
3491 $errors = new WP_Error();
3492
3493 $sanitized_user_login = sanitize_user( $user_login );
3494 /**
3495 * Filters the email address of a user being registered.
3496 *
3497 * @since 2.1.0
3498 *
3499 * @param string $user_email The email address of the new user.
3500 */
3501 $user_email = apply_filters( 'user_registration_email', $user_email );
3502
3503 // Check the username.
3504 if ( '' === $sanitized_user_login ) {
3505 $errors->add( 'empty_username', __( '<strong>Error:</strong> Please enter a username.' ) );
3506 } elseif ( ! validate_username( $user_login ) ) {
3507 $errors->add( 'invalid_username', __( '<strong>Error:</strong> This username is invalid because it uses illegal characters. Please enter a valid username.' ) );
3508 $sanitized_user_login = '';
3509 } elseif ( username_exists( $sanitized_user_login ) ) {
3510 $errors->add( 'username_exists', __( '<strong>Error:</strong> This username is already registered. Please choose another one.' ) );
3511 } else {
3512 /** This filter is documented in wp-includes/user.php */
3513 $illegal_user_logins = (array) apply_filters( 'illegal_user_logins', array() );
3514 if ( in_array( strtolower( $sanitized_user_login ), array_map( 'strtolower', $illegal_user_logins ), true ) ) {
3515 $errors->add( 'invalid_username', __( '<strong>Error:</strong> Sorry, that username is not allowed.' ) );
3516 }
3517 }
3518
3519 // Check the email address.
3520 if ( '' === $user_email ) {
3521 $errors->add( 'empty_email', __( '<strong>Error:</strong> Please type your email address.' ) );
3522 } elseif ( ! is_email( $user_email ) ) {
3523 $errors->add( 'invalid_email', __( '<strong>Error:</strong> The email address is not correct.' ) );
3524 $user_email = '';
3525 } elseif ( email_exists( $user_email ) ) {
3526 $errors->add(
3527 'email_exists',
3528 sprintf(
3529 /* translators: %s: Link to the login page. */
3530 __( '<strong>Error:</strong> This email address is already registered. <a href="%s">Log in</a> with this address or choose another one.' ),
3531 wp_login_url()
3532 )
3533 );
3534 }
3535
3536 /**
3537 * Fires when submitting registration form data, before the user is created.
3538 *
3539 * @since 2.1.0
3540 *
3541 * @param string $sanitized_user_login The submitted username after being sanitized.
3542 * @param string $user_email The submitted email.
3543 * @param WP_Error $errors Contains any errors with submitted username and email,
3544 * e.g., an empty field, an invalid username or email,
3545 * or an existing username or email.
3546 */
3547 do_action( 'register_post', $sanitized_user_login, $user_email, $errors );
3548
3549 /**
3550 * Filters the errors encountered when a new user is being registered.
3551 *
3552 * The filtered WP_Error object may, for example, contain errors for an invalid
3553 * or existing username or email address. A WP_Error object should always be returned,
3554 * but may or may not contain errors.
3555 *
3556 * If any errors are present in $errors, this will abort the user's registration.
3557 *
3558 * @since 2.1.0
3559 *
3560 * @param WP_Error $errors A WP_Error object containing any errors encountered
3561 * during registration.
3562 * @param string $sanitized_user_login User's username after it has been sanitized.
3563 * @param string $user_email User's email.
3564 */
3565 $errors = apply_filters( 'registration_errors', $errors, $sanitized_user_login, $user_email );
3566
3567 if ( $errors->has_errors() ) {
3568 return $errors;
3569 }
3570
3571 $user_pass = wp_generate_password( 12, false );
3572 $user_id = wp_create_user( $sanitized_user_login, $user_pass, $user_email );
3573 if ( ! $user_id || is_wp_error( $user_id ) ) {
3574 $errors->add(
3575 'registerfail',
3576 sprintf(
3577 /* translators: %s: Admin email address. */
3578 __( '<strong>Error:</strong> Could not register you&hellip; please contact the <a href="mailto:%s">site admin</a>!' ),
3579 get_option( 'admin_email' )
3580 )
3581 );
3582 return $errors;
3583 }
3584
3585 update_user_meta( $user_id, 'default_password_nag', true ); // Set up the password change nag.
3586
3587 if ( ! empty( $_COOKIE['wp_lang'] ) ) {
3588 $wp_lang = sanitize_text_field( $_COOKIE['wp_lang'] );
3589 if ( in_array( $wp_lang, get_available_languages(), true ) ) {
3590 update_user_meta( $user_id, 'locale', $wp_lang ); // Set user locale if defined on registration.
3591 }
3592 }
3593
3594 /**
3595 * Fires after a new user registration has been recorded.
3596 *
3597 * @since 4.4.0
3598 *
3599 * @param int $user_id ID of the newly registered user.
3600 */
3601 do_action( 'register_new_user', $user_id );
3602
3603 return $user_id;
3604}
3605
3606/**
3607 * Initiates email notifications related to the creation of new users.
3608 *
3609 * Notifications are sent both to the site admin and to the newly created user.
3610 *
3611 * @since 4.4.0
3612 * @since 4.6.0 Converted the `$notify` parameter to accept 'user' for sending
3613 * notifications only to the user created.
3614 *
3615 * @param int $user_id ID of the newly created user.
3616 * @param string $notify Optional. Type of notification that should happen. Accepts 'admin'
3617 * or an empty string (admin only), 'user', or 'both' (admin and user).
3618 * Default 'both'.
3619 */
3620function wp_send_new_user_notifications( $user_id, $notify = 'both' ) {
3621 wp_new_user_notification( $user_id, null, $notify );
3622}
3623
3624/**
3625 * Retrieves the current session token from the logged_in cookie.
3626 *
3627 * @since 4.0.0
3628 *
3629 * @return string Token.
3630 */
3631function wp_get_session_token() {
3632 $cookie = wp_parse_auth_cookie( '', 'logged_in' );
3633 return ! empty( $cookie['token'] ) ? $cookie['token'] : '';
3634}
3635
3636/**
3637 * Retrieves a list of sessions for the current user.
3638 *
3639 * @since 4.0.0
3640 *
3641 * @return array Array of sessions.
3642 */
3643function wp_get_all_sessions() {
3644 $manager = WP_Session_Tokens::get_instance( get_current_user_id() );
3645 return $manager->get_all();
3646}
3647
3648/**
3649 * Removes the current session token from the database.
3650 *
3651 * @since 4.0.0
3652 */
3653function wp_destroy_current_session() {
3654 $token = wp_get_session_token();
3655 if ( $token ) {
3656 $manager = WP_Session_Tokens::get_instance( get_current_user_id() );
3657 $manager->destroy( $token );
3658 }
3659}
3660
3661/**
3662 * Removes all but the current session token for the current user for the database.
3663 *
3664 * @since 4.0.0
3665 */
3666function wp_destroy_other_sessions() {
3667 $token = wp_get_session_token();
3668 if ( $token ) {
3669 $manager = WP_Session_Tokens::get_instance( get_current_user_id() );
3670 $manager->destroy_others( $token );
3671 }
3672}
3673
3674/**
3675 * Removes all session tokens for the current user from the database.
3676 *
3677 * @since 4.0.0
3678 */
3679function wp_destroy_all_sessions() {
3680 $manager = WP_Session_Tokens::get_instance( get_current_user_id() );
3681 $manager->destroy_all();
3682}
3683
3684/**
3685 * Gets the user IDs of all users with no role on this site.
3686 *
3687 * @since 4.4.0
3688 * @since 4.9.0 The `$site_id` parameter was added to support multisite.
3689 *
3690 * @global wpdb $wpdb WordPress database abstraction object.
3691 *
3692 * @param int|null $site_id Optional. The site ID to get users with no role for. Defaults to the current site.
3693 * @return string[] Array of user IDs as strings.
3694 */
3695function wp_get_users_with_no_role( $site_id = null ) {
3696 global $wpdb;
3697
3698 if ( ! $site_id ) {
3699 $site_id = get_current_blog_id();
3700 }
3701
3702 $prefix = $wpdb->get_blog_prefix( $site_id );
3703
3704 if ( is_multisite() && get_current_blog_id() !== $site_id ) {
3705 switch_to_blog( $site_id );
3706 $role_names = wp_roles()->get_names();
3707 restore_current_blog();
3708 } else {
3709 $role_names = wp_roles()->get_names();
3710 }
3711
3712 $regex = implode( '|', array_keys( $role_names ) );
3713 $regex = preg_replace( '/[^a-zA-Z_\|-]/', '', $regex );
3714 $users = $wpdb->get_col(
3715 $wpdb->prepare(
3716 "SELECT user_id
3717 FROM $wpdb->usermeta
3718 WHERE meta_key = '{$prefix}capabilities'
3719 AND meta_value NOT REGEXP %s",
3720 $regex
3721 )
3722 );
3723
3724 return $users;
3725}
3726
3727/**
3728 * Retrieves the current user object.
3729 *
3730 * Will set the current user, if the current user is not set. The current user
3731 * will be set to the logged-in person. If no user is logged-in, then it will
3732 * set the current user to 0, which is invalid and won't have any permissions.
3733 *
3734 * This function is used by the pluggable functions wp_get_current_user() and
3735 * get_currentuserinfo(), the latter of which is deprecated but used for backward
3736 * compatibility.
3737 *
3738 * @since 4.5.0
3739 * @access private
3740 *
3741 * @see wp_get_current_user()
3742 * @global WP_User $current_user Checks if the current user is set.
3743 *
3744 * @return WP_User Current WP_User instance.
3745 */
3746function _wp_get_current_user() {
3747 global $current_user;
3748
3749 if ( ! empty( $current_user ) ) {
3750 if ( $current_user instanceof WP_User ) {
3751 return $current_user;
3752 }
3753
3754 // Upgrade stdClass to WP_User.
3755 if ( is_object( $current_user ) && isset( $current_user->ID ) ) {
3756 $cur_id = $current_user->ID;
3757 $current_user = null;
3758 wp_set_current_user( $cur_id );
3759 return $current_user;
3760 }
3761
3762 // $current_user has a junk value. Force to WP_User with ID 0.
3763 $current_user = null;
3764 wp_set_current_user( 0 );
3765 return $current_user;
3766 }
3767
3768 if ( defined( 'XMLRPC_REQUEST' ) && XMLRPC_REQUEST ) {
3769 wp_set_current_user( 0 );
3770 return $current_user;
3771 }
3772
3773 /**
3774 * Filters the current user.
3775 *
3776 * The default filters use this to determine the current user from the
3777 * request's cookies, if available.
3778 *
3779 * Returning a value of false will effectively short-circuit setting
3780 * the current user.
3781 *
3782 * @since 3.9.0
3783 *
3784 * @param int|false $user_id User ID if one has been determined, false otherwise.
3785 */
3786 $user_id = apply_filters( 'determine_current_user', false );
3787 if ( ! $user_id ) {
3788 wp_set_current_user( 0 );
3789 return $current_user;
3790 }
3791
3792 wp_set_current_user( $user_id );
3793
3794 return $current_user;
3795}
3796
3797/**
3798 * Sends a confirmation request email when a change of user email address is attempted.
3799 *
3800 * @since 3.0.0
3801 * @since 4.9.0 This function was moved from wp-admin/includes/ms.php so it's no longer Multisite specific.
3802 *
3803 * @global WP_Error $errors WP_Error object.
3804 */
3805function send_confirmation_on_profile_email() {
3806 global $errors;
3807
3808 $current_user = wp_get_current_user();
3809 if ( ! is_object( $errors ) ) {
3810 $errors = new WP_Error();
3811 }
3812
3813 if ( $current_user->ID !== (int) $_POST['user_id'] ) {
3814 return false;
3815 }
3816
3817 if ( $current_user->user_email !== $_POST['email'] ) {
3818 if ( ! is_email( $_POST['email'] ) ) {
3819 $errors->add(
3820 'user_email',
3821 __( '<strong>Error:</strong> The email address is not correct.' ),
3822 array(
3823 'form-field' => 'email',
3824 )
3825 );
3826
3827 return;
3828 }
3829
3830 if ( email_exists( $_POST['email'] ) ) {
3831 $errors->add(
3832 'user_email',
3833 __( '<strong>Error:</strong> The email address is already used.' ),
3834 array(
3835 'form-field' => 'email',
3836 )
3837 );
3838 delete_user_meta( $current_user->ID, '_new_email' );
3839
3840 return;
3841 }
3842
3843 $hash = md5( $_POST['email'] . time() . wp_rand() );
3844 $new_user_email = array(
3845 'hash' => $hash,
3846 'newemail' => $_POST['email'],
3847 );
3848 update_user_meta( $current_user->ID, '_new_email', $new_user_email );
3849
3850 $sitename = wp_specialchars_decode( get_option( 'blogname' ), ENT_QUOTES );
3851
3852 /* translators: Do not translate USERNAME, ADMIN_URL, EMAIL, SITENAME, SITEURL: those are placeholders. */
3853 $email_text = __(
3854 'Howdy ###USERNAME###,
3855
3856You recently requested to have the email address on your account changed.
3857
3858If this is correct, please click on the following link to change it:
3859###ADMIN_URL###
3860
3861You can safely ignore and delete this email if you do not want to
3862take this action.
3863
3864This email has been sent to ###EMAIL###
3865
3866Regards,
3867All at ###SITENAME###
3868###SITEURL###'
3869 );
3870
3871 /**
3872 * Filters the text of the email sent when a change of user email address is attempted.
3873 *
3874 * The following strings have a special meaning and will get replaced dynamically:
3875 *
3876 * - `###USERNAME###` The current user's username.
3877 * - `###ADMIN_URL###` The link to click on to confirm the email change.
3878 * - `###EMAIL###` The new email.
3879 * - `###SITENAME###` The name of the site.
3880 * - `###SITEURL###` The URL to the site.
3881 *
3882 * @since MU (3.0.0)
3883 * @since 4.9.0 This filter is no longer Multisite specific.
3884 *
3885 * @param string $email_text Text in the email.
3886 * @param array $new_user_email {
3887 * Data relating to the new user email address.
3888 *
3889 * @type string $hash The secure hash used in the confirmation link URL.
3890 * @type string $newemail The proposed new email address.
3891 * }
3892 */
3893 $content = apply_filters( 'new_user_email_content', $email_text, $new_user_email );
3894
3895 $content = str_replace( '###USERNAME###', $current_user->user_login, $content );
3896 $content = str_replace( '###ADMIN_URL###', esc_url( self_admin_url( 'profile.php?newuseremail=' . $hash ) ), $content );
3897 $content = str_replace( '###EMAIL###', $_POST['email'], $content );
3898 $content = str_replace( '###SITENAME###', $sitename, $content );
3899 $content = str_replace( '###SITEURL###', home_url(), $content );
3900
3901 /* translators: New email address notification email subject. %s: Site title. */
3902 wp_mail( $_POST['email'], sprintf( __( '[%s] Email Change Request' ), $sitename ), $content );
3903
3904 $_POST['email'] = $current_user->user_email;
3905 }
3906}
3907
3908/**
3909 * Adds an admin notice alerting the user to check for confirmation request email
3910 * after email address change.
3911 *
3912 * @since 3.0.0
3913 * @since 4.9.0 This function was moved from wp-admin/includes/ms.php so it's no longer Multisite specific.
3914 *
3915 * @global string $pagenow The filename of the current screen.
3916 */
3917function new_user_email_admin_notice() {
3918 global $pagenow;
3919
3920 if ( 'profile.php' === $pagenow && isset( $_GET['updated'] ) ) {
3921 $email = get_user_meta( get_current_user_id(), '_new_email', true );
3922 if ( $email ) {
3923 $message = sprintf(
3924 /* translators: %s: New email address. */
3925 __( 'Your email address has not been updated yet. Please check your inbox at %s for a confirmation email.' ),
3926 '<code>' . esc_html( $email['newemail'] ) . '</code>'
3927 );
3928 wp_admin_notice( $message, array( 'type' => 'info' ) );
3929 }
3930 }
3931}
3932
3933/**
3934 * Gets all personal data request types.
3935 *
3936 * @since 4.9.6
3937 * @access private
3938 *
3939 * @return string[] List of core privacy action types.
3940 */
3941function _wp_privacy_action_request_types() {
3942 return array(
3943 'export_personal_data',
3944 'remove_personal_data',
3945 );
3946}
3947
3948/**
3949 * Registers the personal data exporter for users.
3950 *
3951 * @since 4.9.6
3952 *
3953 * @param array[] $exporters An array of personal data exporters.
3954 * @return array[] An array of personal data exporters.
3955 */
3956function wp_register_user_personal_data_exporter( $exporters ) {
3957 $exporters['wordpress-user'] = array(
3958 'exporter_friendly_name' => __( 'WordPress User' ),
3959 'callback' => 'wp_user_personal_data_exporter',
3960 );
3961
3962 return $exporters;
3963}
3964
3965/**
3966 * Finds and exports personal data associated with an email address from the user and user_meta table.
3967 *
3968 * @since 4.9.6
3969 * @since 5.4.0 Added 'Community Events Location' group to the export data.
3970 * @since 5.4.0 Added 'Session Tokens' group to the export data.
3971 *
3972 * @param string $email_address The user's email address.
3973 * @return array {
3974 * An array of personal data.
3975 *
3976 * @type array[] $data An array of personal data arrays.
3977 * @type bool $done Whether the exporter is finished.
3978 * }
3979 */
3980function wp_user_personal_data_exporter( $email_address ) {
3981 $email_address = trim( $email_address );
3982
3983 $data_to_export = array();
3984
3985 $user = get_user_by( 'email', $email_address );
3986
3987 if ( ! $user ) {
3988 return array(
3989 'data' => array(),
3990 'done' => true,
3991 );
3992 }
3993
3994 $user_meta = get_user_meta( $user->ID );
3995
3996 $user_props_to_export = array(
3997 'ID' => __( 'User ID' ),
3998 'user_login' => __( 'User Login Name' ),
3999 'user_nicename' => __( 'User Nice Name' ),
4000 'user_email' => __( 'User Email' ),
4001 'user_url' => __( 'User URL' ),
4002 'user_registered' => __( 'User Registration Date' ),
4003 'display_name' => __( 'User Display Name' ),
4004 'nickname' => __( 'User Nickname' ),
4005 'first_name' => __( 'User First Name' ),
4006 'last_name' => __( 'User Last Name' ),
4007 'description' => __( 'User Description' ),
4008 );
4009
4010 $user_data_to_export = array();
4011
4012 foreach ( $user_props_to_export as $key => $name ) {
4013 $value = '';
4014
4015 switch ( $key ) {
4016 case 'ID':
4017 case 'user_login':
4018 case 'user_nicename':
4019 case 'user_email':
4020 case 'user_url':
4021 case 'user_registered':
4022 case 'display_name':
4023 $value = $user->data->$key;
4024 break;
4025 case 'nickname':
4026 case 'first_name':
4027 case 'last_name':
4028 case 'description':
4029 $value = $user_meta[ $key ][0];
4030 break;
4031 }
4032
4033 if ( ! empty( $value ) ) {
4034 $user_data_to_export[] = array(
4035 'name' => $name,
4036 'value' => $value,
4037 );
4038 }
4039 }
4040
4041 // Get the list of reserved names.
4042 $reserved_names = array_values( $user_props_to_export );
4043
4044 /**
4045 * Filters the user's profile data for the privacy exporter.
4046 *
4047 * @since 5.4.0
4048 *
4049 * @param array $additional_user_profile_data {
4050 * An array of name-value pairs of additional user data items. Default empty array.
4051 *
4052 * @type string $name The user-facing name of an item name-value pair,e.g. 'IP Address'.
4053 * @type string $value The user-facing value of an item data pair, e.g. '50.60.70.0'.
4054 * }
4055 * @param WP_User $user The user whose data is being exported.
4056 * @param string[] $reserved_names An array of reserved names. Any item in `$additional_user_data`
4057 * that uses one of these for its `name` will not be included in the export.
4058 */
4059 $_extra_data = apply_filters( 'wp_privacy_additional_user_profile_data', array(), $user, $reserved_names );
4060
4061 if ( is_array( $_extra_data ) && ! empty( $_extra_data ) ) {
4062 // Remove items that use reserved names.
4063 $extra_data = array_filter(
4064 $_extra_data,
4065 static function ( $item ) use ( $reserved_names ) {
4066 return ! in_array( $item['name'], $reserved_names, true );
4067 }
4068 );
4069
4070 if ( count( $extra_data ) !== count( $_extra_data ) ) {
4071 _doing_it_wrong(
4072 __FUNCTION__,
4073 sprintf(
4074 /* translators: %s: wp_privacy_additional_user_profile_data */
4075 __( 'Filter %s returned items with reserved names.' ),
4076 '<code>wp_privacy_additional_user_profile_data</code>'
4077 ),
4078 '5.4.0'
4079 );
4080 }
4081
4082 if ( ! empty( $extra_data ) ) {
4083 $user_data_to_export = array_merge( $user_data_to_export, $extra_data );
4084 }
4085 }
4086
4087 $data_to_export[] = array(
4088 'group_id' => 'user',
4089 'group_label' => __( 'User' ),
4090 'group_description' => __( 'User&#8217;s profile data.' ),
4091 'item_id' => "user-{$user->ID}",
4092 'data' => $user_data_to_export,
4093 );
4094
4095 if ( isset( $user_meta['community-events-location'] ) ) {
4096 $location = maybe_unserialize( $user_meta['community-events-location'][0] );
4097
4098 $location_props_to_export = array(
4099 'description' => __( 'City' ),
4100 'country' => __( 'Country' ),
4101 'latitude' => __( 'Latitude' ),
4102 'longitude' => __( 'Longitude' ),
4103 'ip' => __( 'IP' ),
4104 );
4105
4106 $location_data_to_export = array();
4107
4108 foreach ( $location_props_to_export as $key => $name ) {
4109 if ( ! empty( $location[ $key ] ) ) {
4110 $location_data_to_export[] = array(
4111 'name' => $name,
4112 'value' => $location[ $key ],
4113 );
4114 }
4115 }
4116
4117 $data_to_export[] = array(
4118 'group_id' => 'community-events-location',
4119 'group_label' => __( 'Community Events Location' ),
4120 'group_description' => __( 'User&#8217;s location data used for the Community Events in the WordPress Events and News dashboard widget.' ),
4121 'item_id' => "community-events-location-{$user->ID}",
4122 'data' => $location_data_to_export,
4123 );
4124 }
4125
4126 if ( isset( $user_meta['session_tokens'] ) ) {
4127 $session_tokens = maybe_unserialize( $user_meta['session_tokens'][0] );
4128
4129 $session_tokens_props_to_export = array(
4130 'expiration' => __( 'Expiration' ),
4131 'ip' => __( 'IP' ),
4132 'ua' => __( 'User Agent' ),
4133 'login' => __( 'Last Login' ),
4134 );
4135
4136 foreach ( $session_tokens as $token_key => $session_token ) {
4137 $session_tokens_data_to_export = array();
4138
4139 foreach ( $session_tokens_props_to_export as $key => $name ) {
4140 if ( ! empty( $session_token[ $key ] ) ) {
4141 $value = $session_token[ $key ];
4142 if ( in_array( $key, array( 'expiration', 'login' ), true ) ) {
4143 $value = date_i18n( 'F d, Y H:i A', $value );
4144 }
4145 $session_tokens_data_to_export[] = array(
4146 'name' => $name,
4147 'value' => $value,
4148 );
4149 }
4150 }
4151
4152 $data_to_export[] = array(
4153 'group_id' => 'session-tokens',
4154 'group_label' => __( 'Session Tokens' ),
4155 'group_description' => __( 'User&#8217;s Session Tokens data.' ),
4156 'item_id' => "session-tokens-{$user->ID}-{$token_key}",
4157 'data' => $session_tokens_data_to_export,
4158 );
4159 }
4160 }
4161
4162 return array(
4163 'data' => $data_to_export,
4164 'done' => true,
4165 );
4166}
4167
4168/**
4169 * Updates log when privacy request is confirmed.
4170 *
4171 * @since 4.9.6
4172 * @access private
4173 *
4174 * @param int $request_id ID of the request.
4175 */
4176function _wp_privacy_account_request_confirmed( $request_id ) {
4177 $request = wp_get_user_request( $request_id );
4178
4179 if ( ! $request ) {
4180 return;
4181 }
4182
4183 if ( ! in_array( $request->status, array( 'request-pending', 'request-failed' ), true ) ) {
4184 return;
4185 }
4186
4187 update_post_meta( $request_id, '_wp_user_request_confirmed_timestamp', time() );
4188 wp_update_post(
4189 array(
4190 'ID' => $request_id,
4191 'post_status' => 'request-confirmed',
4192 )
4193 );
4194}
4195
4196/**
4197 * Notifies the site administrator via email when a request is confirmed.
4198 *
4199 * Without this, the admin would have to manually check the site to see if any
4200 * action was needed on their part yet.
4201 *
4202 * @since 4.9.6
4203 *
4204 * @param int $request_id The ID of the request.
4205 */
4206function _wp_privacy_send_request_confirmation_notification( $request_id ) {
4207 $request = wp_get_user_request( $request_id );
4208
4209 if ( ! ( $request instanceof WP_User_Request ) || 'request-confirmed' !== $request->status ) {
4210 return;
4211 }
4212
4213 $already_notified = (bool) get_post_meta( $request_id, '_wp_admin_notified', true );
4214
4215 if ( $already_notified ) {
4216 return;
4217 }
4218
4219 if ( 'export_personal_data' === $request->action_name ) {
4220 $manage_url = admin_url( 'export-personal-data.php' );
4221 } elseif ( 'remove_personal_data' === $request->action_name ) {
4222 $manage_url = admin_url( 'erase-personal-data.php' );
4223 }
4224 $action_description = wp_user_request_action_description( $request->action_name );
4225
4226 /**
4227 * Filters the recipient of the data request confirmation notification.
4228 *
4229 * In a Multisite environment, this will default to the email address of the
4230 * network admin because, by default, single site admins do not have the
4231 * capabilities required to process requests. Some networks may wish to
4232 * delegate those capabilities to a single-site admin, or a dedicated person
4233 * responsible for managing privacy requests.
4234 *
4235 * @since 4.9.6
4236 *
4237 * @param string $admin_email The email address of the notification recipient.
4238 * @param WP_User_Request $request The request that is initiating the notification.
4239 */
4240 $admin_email = apply_filters( 'user_request_confirmed_email_to', get_site_option( 'admin_email' ), $request );
4241
4242 $email_data = array(
4243 'request' => $request,
4244 'user_email' => $request->email,
4245 'description' => $action_description,
4246 'manage_url' => $manage_url,
4247 'sitename' => wp_specialchars_decode( get_option( 'blogname' ), ENT_QUOTES ),
4248 'siteurl' => home_url(),
4249 'admin_email' => $admin_email,
4250 );
4251
4252 $subject = sprintf(
4253 /* translators: Privacy data request confirmed notification email subject. 1: Site title, 2: Name of the confirmed action. */
4254 __( '[%1$s] Action Confirmed: %2$s' ),
4255 $email_data['sitename'],
4256 $action_description
4257 );
4258
4259 /**
4260 * Filters the subject of the user request confirmation email.
4261 *
4262 * @since 4.9.8
4263 *
4264 * @param string $subject The email subject.
4265 * @param string $sitename The name of the site.
4266 * @param array $email_data {
4267 * Data relating to the account action email.
4268 *
4269 * @type WP_User_Request $request User request object.
4270 * @type string $user_email The email address confirming a request.
4271 * @type string $description Description of the action being performed so the user knows what the email is for.
4272 * @type string $manage_url The link to click manage privacy requests of this type.
4273 * @type string $sitename The site name sending the mail.
4274 * @type string $siteurl The site URL sending the mail.
4275 * @type string $admin_email The administrator email receiving the mail.
4276 * }
4277 */
4278 $subject = apply_filters( 'user_request_confirmed_email_subject', $subject, $email_data['sitename'], $email_data );
4279
4280 /* translators: Do not translate SITENAME, USER_EMAIL, DESCRIPTION, MANAGE_URL, SITEURL; those are placeholders. */
4281 $content = __(
4282 'Howdy,
4283
4284A user data privacy request has been confirmed on ###SITENAME###:
4285
4286User: ###USER_EMAIL###
4287Request: ###DESCRIPTION###
4288
4289You can view and manage these data privacy requests here:
4290
4291###MANAGE_URL###
4292
4293Regards,
4294All at ###SITENAME###
4295###SITEURL###'
4296 );
4297
4298 /**
4299 * Filters the body of the user request confirmation email.
4300 *
4301 * The email is sent to an administrator when a user request is confirmed.
4302 *
4303 * The following strings have a special meaning and will get replaced dynamically:
4304 *
4305 * - `###SITENAME###` The name of the site.
4306 * - `###USER_EMAIL###` The user email for the request.
4307 * - `###DESCRIPTION###` Description of the action being performed so the user knows what the email is for.
4308 * - `###MANAGE_URL###` The URL to manage requests.
4309 * - `###SITEURL###` The URL to the site.
4310 *
4311 * @since 4.9.6
4312 * @deprecated 5.8.0 Use {@see 'user_request_confirmed_email_content'} instead.
4313 * For user erasure fulfillment email content
4314 * use {@see 'user_erasure_fulfillment_email_content'} instead.
4315 *
4316 * @param string $content The email content.
4317 * @param array $email_data {
4318 * Data relating to the account action email.
4319 *
4320 * @type WP_User_Request $request User request object.
4321 * @type string $user_email The email address confirming a request.
4322 * @type string $description Description of the action being performed
4323 * so the user knows what the email is for.
4324 * @type string $manage_url The link to click manage privacy requests of this type.
4325 * @type string $sitename The site name sending the mail.
4326 * @type string $siteurl The site URL sending the mail.
4327 * @type string $admin_email The administrator email receiving the mail.
4328 * }
4329 */
4330 $content = apply_filters_deprecated(
4331 'user_confirmed_action_email_content',
4332 array( $content, $email_data ),
4333 '5.8.0',
4334 sprintf(
4335 /* translators: 1 & 2: Deprecation replacement options. */
4336 __( '%1$s or %2$s' ),
4337 'user_request_confirmed_email_content',
4338 'user_erasure_fulfillment_email_content'
4339 )
4340 );
4341
4342 /**
4343 * Filters the body of the user request confirmation email.
4344 *
4345 * The email is sent to an administrator when a user request is confirmed.
4346 * The following strings have a special meaning and will get replaced dynamically:
4347 *
4348 * - `###SITENAME###` The name of the site.
4349 * - `###USER_EMAIL###` The user email for the request.
4350 * - `###DESCRIPTION###` Description of the action being performed so the user knows what the email is for.
4351 * - `###MANAGE_URL###` The URL to manage requests.
4352 * - `###SITEURL###` The URL to the site.
4353 *
4354 * @since 5.8.0
4355 *
4356 * @param string $content The email content.
4357 * @param array $email_data {
4358 * Data relating to the account action email.
4359 *
4360 * @type WP_User_Request $request User request object.
4361 * @type string $user_email The email address confirming a request.
4362 * @type string $description Description of the action being performed so the user knows what the email is for.
4363 * @type string $manage_url The link to click manage privacy requests of this type.
4364 * @type string $sitename The site name sending the mail.
4365 * @type string $siteurl The site URL sending the mail.
4366 * @type string $admin_email The administrator email receiving the mail.
4367 * }
4368 */
4369 $content = apply_filters( 'user_request_confirmed_email_content', $content, $email_data );
4370
4371 $content = str_replace( '###SITENAME###', $email_data['sitename'], $content );
4372 $content = str_replace( '###USER_EMAIL###', $email_data['user_email'], $content );
4373 $content = str_replace( '###DESCRIPTION###', $email_data['description'], $content );
4374 $content = str_replace( '###MANAGE_URL###', sanitize_url( $email_data['manage_url'] ), $content );
4375 $content = str_replace( '###SITEURL###', sanitize_url( $email_data['siteurl'] ), $content );
4376
4377 $headers = '';
4378
4379 /**
4380 * Filters the headers of the user request confirmation email.
4381 *
4382 * @since 5.4.0
4383 *
4384 * @param string|array $headers The email headers.
4385 * @param string $subject The email subject.
4386 * @param string $content The email content.
4387 * @param int $request_id The request ID.
4388 * @param array $email_data {
4389 * Data relating to the account action email.
4390 *
4391 * @type WP_User_Request $request User request object.
4392 * @type string $user_email The email address confirming a request.
4393 * @type string $description Description of the action being performed so the user knows what the email is for.
4394 * @type string $manage_url The link to click manage privacy requests of this type.
4395 * @type string $sitename The site name sending the mail.
4396 * @type string $siteurl The site URL sending the mail.
4397 * @type string $admin_email The administrator email receiving the mail.
4398 * }
4399 */
4400 $headers = apply_filters( 'user_request_confirmed_email_headers', $headers, $subject, $content, $request_id, $email_data );
4401
4402 $email_sent = wp_mail( $email_data['admin_email'], $subject, $content, $headers );
4403
4404 if ( $email_sent ) {
4405 update_post_meta( $request_id, '_wp_admin_notified', true );
4406 }
4407}
4408
4409/**
4410 * Notifies the user when their erasure request is fulfilled.
4411 *
4412 * Without this, the user would never know if their data was actually erased.
4413 *
4414 * @since 4.9.6
4415 *
4416 * @param int $request_id The privacy request post ID associated with this request.
4417 */
4418function _wp_privacy_send_erasure_fulfillment_notification( $request_id ) {
4419 $request = wp_get_user_request( $request_id );
4420
4421 if ( ! ( $request instanceof WP_User_Request ) || 'request-completed' !== $request->status ) {
4422 return;
4423 }
4424
4425 $already_notified = (bool) get_post_meta( $request_id, '_wp_user_notified', true );
4426
4427 if ( $already_notified ) {
4428 return;
4429 }
4430
4431 // Localize message content for user; fallback to site default for visitors.
4432 if ( ! empty( $request->user_id ) ) {
4433 $switched_locale = switch_to_user_locale( $request->user_id );
4434 } else {
4435 $switched_locale = switch_to_locale( get_locale() );
4436 }
4437
4438 /**
4439 * Filters the recipient of the data erasure fulfillment notification.
4440 *
4441 * @since 4.9.6
4442 *
4443 * @param string $user_email The email address of the notification recipient.
4444 * @param WP_User_Request $request The request that is initiating the notification.
4445 */
4446 $user_email = apply_filters( 'user_erasure_fulfillment_email_to', $request->email, $request );
4447
4448 $email_data = array(
4449 'request' => $request,
4450 'message_recipient' => $user_email,
4451 'privacy_policy_url' => get_privacy_policy_url(),
4452 'sitename' => wp_specialchars_decode( get_option( 'blogname' ), ENT_QUOTES ),
4453 'siteurl' => home_url(),
4454 );
4455
4456 $subject = sprintf(
4457 /* translators: Erasure request fulfilled notification email subject. %s: Site title. */
4458 __( '[%s] Erasure Request Fulfilled' ),
4459 $email_data['sitename']
4460 );
4461
4462 /**
4463 * Filters the subject of the email sent when an erasure request is completed.
4464 *
4465 * @since 4.9.8
4466 * @deprecated 5.8.0 Use {@see 'user_erasure_fulfillment_email_subject'} instead.
4467 *
4468 * @param string $subject The email subject.
4469 * @param string $sitename The name of the site.
4470 * @param array $email_data {
4471 * Data relating to the account action email.
4472 *
4473 * @type WP_User_Request $request User request object.
4474 * @type string $message_recipient The address that the email will be sent to. Defaults
4475 * to the value of `$request->email`, but can be changed
4476 * by the `user_erasure_fulfillment_email_to` filter.
4477 * @type string $privacy_policy_url Privacy policy URL.
4478 * @type string $sitename The site name sending the mail.
4479 * @type string $siteurl The site URL sending the mail.
4480 * }
4481 */
4482 $subject = apply_filters_deprecated(
4483 'user_erasure_complete_email_subject',
4484 array( $subject, $email_data['sitename'], $email_data ),
4485 '5.8.0',
4486 'user_erasure_fulfillment_email_subject'
4487 );
4488
4489 /**
4490 * Filters the subject of the email sent when an erasure request is completed.
4491 *
4492 * @since 5.8.0
4493 *
4494 * @param string $subject The email subject.
4495 * @param string $sitename The name of the site.
4496 * @param array $email_data {
4497 * Data relating to the account action email.
4498 *
4499 * @type WP_User_Request $request User request object.
4500 * @type string $message_recipient The address that the email will be sent to. Defaults
4501 * to the value of `$request->email`, but can be changed
4502 * by the `user_erasure_fulfillment_email_to` filter.
4503 * @type string $privacy_policy_url Privacy policy URL.
4504 * @type string $sitename The site name sending the mail.
4505 * @type string $siteurl The site URL sending the mail.
4506 * }
4507 */
4508 $subject = apply_filters( 'user_erasure_fulfillment_email_subject', $subject, $email_data['sitename'], $email_data );
4509
4510 /* translators: Do not translate SITENAME, SITEURL; those are placeholders. */
4511 $content = __(
4512 'Howdy,
4513
4514Your request to erase your personal data on ###SITENAME### has been completed.
4515
4516If you have any follow-up questions or concerns, please contact the site administrator.
4517
4518Regards,
4519All at ###SITENAME###
4520###SITEURL###'
4521 );
4522
4523 if ( ! empty( $email_data['privacy_policy_url'] ) ) {
4524 /* translators: Do not translate SITENAME, SITEURL, PRIVACY_POLICY_URL; those are placeholders. */
4525 $content = __(
4526 'Howdy,
4527
4528Your request to erase your personal data on ###SITENAME### has been completed.
4529
4530If you have any follow-up questions or concerns, please contact the site administrator.
4531
4532For more information, you can also read our privacy policy: ###PRIVACY_POLICY_URL###
4533
4534Regards,
4535All at ###SITENAME###
4536###SITEURL###'
4537 );
4538 }
4539
4540 /**
4541 * Filters the body of the data erasure fulfillment notification.
4542 *
4543 * The email is sent to a user when their data erasure request is fulfilled
4544 * by an administrator.
4545 *
4546 * The following strings have a special meaning and will get replaced dynamically:
4547 *
4548 * - `###SITENAME###` The name of the site.
4549 * - `###PRIVACY_POLICY_URL###` Privacy policy page URL.
4550 * - `###SITEURL###` The URL to the site.
4551 *
4552 * @since 4.9.6
4553 * @deprecated 5.8.0 Use {@see 'user_erasure_fulfillment_email_content'} instead.
4554 * For user request confirmation email content
4555 * use {@see 'user_request_confirmed_email_content'} instead.
4556 *
4557 * @param string $content The email content.
4558 * @param array $email_data {
4559 * Data relating to the account action email.
4560 *
4561 * @type WP_User_Request $request User request object.
4562 * @type string $message_recipient The address that the email will be sent to. Defaults
4563 * to the value of `$request->email`, but can be changed
4564 * by the `user_erasure_fulfillment_email_to` filter.
4565 * @type string $privacy_policy_url Privacy policy URL.
4566 * @type string $sitename The site name sending the mail.
4567 * @type string $siteurl The site URL sending the mail.
4568 * }
4569 */
4570 $content = apply_filters_deprecated(
4571 'user_confirmed_action_email_content',
4572 array( $content, $email_data ),
4573 '5.8.0',
4574 sprintf(
4575 /* translators: 1 & 2: Deprecation replacement options. */
4576 __( '%1$s or %2$s' ),
4577 'user_erasure_fulfillment_email_content',
4578 'user_request_confirmed_email_content'
4579 )
4580 );
4581
4582 /**
4583 * Filters the body of the data erasure fulfillment notification.
4584 *
4585 * The email is sent to a user when their data erasure request is fulfilled
4586 * by an administrator.
4587 *
4588 * The following strings have a special meaning and will get replaced dynamically:
4589 *
4590 * - `###SITENAME###` The name of the site.
4591 * - `###PRIVACY_POLICY_URL###` Privacy policy page URL.
4592 * - `###SITEURL###` The URL to the site.
4593 *
4594 * @since 5.8.0
4595 *
4596 * @param string $content The email content.
4597 * @param array $email_data {
4598 * Data relating to the account action email.
4599 *
4600 * @type WP_User_Request $request User request object.
4601 * @type string $message_recipient The address that the email will be sent to. Defaults
4602 * to the value of `$request->email`, but can be changed
4603 * by the `user_erasure_fulfillment_email_to` filter.
4604 * @type string $privacy_policy_url Privacy policy URL.
4605 * @type string $sitename The site name sending the mail.
4606 * @type string $siteurl The site URL sending the mail.
4607 * }
4608 */
4609 $content = apply_filters( 'user_erasure_fulfillment_email_content', $content, $email_data );
4610
4611 $content = str_replace( '###SITENAME###', $email_data['sitename'], $content );
4612 $content = str_replace( '###PRIVACY_POLICY_URL###', $email_data['privacy_policy_url'], $content );
4613 $content = str_replace( '###SITEURL###', sanitize_url( $email_data['siteurl'] ), $content );
4614
4615 $headers = '';
4616
4617 /**
4618 * Filters the headers of the data erasure fulfillment notification.
4619 *
4620 * @since 5.4.0
4621 * @deprecated 5.8.0 Use {@see 'user_erasure_fulfillment_email_headers'} instead.
4622 *
4623 * @param string|array $headers The email headers.
4624 * @param string $subject The email subject.
4625 * @param string $content The email content.
4626 * @param int $request_id The request ID.
4627 * @param array $email_data {
4628 * Data relating to the account action email.
4629 *
4630 * @type WP_User_Request $request User request object.
4631 * @type string $message_recipient The address that the email will be sent to. Defaults
4632 * to the value of `$request->email`, but can be changed
4633 * by the `user_erasure_fulfillment_email_to` filter.
4634 * @type string $privacy_policy_url Privacy policy URL.
4635 * @type string $sitename The site name sending the mail.
4636 * @type string $siteurl The site URL sending the mail.
4637 * }
4638 */
4639 $headers = apply_filters_deprecated(
4640 'user_erasure_complete_email_headers',
4641 array( $headers, $subject, $content, $request_id, $email_data ),
4642 '5.8.0',
4643 'user_erasure_fulfillment_email_headers'
4644 );
4645
4646 /**
4647 * Filters the headers of the data erasure fulfillment notification.
4648 *
4649 * @since 5.8.0
4650 *
4651 * @param string|array $headers The email headers.
4652 * @param string $subject The email subject.
4653 * @param string $content The email content.
4654 * @param int $request_id The request ID.
4655 * @param array $email_data {
4656 * Data relating to the account action email.
4657 *
4658 * @type WP_User_Request $request User request object.
4659 * @type string $message_recipient The address that the email will be sent to. Defaults
4660 * to the value of `$request->email`, but can be changed
4661 * by the `user_erasure_fulfillment_email_to` filter.
4662 * @type string $privacy_policy_url Privacy policy URL.
4663 * @type string $sitename The site name sending the mail.
4664 * @type string $siteurl The site URL sending the mail.
4665 * }
4666 */
4667 $headers = apply_filters( 'user_erasure_fulfillment_email_headers', $headers, $subject, $content, $request_id, $email_data );
4668
4669 $email_sent = wp_mail( $user_email, $subject, $content, $headers );
4670
4671 if ( $switched_locale ) {
4672 restore_previous_locale();
4673 }
4674
4675 if ( $email_sent ) {
4676 update_post_meta( $request_id, '_wp_user_notified', true );
4677 }
4678}
4679
4680/**
4681 * Returns request confirmation message HTML.
4682 *
4683 * @since 4.9.6
4684 * @access private
4685 *
4686 * @param int $request_id The request ID being confirmed.
4687 * @return string The confirmation message.
4688 */
4689function _wp_privacy_account_request_confirmed_message( $request_id ) {
4690 $request = wp_get_user_request( $request_id );
4691
4692 $message = '<p class="success">' . __( 'Action has been confirmed.' ) . '</p>';
4693 $message .= '<p>' . __( 'The site administrator has been notified and will fulfill your request as soon as possible.' ) . '</p>';
4694
4695 if ( $request && in_array( $request->action_name, _wp_privacy_action_request_types(), true ) ) {
4696 if ( 'export_personal_data' === $request->action_name ) {
4697 $message = '<p class="success">' . __( 'Thanks for confirming your export request.' ) . '</p>';
4698 $message .= '<p>' . __( 'The site administrator has been notified. You will receive a link to download your export via email when they fulfill your request.' ) . '</p>';
4699 } elseif ( 'remove_personal_data' === $request->action_name ) {
4700 $message = '<p class="success">' . __( 'Thanks for confirming your erasure request.' ) . '</p>';
4701 $message .= '<p>' . __( 'The site administrator has been notified. You will receive an email confirmation when they erase your data.' ) . '</p>';
4702 }
4703 }
4704
4705 /**
4706 * Filters the message displayed to a user when they confirm a data request.
4707 *
4708 * @since 4.9.6
4709 *
4710 * @param string $message The message to the user.
4711 * @param int $request_id The ID of the request being confirmed.
4712 */
4713 $message = apply_filters( 'user_request_action_confirmed_message', $message, $request_id );
4714
4715 return $message;
4716}
4717
4718/**
4719 * Creates and logs a user request to perform a specific action.
4720 *
4721 * Requests are stored inside a post type named `user_request` since they can apply to both
4722 * users on the site, or guests without a user account.
4723 *
4724 * @since 4.9.6
4725 * @since 5.7.0 Added the `$status` parameter.
4726 *
4727 * @param string $email_address User email address. This can be the address of a registered
4728 * or non-registered user.
4729 * @param string $action_name Name of the action that is being confirmed. Required.
4730 * @param array $request_data Misc data you want to send with the verification request and pass
4731 * to the actions once the request is confirmed.
4732 * @param string $status Optional request status (pending or confirmed). Default 'pending'.
4733 * @return int|WP_Error Returns the request ID if successful, or a WP_Error object on failure.
4734 */
4735function wp_create_user_request( $email_address = '', $action_name = '', $request_data = array(), $status = 'pending' ) {
4736 $email_address = sanitize_email( $email_address );
4737 $action_name = sanitize_key( $action_name );
4738
4739 if ( ! is_email( $email_address ) ) {
4740 return new WP_Error( 'invalid_email', __( 'Invalid email address.' ) );
4741 }
4742
4743 if ( ! in_array( $action_name, _wp_privacy_action_request_types(), true ) ) {
4744 return new WP_Error( 'invalid_action', __( 'Invalid action name.' ) );
4745 }
4746
4747 if ( ! in_array( $status, array( 'pending', 'confirmed' ), true ) ) {
4748 return new WP_Error( 'invalid_status', __( 'Invalid request status.' ) );
4749 }
4750
4751 $user = get_user_by( 'email', $email_address );
4752 $user_id = $user && ! is_wp_error( $user ) ? $user->ID : 0;
4753
4754 // Check for duplicates.
4755 $requests_query = new WP_Query(
4756 array(
4757 'post_type' => 'user_request',
4758 'post_name__in' => array( $action_name ), // Action name stored in post_name column.
4759 'title' => $email_address, // Email address stored in post_title column.
4760 'post_status' => array(
4761 'request-pending',
4762 'request-confirmed',
4763 ),
4764 'fields' => 'ids',
4765 )
4766 );
4767
4768 if ( $requests_query->found_posts ) {
4769 return new WP_Error( 'duplicate_request', __( 'An incomplete personal data request for this email address already exists.' ) );
4770 }
4771
4772 $request_id = wp_insert_post(
4773 array(
4774 'post_author' => $user_id,
4775 'post_name' => $action_name,
4776 'post_title' => $email_address,
4777 'post_content' => wp_json_encode( $request_data ),
4778 'post_status' => 'request-' . $status,
4779 'post_type' => 'user_request',
4780 'post_date' => current_time( 'mysql', false ),
4781 'post_date_gmt' => current_time( 'mysql', true ),
4782 ),
4783 true
4784 );
4785
4786 return $request_id;
4787}
4788
4789/**
4790 * Gets action description from the name and return a string.
4791 *
4792 * @since 4.9.6
4793 *
4794 * @param string $action_name Action name of the request.
4795 * @return string Human readable action name.
4796 */
4797function wp_user_request_action_description( $action_name ) {
4798 switch ( $action_name ) {
4799 case 'export_personal_data':
4800 $description = __( 'Export Personal Data' );
4801 break;
4802 case 'remove_personal_data':
4803 $description = __( 'Erase Personal Data' );
4804 break;
4805 default:
4806 /* translators: %s: Action name. */
4807 $description = sprintf( __( 'Confirm the "%s" action' ), $action_name );
4808 break;
4809 }
4810
4811 /**
4812 * Filters the user action description.
4813 *
4814 * @since 4.9.6
4815 *
4816 * @param string $description The default description.
4817 * @param string $action_name The name of the request.
4818 */
4819 return apply_filters( 'user_request_action_description', $description, $action_name );
4820}
4821
4822/**
4823 * Send a confirmation request email to confirm an action.
4824 *
4825 * If the request is not already pending, it will be updated.
4826 *
4827 * @since 4.9.6
4828 *
4829 * @param int $request_id ID of the request created via wp_create_user_request().
4830 * @return true|WP_Error True on success, `WP_Error` on failure.
4831 */
4832function wp_send_user_request( $request_id ) {
4833 $request_id = absint( $request_id );
4834 $request = wp_get_user_request( $request_id );
4835
4836 if ( ! $request ) {
4837 return new WP_Error( 'invalid_request', __( 'Invalid personal data request.' ) );
4838 }
4839
4840 // Localize message content for user; fallback to site default for visitors.
4841 if ( ! empty( $request->user_id ) ) {
4842 $switched_locale = switch_to_user_locale( $request->user_id );
4843 } else {
4844 $switched_locale = switch_to_locale( get_locale() );
4845 }
4846
4847 /*
4848 * Generate the new user request key first, as it is used by both the $request
4849 * object and the confirm_url array.
4850 * See https://core.trac.wordpress.org/ticket/44940
4851 */
4852 $request->confirm_key = wp_generate_user_request_key( $request_id );
4853
4854 $email_data = array(
4855 'request' => $request,
4856 'email' => $request->email,
4857 'description' => wp_user_request_action_description( $request->action_name ),
4858 'confirm_url' => add_query_arg(
4859 array(
4860 'action' => 'confirmaction',
4861 'request_id' => $request_id,
4862 'confirm_key' => $request->confirm_key,
4863 ),
4864 wp_login_url()
4865 ),
4866 'sitename' => wp_specialchars_decode( get_option( 'blogname' ), ENT_QUOTES ),
4867 'siteurl' => home_url(),
4868 );
4869
4870 /* translators: Confirm privacy data request notification email subject. 1: Site title, 2: Name of the action. */
4871 $subject = sprintf( __( '[%1$s] Confirm Action: %2$s' ), $email_data['sitename'], $email_data['description'] );
4872
4873 /**
4874 * Filters the subject of the email sent when an account action is attempted.
4875 *
4876 * @since 4.9.6
4877 *
4878 * @param string $subject The email subject.
4879 * @param string $sitename The name of the site.
4880 * @param array $email_data {
4881 * Data relating to the account action email.
4882 *
4883 * @type WP_User_Request $request User request object.
4884 * @type string $email The email address this is being sent to.
4885 * @type string $description Description of the action being performed so the user knows what the email is for.
4886 * @type string $confirm_url The link to click on to confirm the account action.
4887 * @type string $sitename The site name sending the mail.
4888 * @type string $siteurl The site URL sending the mail.
4889 * }
4890 */
4891 $subject = apply_filters( 'user_request_action_email_subject', $subject, $email_data['sitename'], $email_data );
4892
4893 /* translators: Do not translate DESCRIPTION, CONFIRM_URL, SITENAME, SITEURL: those are placeholders. */
4894 $content = __(
4895 'Howdy,
4896
4897A request has been made to perform the following action on your account:
4898
4899 ###DESCRIPTION###
4900
4901To confirm this, please click on the following link:
4902###CONFIRM_URL###
4903
4904You can safely ignore and delete this email if you do not want to
4905take this action.
4906
4907Regards,
4908All at ###SITENAME###
4909###SITEURL###'
4910 );
4911
4912 /**
4913 * Filters the text of the email sent when an account action is attempted.
4914 *
4915 * The following strings have a special meaning and will get replaced dynamically:
4916 *
4917 * - `###DESCRIPTION###` Description of the action being performed so the user knows what the email is for.
4918 * - `###CONFIRM_URL###` The link to click on to confirm the account action.
4919 * - `###SITENAME###` The name of the site.
4920 * - `###SITEURL###` The URL to the site.
4921 *
4922 * @since 4.9.6
4923 *
4924 * @param string $content Text in the email.
4925 * @param array $email_data {
4926 * Data relating to the account action email.
4927 *
4928 * @type WP_User_Request $request User request object.
4929 * @type string $email The email address this is being sent to.
4930 * @type string $description Description of the action being performed so the user knows what the email is for.
4931 * @type string $confirm_url The link to click on to confirm the account action.
4932 * @type string $sitename The site name sending the mail.
4933 * @type string $siteurl The site URL sending the mail.
4934 * }
4935 */
4936 $content = apply_filters( 'user_request_action_email_content', $content, $email_data );
4937
4938 $content = str_replace( '###DESCRIPTION###', $email_data['description'], $content );
4939 $content = str_replace( '###CONFIRM_URL###', sanitize_url( $email_data['confirm_url'] ), $content );
4940 $content = str_replace( '###EMAIL###', $email_data['email'], $content );
4941 $content = str_replace( '###SITENAME###', $email_data['sitename'], $content );
4942 $content = str_replace( '###SITEURL###', sanitize_url( $email_data['siteurl'] ), $content );
4943
4944 $headers = '';
4945
4946 /**
4947 * Filters the headers of the email sent when an account action is attempted.
4948 *
4949 * @since 5.4.0
4950 *
4951 * @param string|array $headers The email headers.
4952 * @param string $subject The email subject.
4953 * @param string $content The email content.
4954 * @param int $request_id The request ID.
4955 * @param array $email_data {
4956 * Data relating to the account action email.
4957 *
4958 * @type WP_User_Request $request User request object.
4959 * @type string $email The email address this is being sent to.
4960 * @type string $description Description of the action being performed so the user knows what the email is for.
4961 * @type string $confirm_url The link to click on to confirm the account action.
4962 * @type string $sitename The site name sending the mail.
4963 * @type string $siteurl The site URL sending the mail.
4964 * }
4965 */
4966 $headers = apply_filters( 'user_request_action_email_headers', $headers, $subject, $content, $request_id, $email_data );
4967
4968 $email_sent = wp_mail( $email_data['email'], $subject, $content, $headers );
4969
4970 if ( $switched_locale ) {
4971 restore_previous_locale();
4972 }
4973
4974 if ( ! $email_sent ) {
4975 return new WP_Error( 'privacy_email_error', __( 'Unable to send personal data export confirmation email.' ) );
4976 }
4977
4978 return true;
4979}
4980
4981/**
4982 * Returns a confirmation key for a user action and stores the hashed version for future comparison.
4983 *
4984 * @since 4.9.6
4985 *
4986 * @param int $request_id Request ID.
4987 * @return string Confirmation key.
4988 */
4989function wp_generate_user_request_key( $request_id ) {
4990 // Generate something random for a confirmation key.
4991 $key = wp_generate_password( 20, false );
4992
4993 // Save the key, hashed.
4994 wp_update_post(
4995 array(
4996 'ID' => $request_id,
4997 'post_status' => 'request-pending',
4998 'post_password' => wp_fast_hash( $key ),
4999 )
5000 );
5001
5002 return $key;
5003}
5004
5005/**
5006 * Validates a user request by comparing the key with the request's key.
5007 *
5008 * @since 4.9.6
5009 *
5010 * @param int $request_id ID of the request being confirmed.
5011 * @param string $key Provided key to validate.
5012 * @return true|WP_Error True on success, WP_Error on failure.
5013 */
5014function wp_validate_user_request_key(
5015 $request_id,
5016 #[\SensitiveParameter]
5017 $key
5018) {
5019 $request_id = absint( $request_id );
5020 $request = wp_get_user_request( $request_id );
5021 $saved_key = $request->confirm_key;
5022 $key_request_time = $request->modified_timestamp;
5023
5024 if ( ! $request || ! $saved_key || ! $key_request_time ) {
5025 return new WP_Error( 'invalid_request', __( 'Invalid personal data request.' ) );
5026 }
5027
5028 if ( ! in_array( $request->status, array( 'request-pending', 'request-failed' ), true ) ) {
5029 return new WP_Error( 'expired_request', __( 'This personal data request has expired.' ) );
5030 }
5031
5032 if ( empty( $key ) ) {
5033 return new WP_Error( 'missing_key', __( 'The confirmation key is missing from this personal data request.' ) );
5034 }
5035
5036 /**
5037 * Filters the expiration time of confirm keys.
5038 *
5039 * @since 4.9.6
5040 *
5041 * @param int $expiration The expiration time in seconds.
5042 */
5043 $expiration_duration = (int) apply_filters( 'user_request_key_expiration', DAY_IN_SECONDS );
5044 $expiration_time = $key_request_time + $expiration_duration;
5045
5046 if ( ! wp_verify_fast_hash( $key, $saved_key ) ) {
5047 return new WP_Error( 'invalid_key', __( 'The confirmation key is invalid for this personal data request.' ) );
5048 }
5049
5050 if ( ! $expiration_time || time() > $expiration_time ) {
5051 return new WP_Error( 'expired_key', __( 'The confirmation key has expired for this personal data request.' ) );
5052 }
5053
5054 return true;
5055}
5056
5057/**
5058 * Returns the user request object for the specified request ID.
5059 *
5060 * @since 4.9.6
5061 *
5062 * @param int $request_id The ID of the user request.
5063 * @return WP_User_Request|false
5064 */
5065function wp_get_user_request( $request_id ) {
5066 $request_id = absint( $request_id );
5067 $post = get_post( $request_id );
5068
5069 if ( ! $post || 'user_request' !== $post->post_type ) {
5070 return false;
5071 }
5072
5073 return new WP_User_Request( $post );
5074}
5075
5076/**
5077 * Checks if Application Passwords is supported.
5078 *
5079 * Application Passwords is supported only by sites using SSL or local environments
5080 * but may be made available using the {@see 'wp_is_application_passwords_available'} filter.
5081 *
5082 * @since 5.9.0
5083 *
5084 * @return bool
5085 */
5086function wp_is_application_passwords_supported() {
5087 return is_ssl() || 'local' === wp_get_environment_type();
5088}
5089
5090/**
5091 * Checks if Application Passwords is globally available.
5092 *
5093 * By default, Application Passwords is available to all sites using SSL or to local environments.
5094 * Use the {@see 'wp_is_application_passwords_available'} filter to adjust its availability.
5095 *
5096 * @since 5.6.0
5097 *
5098 * @return bool
5099 */
5100function wp_is_application_passwords_available() {
5101 /**
5102 * Filters whether Application Passwords is available.
5103 *
5104 * @since 5.6.0
5105 *
5106 * @param bool $available True if available, false otherwise.
5107 */
5108 return apply_filters( 'wp_is_application_passwords_available', wp_is_application_passwords_supported() );
5109}
5110
5111/**
5112 * Checks if Application Passwords is available for a specific user.
5113 *
5114 * By default all users can use Application Passwords. Use {@see 'wp_is_application_passwords_available_for_user'}
5115 * to restrict availability to certain users.
5116 *
5117 * @since 5.6.0
5118 *
5119 * @param int|WP_User $user The user to check.
5120 * @return bool
5121 */
5122function wp_is_application_passwords_available_for_user( $user ) {
5123 if ( ! wp_is_application_passwords_available() ) {
5124 return false;
5125 }
5126
5127 if ( ! is_object( $user ) ) {
5128 $user = get_userdata( $user );
5129 }
5130
5131 if ( ! $user || ! $user->exists() ) {
5132 return false;
5133 }
5134
5135 /**
5136 * Filters whether Application Passwords is available for a specific user.
5137 *
5138 * @since 5.6.0
5139 *
5140 * @param bool $available True if available, false otherwise.
5141 * @param WP_User $user The user to check.
5142 */
5143 return apply_filters( 'wp_is_application_passwords_available_for_user', true, $user );
5144}
5145
5146/**
5147 * Registers the user meta property for persisted preferences.
5148 *
5149 * This property is used to store user preferences across page reloads and is
5150 * currently used by the block editor for preferences like 'fullscreenMode' and
5151 * 'fixedToolbar'.
5152 *
5153 * @since 6.1.0
5154 * @access private
5155 *
5156 * @global wpdb $wpdb WordPress database abstraction object.
5157 */
5158function wp_register_persisted_preferences_meta() {
5159 /*
5160 * Create a meta key that incorporates the blog prefix so that each site
5161 * on a multisite can have distinct user preferences.
5162 */
5163 global $wpdb;
5164 $meta_key = $wpdb->get_blog_prefix() . 'persisted_preferences';
5165
5166 register_meta(
5167 'user',
5168 $meta_key,
5169 array(
5170 'type' => 'object',
5171 'single' => true,
5172 'show_in_rest' => array(
5173 'name' => 'persisted_preferences',
5174 'type' => 'object',
5175 'schema' => array(
5176 'type' => 'object',
5177 'context' => array( 'edit' ),
5178 'properties' => array(
5179 '_modified' => array(
5180 'description' => __( 'The date and time the preferences were updated.' ),
5181 'type' => 'string',
5182 'format' => 'date-time',
5183 'readonly' => false,
5184 ),
5185 ),
5186 'additionalProperties' => true,
5187 ),
5188 ),
5189 )
5190 );
5191}
5192
5193/**
5194 * Sets the last changed time for the 'users' cache group.
5195 *
5196 * @since 6.3.0
5197 */
5198function wp_cache_set_users_last_changed() {
5199 wp_cache_set_last_changed( 'users' );
5200}
5201
5202/**
5203 * Checks if password reset is allowed for a specific user.
5204 *
5205 * @since 6.3.0
5206 *
5207 * @param int|WP_User $user The user to check.
5208 * @return bool|WP_Error True if allowed, false or WP_Error otherwise.
5209 */
5210function wp_is_password_reset_allowed_for_user( $user ) {
5211 if ( ! is_object( $user ) ) {
5212 $user = get_userdata( $user );
5213 }
5214
5215 if ( ! $user || ! $user->exists() ) {
5216 return false;
5217 }
5218 $allow = true;
5219 if ( is_multisite() && is_user_spammy( $user ) ) {
5220 $allow = false;
5221 }
5222
5223 /**
5224 * Filters whether to allow a password to be reset.
5225 *
5226 * @since 2.7.0
5227 *
5228 * @param bool $allow Whether to allow the password to be reset. Default true.
5229 * @param int $user_id The ID of the user attempting to reset a password.
5230 */
5231 return apply_filters( 'allow_password_reset', $allow, $user->ID );
5232}
5233
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