From 08a7b48d53afa8908d688f051efa753d48ce527c Mon Sep 17 00:00:00 2001 From: ashubawork Date: Mon, 7 Apr 2025 15:01:07 +0300 Subject: [PATCH 1/5] - add schedule action for account_status check --- includes/common/class-users.php | 63 +++++++++++++++++++++++++++++++++ 1 file changed, 63 insertions(+) diff --git a/includes/common/class-users.php b/includes/common/class-users.php index e6fcaa5e..a2c2ad83 100644 --- a/includes/common/class-users.php +++ b/includes/common/class-users.php @@ -4,6 +4,7 @@ namespace um\common; use WP_Error; use WP_Session_Tokens; use WP_User; +use WP_User_Query; if ( ! defined( 'ABSPATH' ) ) { exit; @@ -19,6 +20,68 @@ class Users { public function hooks() { add_filter( 'user_has_cap', array( &$this, 'map_caps_by_role' ), 10, 3 ); add_filter( 'editable_roles', array( &$this, 'restrict_roles' ) ); + + add_action( 'init', array( &$this, 'um_schedule_account_status_check' ) ); + add_action( 'um_schedule_account_status_check', array( &$this, 'status_check' ) ); + add_action( 'um_check_account_status_batch', array( &$this, 'batch_check' ), 10, 2 ); + } + + public function um_schedule_account_status_check() { + $interval = 3600; + + if ( ! as_next_scheduled_action( 'um_schedule_account_status_check' ) ) { + UM()->maybe_action_scheduler()->schedule_recurring_action( + time() + 60, + $interval, + 'um_schedule_account_status_check' + ); + } + } + + public function status_check() { + global $wpdb; + $batch_size = 50; + $total_users = $wpdb->get_var( + "SELECT COUNT(u.ID) + FROM {$wpdb->users} u + LEFT JOIN {$wpdb->usermeta} um ON u.ID = um.user_id AND um.meta_key = 'account_status' + WHERE um.meta_value IS NULL OR um.meta_value = ''" + ); + + if ( absint( $total_users ) > 0 ) { + for ( $offset = 0; $offset < absint( $total_users ); $offset += $batch_size ) { + UM()->maybe_action_scheduler()->enqueue_async_action( 'um_check_account_status_batch', array( $offset, $batch_size ) ); + } + } + } + + public function batch_check( $offset, $limit ) { + $users = new WP_User_Query( + array( + 'number' => $limit, + 'offset' => $offset, + 'fields' => array( 'ID' ), + 'meta_query' => array( + 'relation' => 'OR', + array( + 'key' => 'account_status', + 'compare' => 'NOT EXISTS', + ), + array( + 'key' => 'account_status', + 'value' => '', + 'compare' => '=', + ), + ), + ) + ); + $results = $users->get_results(); + + if ( ! empty( $results ) ) { + foreach ( $results as $user ) { + update_user_meta( $user->ID, 'account_status', 'approved' ); + } + } } /** From c8bd79fcebc0c604a80f2ad614ebdfd893091589 Mon Sep 17 00:00:00 2001 From: Mykyta Synelnikov Date: Mon, 14 Apr 2025 23:12:29 +0300 Subject: [PATCH 2/5] Add scheduled user account status check and improve approval Introduce a new `Users` class to handle scheduled tasks for user status checks and batch processing. Refactor user approval functionality to allow silent operations and avoid sending notifications where unnecessary. Enhance user registration to prevent unfinished registrations from being processed in scheduled checks. --- includes/common/actions/class-users.php | 113 ++++++++++++++++++++++++ includes/common/class-init.php | 4 + includes/common/class-users.php | 99 +++++---------------- includes/core/um-actions-register.php | 7 ++ 4 files changed, 144 insertions(+), 79 deletions(-) create mode 100644 includes/common/actions/class-users.php diff --git a/includes/common/actions/class-users.php b/includes/common/actions/class-users.php new file mode 100644 index 00000000..dae0ce7d --- /dev/null +++ b/includes/common/actions/class-users.php @@ -0,0 +1,113 @@ +maybe_action_scheduler()->next_scheduled_action( self::SCHEDULE_ACTION ) ) { + return; + } + + UM()->maybe_action_scheduler()->schedule_recurring_action( + time() + 60, + self::INTERVAL, + self::SCHEDULE_ACTION + ); + } + + public function status_check() { + global $wpdb; + $total_users = $wpdb->get_var( + "SELECT COUNT(u.ID) + FROM {$wpdb->users} u + LEFT JOIN {$wpdb->usermeta} um ON u.ID = um.user_id AND um.meta_key = 'account_status' + LEFT JOIN {$wpdb->usermeta} um2 ON u.ID = um2.user_id AND um2.meta_key = 'um_registration_in_progress' + WHERE ( um.meta_value IS NULL OR um.meta_value = '' ) AND + um2.meta_value IS NULL OR um2.meta_value != '1'" + ); + $total_users = absint( $total_users ); + + if ( empty( $total_users ) ) { + return; + } + + for ( $offset = 0; $offset < $total_users; $offset += self::BATCH_SIZE ) { + UM()->maybe_action_scheduler()->enqueue_async_action( self::BATCH_ACTION, array( $offset ) ); + } + } + + public function batch_check( $offset ) { + $users = new WP_User_Query( + array( + 'number' => self::BATCH_SIZE, + 'offset' => $offset, + 'fields' => 'ids', + 'meta_query' => array( + 'relation' => 'AND', + array( + 'relation' => 'OR', + array( + 'key' => 'um_registration_in_progress', + 'compare' => 'NOT EXISTS', + ), + array( + 'key' => 'um_registration_in_progress', + 'value' => '1', + 'compare' => '!=', + ), + ), + array( + 'relation' => 'OR', + array( + 'key' => 'account_status', + 'compare' => 'NOT EXISTS', + ), + array( + 'key' => 'account_status', + 'value' => '', + 'compare' => '=', + ), + ), + ), + ) + ); + + $results = $users->get_results(); + + if ( ! empty( $results ) ) { + foreach ( $results as $user_id ) { + UM()->common()->users()->approve( $user_id, true, true ); + } + } + } + } +} diff --git a/includes/common/class-init.php b/includes/common/class-init.php index 73cf4805..2ceff24f 100644 --- a/includes/common/class-init.php +++ b/includes/common/class-init.php @@ -39,6 +39,10 @@ if ( ! class_exists( 'um\common\Init' ) ) { if ( empty( UM()->classes['um\common\actions\emails'] ) ) { UM()->classes['um\common\actions\emails'] = new actions\Emails(); } + + if ( empty( UM()->classes['um\common\actions\users'] ) ) { + UM()->classes['um\common\actions\users'] = new actions\Users(); + } // Other classes init here as soon as possible. } diff --git a/includes/common/class-users.php b/includes/common/class-users.php index a2c2ad83..8b2ee5aa 100644 --- a/includes/common/class-users.php +++ b/includes/common/class-users.php @@ -20,68 +20,6 @@ class Users { public function hooks() { add_filter( 'user_has_cap', array( &$this, 'map_caps_by_role' ), 10, 3 ); add_filter( 'editable_roles', array( &$this, 'restrict_roles' ) ); - - add_action( 'init', array( &$this, 'um_schedule_account_status_check' ) ); - add_action( 'um_schedule_account_status_check', array( &$this, 'status_check' ) ); - add_action( 'um_check_account_status_batch', array( &$this, 'batch_check' ), 10, 2 ); - } - - public function um_schedule_account_status_check() { - $interval = 3600; - - if ( ! as_next_scheduled_action( 'um_schedule_account_status_check' ) ) { - UM()->maybe_action_scheduler()->schedule_recurring_action( - time() + 60, - $interval, - 'um_schedule_account_status_check' - ); - } - } - - public function status_check() { - global $wpdb; - $batch_size = 50; - $total_users = $wpdb->get_var( - "SELECT COUNT(u.ID) - FROM {$wpdb->users} u - LEFT JOIN {$wpdb->usermeta} um ON u.ID = um.user_id AND um.meta_key = 'account_status' - WHERE um.meta_value IS NULL OR um.meta_value = ''" - ); - - if ( absint( $total_users ) > 0 ) { - for ( $offset = 0; $offset < absint( $total_users ); $offset += $batch_size ) { - UM()->maybe_action_scheduler()->enqueue_async_action( 'um_check_account_status_batch', array( $offset, $batch_size ) ); - } - } - } - - public function batch_check( $offset, $limit ) { - $users = new WP_User_Query( - array( - 'number' => $limit, - 'offset' => $offset, - 'fields' => array( 'ID' ), - 'meta_query' => array( - 'relation' => 'OR', - array( - 'key' => 'account_status', - 'compare' => 'NOT EXISTS', - ), - array( - 'key' => 'account_status', - 'value' => '', - 'compare' => '=', - ), - ), - ) - ); - $results = $users->get_results(); - - if ( ! empty( $results ) ) { - foreach ( $results as $user ) { - update_user_meta( $user->ID, 'account_status', 'approved' ); - } - } } /** @@ -721,11 +659,12 @@ class Users { * * @param int $user_id User ID. * @param bool $force If true - ignore current user condition. + * @param bool $silent If true - don't send email notification. E.g. case when user already exists, but doesn't have a status. * * @return bool `true` if the user has been approved * `false` on failure or if the user already has approved status. */ - public function approve( $user_id, $force = false ) { + public function approve( $user_id, $force = false, $silent = false ) { if ( ! $this->can_be_approved( $user_id, $force ) ) { return false; } @@ -746,25 +685,27 @@ class Users { // It's `false` on failure or if the user already has approved status. if ( false !== $result ) { - $userdata = get_userdata( $user_id ); + if ( false === $silent ) { + $userdata = get_userdata( $user_id ); - $this->reset_activation_link( $user_id ); + $this->reset_activation_link( $user_id ); - $email_slug = 'welcome_email'; - if ( 'awaiting_admin_review' === $old_status ) { - $email_slug = 'approved_email'; - $this->maybe_generate_password_reset_key( $userdata ); + $email_slug = 'welcome_email'; + if ( 'awaiting_admin_review' === $old_status ) { + $email_slug = 'approved_email'; + $this->maybe_generate_password_reset_key( $userdata ); + } + + $current_user_id = get_current_user_id(); + um_fetch_user( $user_id ); + + add_filter( 'um_template_tags_patterns_hook', array( UM()->password(), 'add_placeholder' ) ); + add_filter( 'um_template_tags_replaces_hook', array( UM()->password(), 'add_replace_placeholder' ) ); + + UM()->maybe_action_scheduler()->enqueue_async_action( 'um_dispatch_email', array( $userdata->user_email, $email_slug, array( 'fetch_user_id' => $user_id ) ) ); + + um_fetch_user( $current_user_id ); } - - $current_user_id = get_current_user_id(); - um_fetch_user( $user_id ); - - add_filter( 'um_template_tags_patterns_hook', array( UM()->password(), 'add_placeholder' ) ); - add_filter( 'um_template_tags_replaces_hook', array( UM()->password(), 'add_replace_placeholder' ) ); - - UM()->maybe_action_scheduler()->enqueue_async_action( 'um_dispatch_email', array( $userdata->user_email, $email_slug, array( 'fetch_user_id' => $user_id ) ) ); - - um_fetch_user( $current_user_id ); /** * Fires after User has been approved. * diff --git a/includes/core/um-actions-register.php b/includes/core/um-actions-register.php index 0516d902..2459d56c 100644 --- a/includes/core/um-actions-register.php +++ b/includes/core/um-actions-register.php @@ -169,6 +169,8 @@ function um_check_user_status( $user_id, $args, $form_data = null ) { */ do_action( "um_post_registration_{$registration_status}_hook", $user_id, $args, $form_data ); + delete_user_meta( $user_id, 'um_registration_in_progress' ); // Status is set. We can delete this marker. + if ( is_null( $form_data ) || is_admin() ) { return; } @@ -502,6 +504,11 @@ function um_submit_form_register( $args, $form_data ) { 'user_pass' => $user_password, 'user_email' => $user_email, 'role' => $user_role, + 'meta_input' => array( + // It's used to ignore users who cannot finish the registration process in the scheduled tasks + // to set 'approved' status to the users without `account_status` meta. + 'um_registration_in_progress' => true, + ), ); $user_id = wp_insert_user( $userdata ); From bdd973c14c8361fcf66c4996018b4535a5194dc8 Mon Sep 17 00:00:00 2001 From: Mykyta Synelnikov Date: Mon, 14 Apr 2025 23:13:32 +0300 Subject: [PATCH 3/5] Remove unused WP_User_Query import The WP_User_Query import was removed as it is not used in the code. This helps to clean up unnecessary code and improve maintainability. No functionality is affected by this change. --- includes/common/class-users.php | 1 - 1 file changed, 1 deletion(-) diff --git a/includes/common/class-users.php b/includes/common/class-users.php index 8b2ee5aa..61587997 100644 --- a/includes/common/class-users.php +++ b/includes/common/class-users.php @@ -4,7 +4,6 @@ namespace um\common; use WP_Error; use WP_Session_Tokens; use WP_User; -use WP_User_Query; if ( ! defined( 'ABSPATH' ) ) { exit; From 5356148cc4ff2710e2720f7c2ae82de2f4ca5699 Mon Sep 17 00:00:00 2001 From: Mykyta Synelnikov Date: Tue, 15 Apr 2025 14:27:59 +0300 Subject: [PATCH 4/5] Implement batch processing for users with empty account statuses Introduced a new batch process to handle users lacking an `account_status` meta efficiently. Refactored legacy methods, added async scheduling, and created helper functions to manage and track progress. These changes improve performance and reliability for large user bases. --- includes/admin/class-users-columns.php | 3 - includes/admin/core/class-admin-notices.php | 164 +++++++++++++------- includes/common/actions/class-users.php | 69 +++++--- includes/common/class-users.php | 25 +++ includes/core/class-setup.php | 40 ++--- includes/core/class-user.php | 3 - tests/generate-empty-status-users.php | 34 ++++ uninstall.php | 2 +- 8 files changed, 230 insertions(+), 110 deletions(-) create mode 100644 tests/generate-empty-status-users.php diff --git a/includes/admin/class-users-columns.php b/includes/admin/class-users-columns.php index 81165829..5ad62275 100644 --- a/includes/admin/class-users-columns.php +++ b/includes/admin/class-users-columns.php @@ -350,9 +350,6 @@ if ( ! class_exists( 'um\admin\Users_Columns' ) ) { return; } - // Set default statuses if not already done. - UM()->setup()->set_default_user_status(); - $id = 'um_user_status'; // need to add there additional nonce field because WordPress native _wpnonce field isn't visible on the users.php screen then custom actions diff --git a/includes/admin/core/class-admin-notices.php b/includes/admin/core/class-admin-notices.php index 0fbc12ab..be73e0f4 100644 --- a/includes/admin/core/class-admin-notices.php +++ b/includes/admin/core/class-admin-notices.php @@ -47,6 +47,8 @@ if ( ! class_exists( 'um\admin\core\Admin_Notices' ) ) { $this->lock_registration(); + $this->empty_status_users(); + $this->extensions_page(); $this->child_theme_required(); @@ -204,17 +206,17 @@ if ( ! class_exists( 'um\admin\core\Admin_Notices' ) ) { ob_start(); ?> -
- +
+
- array( + 'href' => array(), + ), + 'strong' => array(), + ); + + $this->add_notice( + 'empty_status_users', + array( + 'class' => 'info', + // translators: %1$d: Background update for users is complete; %2$d: Total users for background update. + 'message' => '

' . wp_kses( sprintf( __( 'Background process is running: Setting user statuses %1$d/%2$d.', 'ultimate-member' ), $empty_status_users[0], $empty_status_users[1] ), $allowed_html ) . '

', + 'dismissible' => false, + ) + ); + } + /** * Checking if the "Membership - Anyone can register" WordPress general setting is active */ @@ -256,11 +289,11 @@ if ( ! class_exists( 'um\admin\core\Admin_Notices' ) ) { ?>

- All Access Pass – Get access to all Ultimate Member extensions at a significant discount with our All Access Pass.', 'ultimate-member' ) ?> + All Access Pass – Get access to all Ultimate Member extensions at a significant discount with our All Access Pass.', 'ultimate-member' ); ?>

- +

@@ -290,7 +323,7 @@ if ( ! class_exists( 'um\admin\core\Admin_Notices' ) ) { $path = str_replace( '//', '/', $path ); if ( ! file_exists( $path ) ) { - $old = umask(0); + $old = umask( 0 ); @mkdir( $path, 0777, true ); umask( $old ); } @@ -326,9 +359,12 @@ if ( ! class_exists( 'um\admin\core\Admin_Notices' ) ) { 'woocommerce', ); - $slugs = array_map( function( $item ) { - return 'um-' . $item . '/um-' . $item . '.php'; - }, $old_extensions ); + $slugs = array_map( + function ( $item ) { + return 'um-' . $item . '/um-' . $item . '.php'; + }, + $old_extensions + ); $active_plugins = UM()->dependencies()->get_active_plugins(); foreach ( $slugs as $slug ) { @@ -622,7 +658,7 @@ if ( ! class_exists( 'um\admin\core\Admin_Notices' ) ) { $arr_inactive_license_keys[] = $license->item_name; } - $invalid_license++; + ++$invalid_license; } if ( ! empty( $arr_inactive_license_keys ) ) { @@ -676,34 +712,41 @@ if ( ! class_exists( 'um\admin\core\Admin_Notices' ) ) {

- +  

- add_notice( 'upgrade', array( - 'class' => 'error', - 'message' => $message, - ), 4 ); - } else { - if ( isset( $_GET['msg'] ) && 'updated' === sanitize_key( $_GET['msg'] ) ) { - if ( isset( $_GET['page'] ) && 'um_options' === sanitize_key( $_GET['page'] ) ) { - $this->add_notice( 'settings_upgrade', array( - 'class' => 'updated', - 'message' => '

' . __( 'Settings successfully upgraded', 'ultimate-member' ) . '

', - ), 4 ); - } else { - $this->add_notice( - 'upgrade', - array( - 'class' => 'updated', - // translators: %1$s is a plugin name title; %2$s is a plugin version. - 'message' => '

' . sprintf( __( '%1$s %2$s Successfully Upgraded', 'ultimate-member' ), UM_PLUGIN_NAME, UM_VERSION ) . '

', - ), - 4 - ); - } + $this->add_notice( + 'upgrade', + array( + 'class' => 'error', + 'message' => $message, + ), + 4 + ); + } elseif ( isset( $_GET['msg'] ) && 'updated' === sanitize_key( $_GET['msg'] ) ) { + if ( isset( $_GET['page'] ) && 'um_options' === sanitize_key( $_GET['page'] ) ) { + $this->add_notice( + 'settings_upgrade', + array( + 'class' => 'updated', + 'message' => '

' . __( 'Settings successfully upgraded', 'ultimate-member' ) . '

', + ), + 4 + ); + } else { + $this->add_notice( + 'upgrade', + array( + 'class' => 'updated', + // translators: %1$s is a plugin name title; %2$s is a plugin version. + 'message' => '

' . sprintf( __( '%1$s %2$s Successfully Upgraded', 'ultimate-member' ), UM_PLUGIN_NAME, UM_VERSION ) . '

', + ), + 4 + ); } } } @@ -734,18 +777,18 @@ if ( ! class_exists( 'um\admin\core\Admin_Notices' ) ) { ?>

-  |  -  |  - +  |  +  |  +

- +

- +

@@ -754,7 +797,7 @@ if ( ! class_exists( 'um\admin\core\Admin_Notices' ) ) {

- +

@@ -763,17 +806,22 @@ if ( ! class_exists( 'um\admin\core\Admin_Notices' ) ) {

- +

- add_notice( 'reviews_notice', array( - 'class' => 'updated', - 'message' => $message, - 'dismissible' => true - ), 1 ); + $this->add_notice( + 'reviews_notice', + array( + 'class' => 'updated', + 'message' => $message, + 'dismissible' => true, + ), + 1 + ); } @@ -782,7 +830,8 @@ if ( ! class_exists( 'um\admin\core\Admin_Notices' ) ) { */ function future_changed() { - ob_start(); ?> + ob_start(); + ?>

- add_notice( 'future_changes', array( - 'class' => 'updated', - 'message' => $message, - ), 2 ); + $this->add_notice( + 'future_changes', + array( + 'class' => 'updated', + 'message' => $message, + ), + 2 + ); } /** diff --git a/includes/common/actions/class-users.php b/includes/common/actions/class-users.php index dae0ce7d..d034f3cf 100644 --- a/includes/common/actions/class-users.php +++ b/includes/common/actions/class-users.php @@ -20,16 +20,16 @@ if ( ! class_exists( 'um\common\actions\Users' ) ) { const INTERVAL = 3600; - const SCHEDULE_ACTION = 'um_schedule_account_status_check'; + const SCHEDULE_ACTION = 'um_schedule_empty_account_status_check'; const BATCH_SIZE = 50; - const BATCH_ACTION = 'um_check_account_status_batch'; + const BATCH_ACTION = 'um_set_default_account_status'; public function __construct() { add_action( 'init', array( &$this, 'add_recurring_action' ) ); add_action( self::SCHEDULE_ACTION, array( &$this, 'status_check' ) ); - add_action( self::BATCH_ACTION, array( &$this, 'batch_check' ) ); + add_action( self::BATCH_ACTION, array( &$this, 'batch_check' ), 10, 3 ); } public function add_recurring_action() { @@ -45,31 +45,34 @@ if ( ! class_exists( 'um\common\actions\Users' ) ) { } public function status_check() { - global $wpdb; - $total_users = $wpdb->get_var( - "SELECT COUNT(u.ID) - FROM {$wpdb->users} u - LEFT JOIN {$wpdb->usermeta} um ON u.ID = um.user_id AND um.meta_key = 'account_status' - LEFT JOIN {$wpdb->usermeta} um2 ON u.ID = um2.user_id AND um2.meta_key = 'um_registration_in_progress' - WHERE ( um.meta_value IS NULL OR um.meta_value = '' ) AND - um2.meta_value IS NULL OR um2.meta_value != '1'" - ); - $total_users = absint( $total_users ); - + $total_users = UM()->common()->users()::get_empty_status_users(); if ( empty( $total_users ) ) { return; } - for ( $offset = 0; $offset < $total_users; $offset += self::BATCH_SIZE ) { - UM()->maybe_action_scheduler()->enqueue_async_action( self::BATCH_ACTION, array( $offset ) ); - } + UM()->maybe_action_scheduler()->enqueue_async_action( + self::BATCH_ACTION, + array( + 'page' => 1, + 'total' => $total_users, + 'pages' => ceil( $total_users / self::BATCH_SIZE ), + ) + ); } - public function batch_check( $offset ) { + /** + * Perform batch checking for users based on specific conditions. + * Ignore users with `um_registration_in_progress` that can be in the process of the registration. + * Get users with empty `account_status` meta. + * + * @param int $page The current page number. + * @param int $total The total number of users to process. + * @param int $pages The total number of pages to process. + */ + public function batch_check( $page, $total, $pages ) { $users = new WP_User_Query( array( 'number' => self::BATCH_SIZE, - 'offset' => $offset, 'fields' => 'ids', 'meta_query' => array( 'relation' => 'AND', @@ -104,8 +107,34 @@ if ( ! class_exists( 'um\common\actions\Users' ) ) { $results = $users->get_results(); if ( ! empty( $results ) ) { + $um_empty_status_users = get_option( '_um_log_empty_status_users', array( 0, 0 ) ); + if ( ! is_array( $um_empty_status_users ) ) { + $um_empty_status_users = array( 0, count( $results ) ); + } + foreach ( $results as $user_id ) { - UM()->common()->users()->approve( $user_id, true, true ); + $res = UM()->common()->users()->approve( $user_id, true, true ); + if ( $res ) { + ++$um_empty_status_users[0]; + } + } + + if ( $um_empty_status_users[0] < $um_empty_status_users[1] ) { + update_option( '_um_log_empty_status_users', $um_empty_status_users ); + } else { + delete_option( '_um_log_empty_status_users' ); + } + + $next_page = $page + 1; + if ( $next_page <= $pages ) { + UM()->maybe_action_scheduler()->enqueue_async_action( + self::BATCH_ACTION, + array( + 'page' => $next_page, + 'total' => $total, + 'pages' => $pages, + ) + ); } } } diff --git a/includes/common/class-users.php b/includes/common/class-users.php index 61587997..f5d1f933 100644 --- a/includes/common/class-users.php +++ b/includes/common/class-users.php @@ -830,4 +830,29 @@ class Users { $user = WP_Session_Tokens::get_instance( $user_id ); $user->destroy_all(); } + + /** + * Retrieve the number of users with empty `account_status` usermeta. + * + * @return int + */ + public static function get_empty_status_users() { + global $wpdb; + + $total_users = $wpdb->get_var( + "SELECT COUNT(u.ID) + FROM {$wpdb->users} u + LEFT JOIN {$wpdb->usermeta} um ON u.ID = um.user_id AND um.meta_key = 'account_status' + LEFT JOIN {$wpdb->usermeta} um2 ON u.ID = um2.user_id AND um2.meta_key = 'um_registration_in_progress' + WHERE ( um.meta_value IS NULL OR um.meta_value = '' ) AND + um2.meta_value IS NULL OR um2.meta_value != '1'" + ); + + $total_users = absint( $total_users ); + // In WordPress, an underscore prefix before the option name (e.g., _my_option_name) is commonly used to indicate that the option is private. + // This option has a format: {updated_users}/{total_users_for_update}. + update_option( '_um_log_empty_status_users', array( 0, $total_users ) ); + + return $total_users; + } } diff --git a/includes/core/class-setup.php b/includes/core/class-setup.php index 27a342eb..eb6d37f4 100644 --- a/includes/core/class-setup.php +++ b/includes/core/class-setup.php @@ -310,37 +310,21 @@ KEY meta_value_indx (um_value(191)) * @since 2.4.2 */ public function set_default_user_status() { - $result = get_transient( 'um_count_users_unassigned' ); - if ( false === $result ) { - $args = array( - 'fields' => 'ids', - 'number' => 0, - 'meta_query' => array( - array( - 'key' => 'account_status', - 'compare' => 'NOT EXISTS', - ), - ), - 'um_custom_user_query' => true, - ); - - $users = new WP_User_Query( $args ); - if ( empty( $users ) || is_wp_error( $users ) ) { - $result = array(); - } else { - $result = $users->get_results(); - } - - set_transient( 'um_count_users_unassigned', $result, DAY_IN_SECONDS ); - } - - if ( empty( $result ) ) { + $total_users = UM()->common()->users()::get_empty_status_users(); + if ( empty( $total_users ) ) { return; } - foreach ( $result as $user_id ) { - update_user_meta( $user_id, 'account_status', 'approved' ); - } + // If there are some users without `account_status` then run the first async batch for update. + $batch_size = 50; // See the class constant value `\um\common\actions\Users::BATCH_ACTION`. + UM()->maybe_action_scheduler()->enqueue_async_action( + $batch_size, + array( + 'page' => 1, + 'total' => $total_users, + 'pages' => ceil( $total_users / $batch_size ), + ) + ); } /** diff --git a/includes/core/class-user.php b/includes/core/class-user.php index f8d70584..66baf197 100644 --- a/includes/core/class-user.php +++ b/includes/core/class-user.php @@ -640,7 +640,6 @@ if ( ! class_exists( 'um\core\User' ) ) { UM()->files()->remove_dir( UM()->files()->upload_temp ); UM()->files()->remove_dir( UM()->uploader()->get_upload_base_dir() . um_user( 'ID' ) . DIRECTORY_SEPARATOR ); - delete_transient( 'um_count_users_unassigned' ); delete_transient( 'um_count_users_pending_dot' ); } @@ -959,8 +958,6 @@ if ( ! class_exists( 'um\core\User' ) ) { /** This action is documented in ultimate-member/includes/common/um-actions-register.php */ do_action( 'um_user_register', $user_id, $_POST, null ); } - - delete_transient( 'um_count_users_unassigned' ); } diff --git a/tests/generate-empty-status-users.php b/tests/generate-empty-status-users.php new file mode 100644 index 00000000..55e26ae2 --- /dev/null +++ b/tests/generate-empty-status-users.php @@ -0,0 +1,34 @@ + $random_user_name, + 'user_pass' => 'q1q2q1q2', + 'user_email' => $random_user_email, + 'first_name' => $random_first_name, + 'last_name' => $random_last_name, + 'role' => 'subscriber', + ); + + $user_id = wp_insert_user( $userdata ); + + if ( is_wp_error( $user_id ) ) { + // Something went wrong, handle the error + var_dump( 'User creation failed: ' . $user_id->get_error_message() ); + } else { + var_dump( 'User creation complete: ID:' . $user_id . ' Username:' . $userdata['user_login'] ); + } +} + +exit; diff --git a/uninstall.php b/uninstall.php index 6cf0d554..5cf4e8ab 100644 --- a/uninstall.php +++ b/uninstall.php @@ -127,7 +127,7 @@ if ( ! empty( $delete_options ) ) { delete_transient( "um_count_users_{$status}" ); } delete_transient( 'um_count_users_pending_dot' ); - delete_transient( 'um_count_users_unassigned' ); + delete_transient( 'um_count_users_unassigned' ); // legacy but still need to delete while uninstall. //remove all users cache UM()->user()->remove_cache_all_users(); From 820ef35104a74e93a4ebfec80c3679e1c0bebd95 Mon Sep 17 00:00:00 2001 From: Mykyta Synelnikov Date: Tue, 15 Apr 2025 14:54:44 +0300 Subject: [PATCH 5/5] Fix user meta key for registration progress handling Updated the user meta key from `um_registration_in_progress` to `_um_registration_in_progress` across multiple files for consistency and proper functionality. This change ensures accurate checking and handling of users in the registration process. --- includes/common/actions/class-users.php | 6 +++--- includes/common/class-users.php | 2 +- includes/core/um-actions-register.php | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/includes/common/actions/class-users.php b/includes/common/actions/class-users.php index d034f3cf..e3309b6a 100644 --- a/includes/common/actions/class-users.php +++ b/includes/common/actions/class-users.php @@ -62,7 +62,7 @@ if ( ! class_exists( 'um\common\actions\Users' ) ) { /** * Perform batch checking for users based on specific conditions. - * Ignore users with `um_registration_in_progress` that can be in the process of the registration. + * Ignore users with `_um_registration_in_progress` that can be in the process of the registration. * Get users with empty `account_status` meta. * * @param int $page The current page number. @@ -79,11 +79,11 @@ if ( ! class_exists( 'um\common\actions\Users' ) ) { array( 'relation' => 'OR', array( - 'key' => 'um_registration_in_progress', + 'key' => '_um_registration_in_progress', 'compare' => 'NOT EXISTS', ), array( - 'key' => 'um_registration_in_progress', + 'key' => '_um_registration_in_progress', 'value' => '1', 'compare' => '!=', ), diff --git a/includes/common/class-users.php b/includes/common/class-users.php index f5d1f933..392c29d2 100644 --- a/includes/common/class-users.php +++ b/includes/common/class-users.php @@ -843,7 +843,7 @@ class Users { "SELECT COUNT(u.ID) FROM {$wpdb->users} u LEFT JOIN {$wpdb->usermeta} um ON u.ID = um.user_id AND um.meta_key = 'account_status' - LEFT JOIN {$wpdb->usermeta} um2 ON u.ID = um2.user_id AND um2.meta_key = 'um_registration_in_progress' + LEFT JOIN {$wpdb->usermeta} um2 ON u.ID = um2.user_id AND um2.meta_key = '_um_registration_in_progress' WHERE ( um.meta_value IS NULL OR um.meta_value = '' ) AND um2.meta_value IS NULL OR um2.meta_value != '1'" ); diff --git a/includes/core/um-actions-register.php b/includes/core/um-actions-register.php index 2459d56c..b30d43a9 100644 --- a/includes/core/um-actions-register.php +++ b/includes/core/um-actions-register.php @@ -169,7 +169,7 @@ function um_check_user_status( $user_id, $args, $form_data = null ) { */ do_action( "um_post_registration_{$registration_status}_hook", $user_id, $args, $form_data ); - delete_user_meta( $user_id, 'um_registration_in_progress' ); // Status is set. We can delete this marker. + delete_user_meta( $user_id, '_um_registration_in_progress' ); // Status is set. We can delete this marker. if ( is_null( $form_data ) || is_admin() ) { return; @@ -507,7 +507,7 @@ function um_submit_form_register( $args, $form_data ) { 'meta_input' => array( // It's used to ignore users who cannot finish the registration process in the scheduled tasks // to set 'approved' status to the users without `account_status` meta. - 'um_registration_in_progress' => true, + '_um_registration_in_progress' => true, ), );