Issues (19)

inc/management.php (12 issues)

1
<?php
2
/**
3
 * User account management - approval/block.
4
 *
5
 * @package user-approval
6
 */
7
8
namespace User_Approval\Management;
9
10
use function User_Approval\filter_input;
11
use function User_Approval\get_default_user_role;
12
use function User_Approval\get_pre_approved_user_roles;
13
use function User_Approval\get_user_status;
14
15
use const User_Approval\STATUS_APPROVED;
0 ignored issues
show
The constant User_Approval\STATUS_APPROVED was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
16
use const User_Approval\STATUS_APPROVED_NONCE;
0 ignored issues
show
The constant User_Approval\STATUS_APPROVED_NONCE was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
17
use const User_Approval\STATUS_BLOCKED;
0 ignored issues
show
The constant User_Approval\STATUS_BLOCKED was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
18
use const User_Approval\STATUS_BLOCKED_NONCE;
0 ignored issues
show
The constant User_Approval\STATUS_BLOCKED_NONCE was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
19
use const User_Approval\STATUS_META_KEY;
0 ignored issues
show
The constant User_Approval\STATUS_META_KEY was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
20
use const User_Approval\STATUS_PENDING;
0 ignored issues
show
The constant User_Approval\STATUS_PENDING was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
21
use const User_Approval\STATUS_PRE_APPROVED;
0 ignored issues
show
The constant User_Approval\STATUS_PRE_APPROVED was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
22
23
use WP_User;
24 1
25
/**
26 1
 * Hook up all the filters and actions.
27 1
 */
28
function bootstrap() {
29 1
	add_action( 'manage_users_columns', __NAMESPACE__ . '\\user_verification_column', 1 );
30
31 1
	add_filter( 'manage_users_custom_column', __NAMESPACE__ . '\\add_user_verification_status', 10, 3 );
32
	add_filter( 'user_row_actions', __NAMESPACE__ . '\\add_user_verification_action', 10, 2 );
33 1
34 1
	add_action( 'admin_action_aj_user_status', __NAMESPACE__ . '\\aj_user_status_update' );
35
36 1
	add_filter( 'wp_new_user_notification_email', __NAMESPACE__ . '\\update_approved_new_user_email', 50, 3 );
37
38
	add_filter( 'manage_users_extra_tablenav', __NAMESPACE__ . '\\add_user_status_filters', 10 );
39
	add_filter( 'users_list_table_query_args', __NAMESPACE__ . '\\filter_user_list_by_status', 100 );
40
41
}
42
43
/**
44
 * Add new column for users list page for user status.
45
 *
46
 * @param array $columns Columns list of user data.
47 1
 *
48
 * @return array
49 1
 */
50
function user_verification_column( $columns ) {
51
52
	$columns[ STATUS_META_KEY ] = esc_html__( 'Status', 'user-approval' );
53
54
	return $columns;
55
}
56
57
/**
58
 * Add a user status to status column of respective user row.
59
 *
60
 * @param string $val Default value of column.
61
 * @param string $column_name Column id/name.
62
 * @param int    $user_id User id of respective user row.
63 2
 *
64
 * @return array|string
65
 */
66
function add_user_verification_status( $val, $column_name, $user_id ) {
67 2
68 2
	if ( STATUS_META_KEY !== $column_name ) {
69 2
		return $val;
70
	}
71 2
72
	$user_status = get_user_meta( $user_id, STATUS_META_KEY, true ) ?: STATUS_PENDING;
73
	$user        = get_userdata( $user_id );
74
	$user_status = ( ! empty( $user ) && in_array( get_default_user_role(), $user->roles, true ) ) ? $user_status : STATUS_PRE_APPROVED;
75
76
	return get_user_status( $user_status );
77
}
78
79
/**
80
 * Add action links to respective user row to approve/block the user.
81
 *
82
 * @param array   $actions All action link lists.
83
 * @param WP_User $user WP_User object for respective user row.
84 1
 *
85 1
 * @return array
86
 */
