Passed
Push — master ( 850ae4...fa5c5e )
by Rahul
02:30
created

update_approved_new_user_email()   A

Complexity

Conditions 3
Paths 2

Size

Total Lines 19
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 8
CRAP Score 3.0123

Importance

Changes 0
Metric Value
cc 3
eloc 10
nc 2
nop 3
dl 0
loc 19
ccs 8
cts 9
cp 0.8889
crap 3.0123
rs 9.9332
c 0
b 0
f 0
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 WP_User;
16
17
const APPROVE_STATUS_NONCE = 'aj-user-approve';
18
const BLOCKED_STATUS_NONCE = 'aj-user-blocked';
19
20
/**
21
 * Hook up all the filters and actions.
22
 */
23
function bootstrap() {
24 1
	add_action( 'manage_users_columns', __NAMESPACE__ . '\\user_verification_column', 1 );
25
26 1
	add_filter( 'manage_users_custom_column', __NAMESPACE__ . '\\add_user_verification_status', 10, 3 );
27 1
	add_filter( 'user_row_actions', __NAMESPACE__ . '\\add_user_verification_action', 10, 2 );
28
29 1
	add_action( 'admin_action_aj_user_status', __NAMESPACE__ . '\\aj_user_status_update' );
30
31 1
	add_filter( 'wp_new_user_notification_email', __NAMESPACE__ . '\\update_approved_new_user_email', 50, 3 );
32
33 1
	add_filter( 'manage_users_extra_tablenav', __NAMESPACE__ . '\\add_user_status_filters', 10 );
34 1
	add_filter( 'users_list_table_query_args', __NAMESPACE__ . '\\filter_user_list_by_status', 100 );
35
36 1
}
37
38
/**
39
 * Add new column for users list page for user status.
40
 *
41
 * @param array $columns Columns list of user data.
42
 *
43
 * @return array
44
 */
45
function user_verification_column( $columns ) {
46
47 1
	$columns['aj_user_status'] = esc_html__( 'Status', 'user-approval' );
48
49 1
	return $columns;
50
}
51
52
/**
53
 * Add a user status to status column of respective user row.
54
 *
55
 * @param string $val Default value of column.
56
 * @param string $column_name Column id/name.
57
 * @param int    $user_id User id of respective user row.
58
 *
59
 * @return array|string
60
 */
61
function add_user_verification_status( $val, $column_name, $user_id ) {
62
63 2
	if ( 'aj_user_status' !== $column_name ) {
64
		return $val;
65
	}
66
67 2
	$user_status = get_user_meta( $user_id, 'aj_user_status', true ) ?: 'pending';
68 2
	$user        = get_userdata( $user_id );
69 2
	$user_status = ( ! empty( $user ) && in_array( get_default_user_role(), $user->roles, true ) ) ? $user_status : 'pre-approved';
70
71 2
	return get_user_status( $user_status );
72
}
73
74
/**
75
 * Add action links to respective user row to approve/block the user.
76
 *
77
 * @param array   $actions All action link lists.
78
 * @param WP_User $user WP_User object for respective user row.
79
 *
80
 * @return array
81
 */
82
function add_user_verification_action( $actions, $user ) {
83
84 1
	if ( ! in_array( get_default_user_role(), $user->roles, true ) ) {
85 1
		return $actions;
86
	}
87
88 1
	$user_current_status = get_user_meta( $user->ID, 'aj_user_status', true );
89
90
	$query_args = [
91 1
		'user'   => $user->ID,
92 1
		'action' => 'aj_user_status',
93 1
		'status' => 'approve',
94
	];
95
96
	// Approve action link.
97 1
	$approve_link = add_query_arg( $query_args );
98 1
	$approve_link = remove_query_arg( [ 'new_role' ], $approve_link );
99 1
	$approve_link = wp_nonce_url( $approve_link, APPROVE_STATUS_NONCE );
100
101 1
	$query_args['status'] = 'block';
102
103
	// Block action link.
104 1
	$block_link = add_query_arg( $query_args );
105 1
	$block_link = remove_query_arg( [ 'new_role' ], $block_link );
106 1
	$block_link = wp_nonce_url( $block_link, BLOCKED_STATUS_NONCE );
107
108 1
	$approve_action = sprintf(
109 1
		'<a href="%s">%s</a>',
110 1
		esc_url( $approve_link ),
111 1
		get_user_status( 'approved' )
0 ignored issues
show
Bug introduced by
It seems like get_user_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

111
		/** @scrutinizer ignore-type */ get_user_status( 'approved' )
Loading history...
112
	);
113
114 1
	$block_action = sprintf(
115 1
		'<a href="%s">%s</a>',
116 1
		esc_url( $block_link ),
117 1
		get_user_status( 'blocked' )
118
	);
119
120 1
	$actions = array_merge( $actions, [
121 1
		'approved' => $approve_action,
122 1
		'blocked'  => $block_action,
123
	] );
124
125 1
	if ( isset( $actions[ $user_current_status ] ) ) {
126 1
		unset( $actions[ $user_current_status ] );
127
	}
128
129 1
	return $actions;
130
}
131
132
/**
133
 * Update user status on post request.
134
 */
