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.
This commit is contained in:
Mykyta Synelnikov
2025-04-14 23:12:29 +03:00
parent 08a7b48d53
commit c8bd79fceb
4 changed files with 144 additions and 79 deletions
+113
View File
@@ -0,0 +1,113 @@
<?php
namespace um\common\actions;
use WP_User_Query;
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
if ( ! class_exists( 'um\common\actions\Users' ) ) {
/**
* Class Users
*
* @since 2.10.3
*
* @package um\common\actions
*/
class Users {
const INTERVAL = 3600;
const SCHEDULE_ACTION = 'um_schedule_account_status_check';
const BATCH_SIZE = 50;
const BATCH_ACTION = 'um_check_account_status_batch';
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' ) );
}
public function add_recurring_action() {
if ( UM()->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 );
}
}
}
}
}
+4
View File
@@ -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.
}
+20 -79
View File
@@ -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.
*
+7
View File
@@ -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 );