87
function add_user_verification_action( $actions, $user ) {
88 1
89
	if ( ! in_array( get_default_user_role(), $user->roles, true ) ) {
90
		return $actions;
91 1
	}
92 1
93 1
	$user_current_status = get_user_meta( $user->ID, STATUS_META_KEY, true );
94
95
	$query_args = [
96
		'user'   => $user->ID,
97 1
		'action' => STATUS_META_KEY,
98 1
		'status' => STATUS_APPROVED,
99 1
	];
100
101 1
	// Approve action link.
102
	$approve_link = add_query_arg( $query_args );
103
	$approve_link = remove_query_arg( [ 'new_role' ], $approve_link );
104 1
	$approve_link = wp_nonce_url( $approve_link, STATUS_APPROVED_NONCE );
105 1
106 1
	$query_args['status'] = STATUS_BLOCKED;
107
108 1
	// Block action link.
109 1
	$block_link = add_query_arg( $query_args );
110 1
	$block_link = remove_query_arg( [ 'new_role' ], $block_link );
111 1
	$block_link = wp_nonce_url( $block_link, STATUS_BLOCKED_NONCE );
112
113
	$approve_action = sprintf(
114 1
		'<a href="%s">%s</a>',
115 1
		esc_url( $approve_link ),
116 1
		get_user_status( STATUS_APPROVED )
0 ignored issues
show
It seems like get_user_status(User_Approval\STATUS_APPROVED) can also be of type array; however, parameter $args of sprintf() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

116
		/** @scrutinizer ignore-type */ get_user_status( STATUS_APPROVED )
Loading history...
117 1
	);
118
119
	$block_action = sprintf(
120 1
		'<a href="%s">%s</a>',
121 1
		esc_url( $block_link ),
122 1
		get_user_status( STATUS_BLOCKED )
123
	);
124
125 1
	$actions = array_merge( $actions, [
126 1
		'approved' => $approve_action,
127
		'blocked'  => $block_action,
128
	] );
129 1
130
	if ( isset( $actions[ $user_current_status ] ) ) {
131
		unset( $actions[ $user_current_status ] );
132
	}
133
134
	return $actions;
135
}
136 1
137 1
/**
138
 * Update user status on post request.
139 1
 */
140 1
function aj_user_status_update() {
141
	$user_id = filter_input( INPUT_GET, 'user', FILTER_VALIDATE_INT );
142
	$status  = filter_input( INPUT_GET, 'status', FILTER_SANITIZE_STRING );
143 1
144 1
	$user        = get_userdata( $user_id );
0 ignored issues
show
It seems like $user_id can also be of type string; however, parameter $user_id of get_userdata() does only seem to accept integer, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

144
	$user        = get_userdata( /** @scrutinizer ignore-type */ $user_id );
Loading history...
145 1
	$user_status = get_user_meta( $user_id, STATUS_META_KEY, true );
0 ignored issues
show
It seems like $user_id can also be of type string; however, parameter $user_id of get_user_meta() does only seem to accept integer, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

145
	$user_status = get_user_meta( /** @scrutinizer ignore-type */ $user_id, STATUS_META_KEY, true );
Loading history...
146 1
147
	if (
148
		! $user instanceof WP_User
149
		|| ! in_array( get_default_user_role(), $user->roles, true )
150
		|| ( $status === $user_status ) // Avoid any refresh page.
151 1
		|| ! current_user_can( 'list_users' )
152
	) {
153 1
		return;
154 1
	}
155 1
156 1
	$current_user_id = get_current_user_id();
157 1
158 1
	if ( STATUS_APPROVED === $status && check_admin_referer( STATUS_APPROVED_NONCE ) ) {
159 1
		update_user_meta( $user_id, STATUS_META_KEY, STATUS_APPROVED );
160 1
		wp_send_new_user_notifications( $user_id, 'user' );
161
		update_user_meta( $user_id, 'aj_user_verified_by', $current_user_id );
162 1
	} elseif ( STATUS_BLOCKED === $status && check_admin_referer( STATUS_BLOCKED_NONCE ) ) {
163
		update_user_meta( $user_id, STATUS_META_KEY, STATUS_BLOCKED );
164
		send_user_blocked_email( $user );
165
		update_user_meta( $user_id, 'aj_user_verified_by', $current_user_id );
166
	}
167
}
168
169
/**
170
 * Send a notification/email to approved user.
171
 *
172
 * @param array   $email_data {
173
 *     Used to build wp_mail().
174
 *
175
 *     @type string $to      The intended recipient - New user email address.
176
 *     @type string $subject The subject of the email.
177
 *     @type string $message The body of the email.
178
 *     @type string $headers The headers of the email.
179
 * }
180
 * @param WP_User $user     User object for new user.
181
 * @param string  $blogname The site title.
182 1
 *
183
 * @return array Updated email data for wp_mail.
184
 */