135
function aj_user_status_update() {
136 1
	$user_id = filter_input( INPUT_GET, 'user', FILTER_VALIDATE_INT );
137 1
	$status  = filter_input( INPUT_GET, 'status', FILTER_SANITIZE_STRING );
138
139 1
	$user        = get_userdata( $user_id );
0 ignored issues
show
Bug introduced by
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

139
	$user        = get_userdata( /** @scrutinizer ignore-type */ $user_id );
Loading history...
140 1
	$user_status = get_user_meta( $user_id, 'aj_user_status', true );
0 ignored issues
show
Bug introduced by
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

140
	$user_status = get_user_meta( /** @scrutinizer ignore-type */ $user_id, 'aj_user_status', true );
Loading history...
141
142
	if (
143 1
		! $user instanceof WP_User
144 1
		|| ! in_array( get_default_user_role(), $user->roles, true )
145 1
		|| ( $status === $user_status ) // Avoid any refresh page.
146 1
		|| ! current_user_can( 'list_users' )
147
	) {
148
		return;
149
	}
150
151 1
	$current_user_id = get_current_user_id();
152
153 1
	if ( 'approve' === $status && check_admin_referer( APPROVE_STATUS_NONCE ) ) {
154 1
		update_user_meta( $user_id, 'aj_user_status', 'approved' );
155 1
		wp_send_new_user_notifications( $user_id, 'user' );
156 1
		update_user_meta( $user_id, 'aj_user_verified_by', $current_user_id );
157 1
	} elseif ( 'block' === $status && check_admin_referer( BLOCKED_STATUS_NONCE ) ) {
158 1
		update_user_meta( $user_id, 'aj_user_status', 'blocked' );
159 1
		send_user_blocked_email( $user );
160 1
		update_user_meta( $user_id, 'aj_user_verified_by', $current_user_id );
161
	}
162 1
}
163
164
/**
165
 * Send a notification/email to approved user.
166
 *
167
 * @param array   $email_data {
168
 *     Used to build wp_mail().
169
 *
170
 *     @type string $to      The intended recipient - New user email address.
171
 *     @type string $subject The subject of the email.
172
 *     @type string $message The body of the email.
173
 *     @type string $headers The headers of the email.
174
 * }
175
 * @param WP_User $user     User object for new user.
176
 * @param string  $blogname The site title.
177
 *
178
 * @return array Updated email data for wp_mail.
179
 */
180
function update_approved_new_user_email( $email_data, $user, $blogname ) {
181
182 1
	if ( empty( $user ) || ! in_array( get_default_user_role(), $user->roles, true ) ) {
183
		return $email_data;
184
	}
185
186 1
	$message = sprintf(
187
		/* translators: %s: User login. */
188 1
		esc_html__( 'You have been approved to access %s', 'user-approval' ),
189
		$blogname
190
	);
191 1
	$message .= "\r\n\r\n";
192 1
	$message .= $email_data['message'];
193
194
	/* translators: Login details notification email subject. %s: Site title. */
195 1
	$email_data['subject'] = esc_html__( '[%s] Login Details [Account Approved]', 'user-approval' );
196 1
	$email_data['message'] = $message;
197
198 1
	return apply_filters( 'user_approval_approved_user_email_data', $email_data );
199
}
200
201
/**
202
 * Send a notification/email to blocked user.
203
 *
204
 * @param WP_User $user WP_User object.
205
 */
