From 767384f36baac2fc87cb3496c5dd98dd80bf46dc Mon Sep 17 00:00:00 2001 From: Konstantin Obenland Date: Mon, 8 Jul 2024 20:12:50 -0500 Subject: [PATCH] Switch to three-state approach (#31) * # * Switch to three-state approach * Update readme.txt * Use wp_parse_url * Update test-user-meta.php --- class-obenland-wp-approve-user.php | 115 +++++++++++++++++++++-------- css/settings-page.css | 1 - js/wp-approve-user.js | 38 ++++++++-- js/wp-approve-user.min.js | 2 +- readme.txt | 1 + tests/test-user-meta.php | 12 +-- 6 files changed, 123 insertions(+), 46 deletions(-) diff --git a/class-obenland-wp-approve-user.php b/class-obenland-wp-approve-user.php index 98a5f82..ab58ae0 100644 --- a/class-obenland-wp-approve-user.php +++ b/class-obenland-wp-approve-user.php @@ -32,6 +32,15 @@ class Obenland_Wp_Approve_User extends Obenland_Wp_Plugins_V5 { */ protected $options; + /** + * Users flagged as pending. + * + * @since 12 + * + * @var int + */ + protected $pending_count = 0; + /** * Users flagged as unapproved. * @@ -43,6 +52,15 @@ class Obenland_Wp_Approve_User extends Obenland_Wp_Plugins_V5 { */ protected $unapproved_users = array(); + /** + * Number of unapproved users. + * + * @since 12 + * + * @var int + */ + protected $unapproved_count = 0; + /** * Constructor * @@ -66,16 +84,22 @@ public function __construct() { ); if ( is_admin() ) { + /** + * Get all users where wp-approve-user meta value is false or doesn't exist. + */ $args = array( + 'fields' => 'ID', 'meta_key' => 'wp-approve-user', //phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_key - 'meta_value' => false, //phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_value + 'meta_value' => 'pending', //phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_value ); if ( is_multisite() ) { $args['blog_id'] = is_network_admin() ? 0 : get_current_blog_id(); } + $this->pending_count = count( get_users( $args ) ); - $this->unapproved_users = get_users( $args ); + $args['meta_value'] = 'unapproved'; //phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_value + $this->unapproved_count = count( get_users( $args ) ); } load_plugin_textdomain( 'wp-approve-user', false, 'wp-approve-user/lang' ); @@ -201,18 +225,29 @@ public function admin_print_styles_settings_page_wp_approve_user() { * @return array */ public function views_users( $views ) { - if ( $this->unapproved_users ) { - // phpcs:ignore WordPress.Security.NonceVerification - $site_id = isset( $_REQUEST['id'] ) ? intval( $_REQUEST['id'] ) : 0; - $url = 'site-users-network' === get_current_screen()->id ? add_query_arg( array( 'id' => $site_id ), 'site-users.php' ) : 'users.php'; + // phpcs:ignore WordPress.Security.NonceVerification + $site_id = isset( $_REQUEST['id'] ) ? intval( $_REQUEST['id'] ) : 0; + $url = 'site-users-network' === get_current_screen()->id ? add_query_arg( array( 'id' => $site_id ), 'site-users.php' ) : 'users.php'; + if ( $this->pending_count ) { + $views['pending'] = sprintf( + '%3$s (%4$s)', + esc_url( add_query_arg( array( 'role' => 'wpau_pending' ), $url ) ), + // phpcs:ignore WordPress.Security.NonceVerification.Recommended + 'wpau_pending' === $this->get_role() ? 'current' : '', + esc_html__( 'Pending', 'wp-approve-users' ), + $this->pending_count + ); + } + + if ( $this->unapproved_count ) { $views['unapproved'] = sprintf( '%3$s (%4$s)', esc_url( add_query_arg( array( 'role' => 'wpau_unapproved' ), $url ) ), // phpcs:ignore WordPress.Security.NonceVerification.Recommended 'wpau_unapproved' === $this->get_role() ? 'current' : '', esc_html__( 'Unapproved', 'wp-approve-users' ), - count( $this->unapproved_users ) + $this->unapproved_count ); } @@ -232,11 +267,21 @@ public function pre_user_query( $query ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended, WordPress.Security.ValidatedSanitizedInput $role = empty( $query->query_vars['role'] ) && isset( $_REQUEST['role'] ) ? $_REQUEST['role'] : $query->query_vars['role']; + if ( 'wpau_pending' === $role ) { + unset( $query->query_vars['meta_query'] ); + $query->query_vars['role'] = ''; + $query->query_vars['meta_key'] = 'wp-approve-user'; //phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_key + $query->query_vars['meta_value'] = 'pending'; //phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_value + + remove_filter( 'pre_user_query', array( $this, 'pre_user_query' ) ); + $query->prepare_query(); + } + if ( 'wpau_unapproved' === $role ) { unset( $query->query_vars['meta_query'] ); $query->query_vars['role'] = ''; $query->query_vars['meta_key'] = 'wp-approve-user'; //phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_key - $query->query_vars['meta_value'] = false; //phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_value + $query->query_vars['meta_value'] = 'unapproved'; //phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_value remove_filter( 'pre_user_query', array( $this, 'pre_user_query' ) ); $query->prepare_query(); @@ -260,34 +305,38 @@ public function user_row_actions( $actions, $user_object ) { $site_id = isset( $_REQUEST['id'] ) ? intval( $_REQUEST['id'] ) : 0; $url = 'site-users-network' === get_current_screen()->id ? add_query_arg( array( 'id' => $site_id ), 'site-users.php' ) : 'users.php'; - if ( get_user_meta( $user_object->ID, 'wp-approve-user', true ) ) { - $url = wp_nonce_url( + $status = get_user_meta( $user_object->ID, 'wp-approve-user', true ); + + if ( 'approved' !== $status ) { + $unapprove_url = wp_nonce_url( add_query_arg( array( - 'action' => 'wpau_unapprove', + 'action' => 'wpau_approve', 'user' => $user_object->ID, + 'role' => $this->get_role(), ), $url ), - 'wpau-unapprove-users' + 'wpau-approve-users' ); - $actions['wpau-unapprove'] = sprintf( '%2$s', esc_url( $url ), esc_html__( 'Unapprove', 'wp-approve-user' ) ); + $actions['wpau-approve'] = sprintf( '%2$s', esc_url( $unapprove_url ), esc_html__( 'Approve', 'wp-approve-user' ) ); + } - } else { - $url = wp_nonce_url( + if ( 'unapproved' !== $status ) { + $approve_url = wp_nonce_url( add_query_arg( array( - 'action' => 'wpau_approve', + 'action' => 'wpau_unapprove', 'user' => $user_object->ID, 'role' => $this->get_role(), ), $url ), - 'wpau-approve-users' + 'wpau-unapprove-users' ); - $actions['wpau-approve'] = sprintf( '%2$s', esc_url( $url ), esc_html__( 'Approve', 'wp-approve-user' ) ); + $actions['wpau-unapprove'] = sprintf( '%2$s', esc_url( $approve_url ), esc_html__( 'Unapprove', 'wp-approve-user' ) ); } } @@ -317,7 +366,7 @@ public function wp_authenticate_user( $userdata ) { return $userdata; } - if ( get_user_meta( $userdata->ID, 'wp-approve-user', true ) ) { + if ( 'approved' === get_user_meta( $userdata->ID, 'wp-approve-user', true ) ) { return $userdata; } @@ -337,13 +386,18 @@ public function wp_authenticate_user( $userdata ) { * @param int $id User ID. */ public function user_register( $id ) { - update_user_meta( $id, 'wp-approve-user', current_user_can( 'create_users' ) ); + $status = current_user_can( 'create_users' ) ? 'approved' : 'pending'; + + update_user_meta( $id, 'wp-approve-user', $status ); update_user_meta( $id, 'wp-approve-user-new-registration', true ); } /** * Fires after a new user registration has been recorded. * + * Prevents WordPress to send the new user notification email, if the user has to be approved first. + * Still notifies the admin about the new user registration. + * * @author Konstantin Obenland * @since 6 - 04.03.2019 * @access public @@ -351,7 +405,7 @@ public function user_register( $id ) { * @param int $user_id ID of the newly registered user. */ public function register_new_user( $user_id ) { - if ( ! get_user_meta( $user_id, 'wp-approve-user', true ) ) { + if ( 'pending' === get_user_meta( $user_id, 'wp-approve-user', true ) ) { remove_action( 'register_new_user', 'wp_send_new_user_notifications' ); add_action( 'register_new_user', 'wp_new_user_notification' ); } @@ -372,6 +426,8 @@ public function map_action2() { } // phpcs:enable WordPress.Security.NonceVerification.Recommended, WordPress.Security.ValidatedSanitizedInput + + wp_add_inline_style( 'list-tables', '.wp-list-table.users tbody th, .wp-list-table.users tbody td { box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.1); } #the-list .submitapprove { color:#007017; } #the-list .submitunapprove { color:#996800; }' ); } /** @@ -524,12 +580,8 @@ public function admin_menu() { foreach ( $menu as $key => $menu_item ) { if ( array_search( 'users.php', $menu_item, true ) ) { - - // No need for number formatting, count() always returns an integer. - $awaiting_mod = count( $this->unapproved_users ); - // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited - $menu[ $key ][0] .= " {$awaiting_mod}"; + $menu[ $key ][0] .= " {$this->unapproved_count}"; break; // Bail on success. } @@ -774,9 +826,9 @@ public function wpau_approve( $user_id ) { */ public function delete_user( $user_id ) { $is_new_registration = get_user_meta( $user_id, 'wp-approve-user-new-registration', true ); - $is_approved = get_user_meta( $user_id, 'wp-approve-user', true ); + $is_unapproved = 'unapproved' === get_user_meta( $user_id, 'wp-approve-user', true ); - if ( $is_new_registration && ! $is_approved && $this->options['wpau-send-unapprove-email'] ) { + if ( $is_new_registration && $is_unapproved && $this->options['wpau-send-unapprove-email'] ) { $user = new WP_User( $user_id ); $blogname = wp_specialchars_decode( get_option( 'blogname' ), ENT_QUOTES ); @@ -826,7 +878,7 @@ protected function approve() { ); } - update_user_meta( $id, 'wp-approve-user', true ); + update_user_meta( $id, 'wp-approve-user', 'approved' ); /** * Fires after a user has been approved. @@ -875,7 +927,7 @@ protected function unapprove() { ); } - update_user_meta( $id, 'wp-approve-user', false ); + update_user_meta( $id, 'wp-approve-user', 'unapproved' ); WP_Session_Tokens::get_instance( $id )->destroy_all(); /** @@ -1036,7 +1088,7 @@ protected function set_up_role_context() { // phpcs:disable WordPress.Security.NonceVerification.Recommended, WordPress.Security.ValidatedSanitizedInput if ( empty( $_REQUEST['role'] ) && ! empty( $_REQUEST['_wp_http_referer'] ) ) { - $referrer = parse_url( $_REQUEST['_wp_http_referer'] ); // phpcs:ignore WordPress.WP.AlternativeFunctions.parse_url_parse_url + $referrer = wp_parse_url( $_REQUEST['_wp_http_referer'] ); if ( ! empty( $referrer['query'] ) ) { $args = wp_parse_args( $referrer['query'] ); @@ -1064,6 +1116,7 @@ protected function set_up_role_context() { protected function get_role() { $roles = array_keys( get_editable_roles() ); $roles[] = 'wpau_unapproved'; + $roles[] = 'wpau_pending'; $role = false; if ( isset( $_REQUEST['role'] ) && in_array( $_REQUEST['role'], $roles, true ) ) { diff --git a/css/settings-page.css b/css/settings-page.css index d7698c1..c887f0d 100644 --- a/css/settings-page.css +++ b/css/settings-page.css @@ -55,4 +55,3 @@ label.image-radio-option input { float: none !important; } } - diff --git a/js/wp-approve-user.js b/js/wp-approve-user.js index 4575f38..63c161b 100644 --- a/js/wp-approve-user.js +++ b/js/wp-approve-user.js @@ -1,7 +1,31 @@ -jQuery(function($){ - $('tr:has(.submitapprove)').css('background-color', '#FFFFE0'); - $('.actions select[name^="action"]').append( - '' + - '' - ); -}); \ No newline at end of file +document.addEventListener('DOMContentLoaded', function() { + // Select all table rows + document.querySelectorAll('tr').forEach(function(row) { + // Check if the row has an element with class 'submitapprove' + let hasSubmitApprove = row.querySelector('.submitapprove') !== null; + // Check if the row has an element with class 'submitunapprove' + let hasSubmitUnapprove = row.querySelector('.submitunapprove') !== null; + + // If the row has both 'submitapprove' and 'submitunapprove' elements, change background color to '#FFFFE0' + if (hasSubmitApprove && hasSubmitUnapprove) { + row.style.backgroundColor = '#FFFFE0'; + } + // If the row has only 'submitapprove' element, change background color to '#FAAFAA' + else if (hasSubmitApprove) { + row.style.backgroundColor = '#FAAFAA'; + } + }); + + // Append options to the select elements with name starting with 'action' inside elements with class 'actions' + document.querySelectorAll('.actions select[name^="action"]').forEach(function(select) { + var optionApprove = document.createElement('option'); + optionApprove.value = 'wpau_bulk_approve'; + optionApprove.textContent = wp_approve_user.approve; + select.appendChild(optionApprove); + + var optionUnapprove = document.createElement('option'); + optionUnapprove.value = 'wpau_bulk_unapprove'; + optionUnapprove.textContent = wp_approve_user.unapprove; + select.appendChild(optionUnapprove); + }); +}); diff --git a/js/wp-approve-user.min.js b/js/wp-approve-user.min.js index b125728..9ed8ead 100644 --- a/js/wp-approve-user.min.js +++ b/js/wp-approve-user.min.js @@ -1 +1 @@ -jQuery(function($){$("tr:has(.submitapprove)").css("background-color","#FFFFE0"),$('.actions select[name^="action"]').append('")}); \ No newline at end of file +document.addEventListener('DOMContentLoaded',function(){document.querySelectorAll('tr').forEach(function(e){let t=null!==e.querySelector('.submitapprove'),o=null!==e.querySelector('.submitunapprove');t&&o?e.style.backgroundColor='#FFFFE0':t&&(e.style.backgroundColor='#FAAFAA')});document.querySelectorAll('.actions select[name^="action"]').forEach(function(e){var t=document.createElement('option');t.value='wpau_bulk_approve',t.textContent=wp_approve_user.approve,e.appendChild(t);var o=document.createElement('option');o.value='wpau_bulk_unapprove',o.textContent=wp_approve_user.unapprove,e.appendChild(o)})}); diff --git a/readme.txt b/readme.txt index 5653cf3..ba2f646 100644 --- a/readme.txt +++ b/readme.txt @@ -78,6 +78,7 @@ Yes! Under Settings > Approve User, you can choose when to send an email and cus = 12 = * Bumped minimum required WordPress version to 4.7. +* Switches to a three-state approval system: approved, unapproved, and pending. * When a user is unapproved, they now get immediately logged out from all active sessions. * Uses a cron job to auto-approve more than 100 users asynchronously after plugin activation. diff --git a/tests/test-user-meta.php b/tests/test-user-meta.php index 6f975c7..5d572a6 100644 --- a/tests/test-user-meta.php +++ b/tests/test-user-meta.php @@ -63,7 +63,7 @@ public function test_user_register_admin_single_site() { $class->user_register( $user_id ); - $this->assertSame( '1', get_user_meta( $user_id, 'wp-approve-user', true ) ); + $this->assertSame( 'approved', get_user_meta( $user_id, 'wp-approve-user', true ) ); $this->assertSame( '1', get_user_meta( $user_id, 'wp-approve-user-new-registration', true ) ); } @@ -80,7 +80,7 @@ public function test_user_register_admin_multisite() { $class->user_register( $user_id ); - $this->assertEmpty( get_user_meta( $user_id, 'wp-approve-user', true ) ); + $this->assertSame( 'pending', get_user_meta( $user_id, 'wp-approve-user', true ) ); $this->assertSame( '1', get_user_meta( $user_id, 'wp-approve-user-new-registration', true ) ); } @@ -97,7 +97,7 @@ public function test_user_register_subscriber() { $class->user_register( $user->ID ); - $this->assertEmpty( get_user_meta( $user->ID, 'wp-approve-user', true ) ); + $this->assertSame( 'pending', get_user_meta( $user->ID, 'wp-approve-user', true ) ); $this->assertSame( '1', get_user_meta( $user->ID, 'wp-approve-user-new-registration', true ) ); } @@ -132,7 +132,7 @@ public function test_wp_authenticate_user_simple_site() { $class = new Obenland_Wp_Approve_User(); // Returns WP_User for admins, even if they're unapproved. - update_user_meta( static::$admin->ID, 'wp-approve-user', false ); + update_user_meta( static::$admin->ID, 'wp-approve-user', 'unapproved' ); $result = $class->wp_authenticate_user( static::$admin ); $this->assertSame( static::$admin, $result ); } @@ -147,7 +147,7 @@ public function test_wp_authenticate_user_multisite() { $class = new Obenland_Wp_Approve_User(); // Returns WP_Error for admins when they're unapproved. - update_user_meta( static::$admin->ID, 'wp-approve-user', false ); + update_user_meta( static::$admin->ID, 'wp-approve-user', 'unapproved' ); $result = $class->wp_authenticate_user( static::$admin ); $this->assertWPError( $result ); $this->assertSame( 'wpau_confirmation_error', $result->get_error_code() ); @@ -155,7 +155,7 @@ public function test_wp_authenticate_user_multisite() { // Returns WP_User for super admins, even if they're unapproved. $user = static::factory()->user->create_and_get( array( 'role' => 'subscriber' ) ); grant_super_admin( $user->ID ); - update_user_meta( $user->ID, 'wp-approve-user', false ); + update_user_meta( $user->ID, 'wp-approve-user', 'unapproved' ); $result = $class->wp_authenticate_user( $user ); $this->assertSame( $user, $result );