185
function update_approved_new_user_email( $email_data, $user, $blogname ) {
186 1
187
	if ( empty( $user ) || ! in_array( get_default_user_role(), $user->roles, true ) ) {
188 1
		return $email_data;
189 1
	}
190
191 1
	$message = sprintf(
192 1
		/* translators: %s: User login. */
193
		esc_html__( 'You have been approved to access %s', 'user-approval' ),
194
		$blogname
195 1
	);
196 1
	$message .= "\r\n\r\n";
197
	$message .= $email_data['message'];
198 1
199
	/* translators: Login details notification email subject. %s: Site title. */
200
	$email_data['subject'] = esc_html__( '[%s] Login Details [Account Approved]', 'user-approval' );
201
	$email_data['message'] = $message;
202
203
	return apply_filters( 'user_approval_approved_user_email_data', $email_data );
204
}
205
206
/**
207 1
 * Send a notification/email to blocked user.
208
 *
209 1
 * @param WP_User $user WP_User object.
210
 */
211 1
function send_user_blocked_email( $user ) {
212 1
	$blogname = wp_specialchars_decode( get_option( 'blogname' ), ENT_QUOTES );
0 ignored issues
show
It seems like get_option('blogname') can also be of type false; however, parameter $string of wp_specialchars_decode() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

212
	$blogname = wp_specialchars_decode( /** @scrutinizer ignore-type */ get_option( 'blogname' ), ENT_QUOTES );
Loading history...
213
214
	$message = sprintf(
215 1
		/* translators: %s: Site title. */
216
		esc_html__( 'You have been denied access to %s.', 'user-approval' ),
217 1
		$blogname
218 1
	);
219
220
	$subject = sprintf(
221
		/* translators: %s: Site title. */
222 1
		esc_html__( '[%s] Account Blocked.', 'user-approval' ),
223 1
		$blogname
224 1
	);
225 1
226
	$email_data = [
227
		'to'      => $user->user_email,
228 1
		'subject' => $subject,
229
		'message' => $message,
230
		'headers' => '',
231 1
	];
232 1
233 1
	/**
234 1
	 * Filters the contents of account blocked notification email sent to the blogger.
235 1
	 *
236
	 * @param array   $email_data {
237 1
	 *     Used to build wp_mail().
238
	 *
239
	 *     @type string $to      The intended recipient - site admin email address.
240
	 *     @type string $subject The subject of the email.
241
	 *     @type string $message The body of the email.
242
	 *     @type string $headers The headers of the email.
243
	 * }
244
	 * @param WP_User $user     User object for new user.
245
	 * @param string  $blogname The site title.
246 1
	 */
247 1
	$email_data = apply_filters( 'user_approval_blocked_user_email_data', $email_data, $user, $blogname );
248
249
	// phpcs:ignore WordPressVIPMinimum.VIP.RestrictedFunctions.wp_mail_wp_mail, WordPressVIPMinimum.Functions.RestrictedFunctions.wp_mail_wp_mail
250 1
	wp_mail(
251 1
		$email_data['to'],
252
		wp_specialchars_decode( $email_data['subject'] ),
253
		$email_data['message'],
254 1
		$email_data['headers']
255
	);
256
}
257
258 1
/**
259 1
 * Add a filter to list users based on user status.
260 1
 *
261 1
 * @param string $which The location of the extra table nav markup: 'top' or 'bottom'.
262 1
 */