206
function send_user_blocked_email( $user ) {
207 1
	$blogname = wp_specialchars_decode( get_option( 'blogname' ), ENT_QUOTES );
0 ignored issues
show
Bug introduced by
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

207
	$blogname = wp_specialchars_decode( /** @scrutinizer ignore-type */ get_option( 'blogname' ), ENT_QUOTES );
Loading history...
208
209 1
	$message = sprintf(
210
		/* translators: %s: Site title. */
211 1
		esc_html__( 'You have been denied access to %s.', 'user-approval' ),
212
		$blogname
213
	);
214
215 1
	$subject = sprintf(
216
		/* translators: %s: Site title. */
217 1
		esc_html__( '[%s] Account Blocked.', 'user-approval' ),
218
		$blogname
219
	);
220
221
	$email_data = [
222 1
		'to'      => $user->user_email,
223 1
		'subject' => $subject,
224 1
		'message' => $message,
225 1
		'headers' => '',
226
	];
227
228 1
	$email_data = apply_filters( 'user_approval_blocked_user_email_data', $email_data );
229
230
	// phpcs:ignore WordPressVIPMinimum.VIP.RestrictedFunctions.wp_mail_wp_mail, WordPressVIPMinimum.Functions.RestrictedFunctions.wp_mail_wp_mail
231 1
	wp_mail(
232 1
		$email_data['to'],
233 1
		wp_specialchars_decode( $email_data['subject'] ),
234 1
		$email_data['message'],
235 1
		$email_data['headers']
236
	);
237 1
}
238
239
/**
240
 * Add a filter to list users based on user status.
241
 *
242
 * @param string $which The location of the extra table nav markup: 'top' or 'bottom'.
243
 */
244
function add_user_status_filters( $which ) {
245
246 1
	if ( 'top' !== $which ) {
247 1
		return;
248
	}
249
250 1
	$status          = filter_input( INPUT_GET, 'user_status', FILTER_SANITIZE_STRING );
251 1
	$all_user_status = get_user_status( 'all' );
252
253
	?>
254 1
	<div class="alignleft actions">
255
		<label for="filter-by-date" class="screen-reader-text">Filter by user status</label>
256
		<select name="user_status" id="filter-by-user-status">
257
			<?php
258 1
			printf(
259 1
				'<option %s value="%s">%s</option>',
260 1
				( 'all' === $status ) ? esc_html( 'selected="selected"' ) : '',
261 1
				esc_attr( 'all' ),
262 1
				esc_html__( 'All', 'user-approval' )
263
			);
264 1
			foreach ( $all_user_status as $value => $label ) {
265
266 1
				printf(
267 1
					'<option %s value="%s">%s</option>',
268 1
					( $value === $status ) ? esc_html( 'selected="selected"' ) : '',
269 1
					esc_attr( $value ),
270 1
					esc_html( $label )
271
				);
272
			}
273
			?>
274 1
		</select>
275
		<button type="submit" name="filter_action" class="button">Filter</button>
276
	</div>
277
	<?php
278 1
}
279
280
/**
281
 * Filter user list in WP Admin filter by user status.
282
 *
283
 * @param array $args Arguments passed to WP_User_Query to retrieve items for the current
284
 *                    users list table.
285
 *
286
 * @return array
287
 */
288
function filter_user_list_by_status( $args ) {
289
290 1
	$current_screen = get_current_screen();
0 ignored issues
show
Bug introduced by
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...
291
292 1
	$status = filter_input( INPUT_GET, 'user_status', FILTER_SANITIZE_STRING );
293
294
	if (
295 1
		! is_admin()
296 1
		|| empty( $current_screen )
297 1
		|| 'users' !== $current_screen->id
298 1
		|| empty( $status )
299 1
		|| is_array( get_user_status( $status ) )
300
	) {
301 1
		return $args;
302
	}
303
304 1
	if ( in_array( $status, [ 'blocked', 'approved' ], true ) ) {
305 1
		$args['meta_key']   = 'aj_user_status'; // phpcs:ignore WordPress.VIP.SlowDBQuery.slow_db_query_meta_key
306 1
		$args['meta_value'] = $status; // phpcs:ignore WordPress.VIP.SlowDBQuery.slow_db_query_meta_value, WordPress.DB.SlowDBQuery.slow_db_query_meta_value
307 1
	} elseif ( 'pending' === $status ) {
308 1
		$args['role']         = get_default_user_role();
309 1
		$args['meta_key']     = 'aj_user_status'; // phpcs:ignore WordPress.VIP.SlowDBQuery.slow_db_query_meta_key
310 1
		$args['meta_compare'] = 'NOT EXISTS';
311
	} else {
312 1
		$user_roles_data = get_pre_approved_user_roles();
313
314 1
		if ( ! empty( $user_roles_data ) ) {
315 1
			$args['role__in'] = array_keys( $user_roles_data );
316
		}
317
	}
318
319 1
	return $args;
320
}
321