container = $container; } /** * Build the default user factory * * @return callable(WP_User): User Factory turning a WP_User into the plugin's User instance. */ protected function get_default_user_factory(): callable { return function ( WP_User $wp_user ): User { return new User( $this->container, $wp_user ); // @codeCoverageIgnore }; } /** * Collect user IDs based on given criteria * * @param bool $to_all Whether to apply to all users. * @param string[] $to_roles Array of roles to apply to. * @param int[]|string[] $to_users Array of user IDs to apply to. * @param ?int $limit Number of users to process per batch, or null. * @param ?int $paged Page number for pagination, or null. * @param ?Utils\Users $users Users utility instance for dependency injection (optional, creates new if not provided). * * @return int[] Array of user IDs collected. */ public function collect_user_ids( bool $to_all, array $to_roles, array $to_users, ?int $limit = null, ?int $paged = null, ?object $users = null ): array { if ( null === $users ) { $users = new Utils\Users( $this->container ); // @codeCoverageIgnore } if ( true === $to_all ) { $to_roles = []; $to_users = []; } // Map user logins to user IDs, as the user query requires user IDs. if ( ! empty( $to_users ) ) { $to_users = $users->maybe_map_user_logins_to_user_ids( $to_users ); } return $users->get_users_batch( $to_all, $to_roles, $to_users, $limit, $paged, true ); } /** * Clear password reset enforcement for users * * @param int[] $user_ids Array of user IDs to process. * @param (callable(WP_User): User)|null $user_factory Optional user factory callable for dependency injection. If null, uses default User instantiation. * * @return void */ public function clear_password_reset_enforcement( array $user_ids, ?callable $user_factory = null ): void { $user_factory ??= $this->get_default_user_factory(); foreach ( $user_ids as $user_id ) { $wp_user = get_user_by( 'ID', $user_id ); if ( ! $wp_user instanceof WP_User ) { continue; } $user = $user_factory( $wp_user ); $user->remove_password_reset_enforcement(); } } /** * Force password reset for users * * @param int[] $user_ids Array of user IDs to process. * @param string $applicability One of the self::APPLICABILITY_* values. * @param bool $with_email Whether to send email notification. * @param bool $with_current_password_allowed Whether to allow current password. * @param int|string $requestor Current user ID or identifier performing the action. * @param (callable(WP_User): User)|null $user_factory Optional user factory callable for dependency injection. If null, uses default User instantiation. * * @return void */ public function force_password_reset( array $user_ids, string $applicability, bool $with_email, bool $with_current_password_allowed, $requestor = '', ?callable $user_factory = null ): void { $user_factory ??= $this->get_default_user_factory(); foreach ( $user_ids as $user_id ) { $wp_user = get_user_by( 'ID', $user_id ); if ( ! $wp_user instanceof WP_User ) { continue; } $user = $user_factory( $wp_user ); if ( self::APPLICABILITY_IMMEDIATELY === $applicability ) { $user->logout_everywhere(); } if ( true === $with_email ) { $user->send_email_with_link(); } $user->force_password_reset( $requestor, $with_current_password_allowed ); } } /** * Get the password reset enforcement status for users * * @param int[] $user_ids Array of user IDs to process. * @param (callable(WP_User): User)|null $user_factory Optional user factory callable for dependency injection. If null, uses default User instantiation. * * @return array Array containing status information about the password reset enforcement for users. */ public function get_password_reset_enforcement_status( array $user_ids, ?callable $user_factory = null ): array { $user_factory ??= $this->get_default_user_factory(); $results = []; foreach ( $user_ids as $user_id ) { $wp_user = get_user_by( 'ID', $user_id ); if ( ! $wp_user instanceof WP_User ) { continue; } $user = $user_factory( $wp_user ); $data = $user->get_password_reset_request_data(); $result = [ 'user_id' => $user_id, 'user_name' => $user->get_user_login(), 'needs_password_reset' => 'no', 'requested_at' => '-', 'requested_by' => '-', 'with_current_password_allowed' => '-', ]; if ( null !== $data ) { $result['needs_password_reset'] = 'yes'; $result['requested_at'] = sprintf( '%1$s (%2$s ago)', Utils\Date_Time::get_i18n_datetime_string( $data['requested_at'] ), human_time_diff( $data['requested_at'] ), ); $result['requested_by'] = $user->get_requestor_display_name( $data['requested_by'] ); $result['with_current_password_allowed'] = Utils\Type::ensure_bool( $data['with_current_password_allowed'] ) ? 'yes' : 'no'; } $results[] = $result; } return $results; } /** * List users who have a pending password reset request * * Resolves affected users by querying for the existence of the enforcement * request user meta — the feature's data store. * * @param ?int $limit Number of users to process per batch, or null for no limit. * @param ?int $paged Page number for pagination (1-based), or null for no pagination. * @param (callable(WP_User): User)|null $user_factory Optional user factory callable for dependency injection. If null, uses default User instantiation. * * @return array Array containing information about users with pending password reset requests. */ public function list_users_with_enforced_password_reset( ?int $limit = null, ?int $paged = null, ?callable $user_factory = null ): array { $user_factory ??= $this->get_default_user_factory(); $results = []; // "blog_id => 0" keeps the network-wide scope of the enforcement request meta. $args = [ 'meta_key' => ( new User( $this->container ) )->get_prefixed_meta_key( User::USER_META_KEY__REQUEST ), // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_key -- the enforcement request meta is the feature's data store; there's no other way to resolve affected users. 'meta_compare' => 'EXISTS', 'fields' => 'ID', 'orderby' => 'ID', 'order' => 'ASC', 'count_total' => false, 'blog_id' => 0, ]; if ( null !== $limit ) { $args['number'] = $limit; if ( null !== $paged ) { $args['paged'] = $paged; } } $user_ids = Utils\Type::ensure_array_of_ints( get_users( $args ) ); foreach ( $user_ids as $user_id ) { $wp_user = get_user_by( 'ID', $user_id ); if ( ! $wp_user instanceof WP_User ) { continue; } $user = $user_factory( $wp_user ); if ( null === $user->get_user_login() ) { continue; } $data = $user->get_password_reset_request_data(); $result = [ 'user_id' => $user_id, 'user_name' => $user->get_user_login(), 'requested_at' => '-', 'requested_by' => '-', 'with_current_password_allowed' => '-', ]; if ( null !== $data ) { $result['requested_at'] = sprintf( '%1$s (%2$s ago)', Utils\Date_Time::get_i18n_datetime_string( $data['requested_at'] ), human_time_diff( $data['requested_at'] ), ); $result['requested_by'] = $user->get_requestor_display_name( $data['requested_by'] ); $result['with_current_password_allowed'] = Utils\Type::ensure_bool( $data['with_current_password_allowed'] ) ? 'yes' : 'no'; } $results[] = $result; } return $results; } }