263
function add_user_status_filters( $which ) {
264 1
265
	if ( 'top' !== $which ) {
266 1
		return;
267 1
	}
268 1
269 1
	$status          = filter_input( INPUT_GET, 'user_status', FILTER_SANITIZE_STRING );
270 1
	$all_user_status = get_user_status( 'all' );
271
272
	?>
273
	<div class="alignleft actions">
274 1
		<label for="filter-by-date" class="screen-reader-text">Filter by user status</label>
275
		<select name="user_status" id="filter-by-user-status">
276
			<?php
277
			printf(
278 1
				'<option %s value="%s">%s</option>',
279
				( 'all' === $status ) ? esc_html( 'selected="selected"' ) : '',
280
				esc_attr( 'all' ),
281
				esc_html__( 'All', 'user-approval' )
282
			);
283
			foreach ( $all_user_status as $value => $label ) {
284
285
				printf(
286
					'<option %s value="%s">%s</option>',
287
					( $value === $status ) ? esc_html( 'selected="selected"' ) : '',
288
					esc_attr( $value ),
289
					esc_html( $label )
290 1
				);
291
			}
292 1
			?>
293
		</select>
294
		<button type="submit" name="filter_action" class="button"><?php esc_html_e( 'Filter', 'user-approval' ) ?></button>
295 1
	</div>
296 1
	<?php
297 1
}
298 1
299 1
/**
300
 * Filter user list in WP Admin filter by user status.
301 1
 *
302
 * @param array $args Arguments passed to WP_User_Query to retrieve items for the current
303
 *                    users list table.
304 1
 *
305 1
 * @return array
306 1
 */
307 1
function filter_user_list_by_status( $args ) {
308 1
309 1
	$current_screen = get_current_screen();
0 ignored issues
show
Are you sure the assignment to $current_screen is correct as get_current_screen() seems to always return null.

This check looks for function or method calls that always return null and whose return value is assigned to a variable.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
$object = $a->getObject();

The method getObject() can return nothing but null, so it makes no sense to assign that value to a variable.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
310 1
311
	$status = filter_input( INPUT_GET, 'user_status', FILTER_SANITIZE_STRING );
312 1
313
	if (
314 1
		! is_admin()
315 1
		|| empty( $current_screen )
316
		|| 'users' !== $current_screen->id
317
		|| empty( $status )
318
		|| is_array( get_user_status( $status ) )
319 1
	) {
320
		return $args;
321
	}
322
323
	if ( in_array( $status, [ STATUS_APPROVED, STATUS_BLOCKED ], true ) ) {
324
		$args['meta_key']   = STATUS_META_KEY; // phpcs:ignore WordPress.VIP.SlowDBQuery.slow_db_query_meta_key
325
		$args['meta_value'] = $status; // phpcs:ignore WordPress.VIP.SlowDBQuery.slow_db_query_meta_value, WordPress.DB.SlowDBQuery.slow_db_query_meta_value
326
	} elseif ( STATUS_PENDING === $status ) {
327
		$args['role']         = get_default_user_role();
328
		$args['meta_key']     = STATUS_META_KEY; // phpcs:ignore WordPress.VIP.SlowDBQuery.slow_db_query_meta_key
329
		$args['meta_compare'] = 'NOT EXISTS';
330
	} else {
331
		$user_roles_data = get_pre_approved_user_roles();
332
333
		if ( ! empty( $user_roles_data ) ) {
334
			$args['role__in'] = array_keys( $user_roles_data );
335
		}
336
	}
337
338
	return $args;
339
}
340