init_hooks(); } /** * Initialize WordPress hooks. * * @since 0.1.0 */ private function init_hooks(): void { // Add bulk actions to users list. add_filter( 'bulk_actions-users', array( $this, 'register_bulk_actions' ) ); // Handle bulk actions. add_filter( 'handle_bulk_actions-users', array( $this, 'handle_bulk_actions' ), 10, 3 ); // Display admin notices after bulk actions. add_action( 'admin_notices', array( $this, 'display_bulk_action_notices' ) ); } /** * Register bulk actions. * * @since 0.1.0 * * @param array $actions Existing bulk actions. * * @return array Modified bulk actions. */ public function register_bulk_actions( array $actions ): array { if ( ! current_user_can( 'manage_options' ) ) { return $actions; } $actions['two_factor_extended_require'] = __( 'Require 2FA Setup', 'two-factor-extended' ); $actions['two_factor_extended_reset'] = __( 'Reset 2FA Grace Period', 'two-factor-extended' ); return $actions; } /** * Handle bulk actions. * * @since 0.1.0 * * @param string $redirect_to Redirect URL. * @param string $action Action name. * @param int[] $user_ids User IDs. * * @return string Modified redirect URL. */ public function handle_bulk_actions( string $redirect_to, string $action, array $user_ids ): string { // Check capability. if ( ! current_user_can( 'manage_options' ) ) { return $redirect_to; } // Handle our bulk actions. if ( 'two_factor_extended_require' === $action ) { $processed = $this->bulk_require_2fa( $user_ids ); $redirect_to = add_query_arg( array( 'two_factor_extended_bulk_require' => $processed, ), $redirect_to ); } elseif ( 'two_factor_extended_reset' === $action ) { $processed = $this->bulk_reset_grace_period( $user_ids ); $redirect_to = add_query_arg( array( 'two_factor_extended_bulk_reset' => $processed, ), $redirect_to ); } return $redirect_to; } /** * Bulk require 2FA for users. * * @since 0.1.0 * * @param int[] $user_ids User IDs. * * @return int Number of users processed. */ private function bulk_require_2fa( array $user_ids ): int { $processed = 0; $enforcement = two_factor_extended()->get_enforcement(); if ( null === $enforcement ) { return 0; } foreach ( $user_ids as $user_id ) { // Skip if user doesn't exist. $user = get_userdata( $user_id ); if ( ! $user ) { continue; } // Get required providers for user. $required = $enforcement->get_required_providers_for_user( $user_id ); // Skip if no requirements. if ( empty( $required ) ) { continue; } // Set enforcement start date to now (starts grace period). update_user_meta( $user_id, Two_Factor_Extended_Enforcement::META_ENFORCEMENT_START, time() ); // Log the action. $audit_log = two_factor_extended()->get_audit_log(); if ( null !== $audit_log ) { $audit_log->log_event( 'bulk_require_2fa', sprintf( 'Bulk action: Require 2FA for user %s', $user->user_login ), $user_id, array( 'required_providers' => $required, ) ); } $processed++; } return $processed; } /** * Bulk reset grace period for users. * * @since 0.1.0 * * @param int[] $user_ids User IDs. * * @return int Number of users processed. */ private function bulk_reset_grace_period( array $user_ids ): int { $processed = 0; foreach ( $user_ids as $user_id ) { // Skip if user doesn't exist. $user = get_userdata( $user_id ); if ( ! $user ) { continue; } // Reset enforcement start date (restarts grace period). update_user_meta( $user_id, Two_Factor_Extended_Enforcement::META_ENFORCEMENT_START, time() ); // Clear grace period notified flag. delete_user_meta( $user_id, Two_Factor_Extended_Enforcement::META_GRACE_NOTIFIED ); // Log the action. $audit_log = two_factor_extended()->get_audit_log(); if ( null !== $audit_log ) { $audit_log->log_event( 'bulk_reset_grace', sprintf( 'Bulk action: Reset grace period for user %s', $user->user_login ), $user_id ); } $processed++; } return $processed; } /** * Display admin notices after bulk actions. * * This method displays success messages after bulk actions are completed. * No nonce verification is needed here because: * 1. This is a read-only operation (displaying a message) * 2. The actual bulk action was already verified in handle_bulk_actions() * 3. The GET parameters are the result of a POST-redirect-GET pattern * 4. Only the count is used, which is safely cast to int * 5. User capability is verified before displaying * * @since 0.1.0 */ public function display_bulk_action_notices(): void { // Check if on users page. $screen = get_current_screen(); if ( ! $screen || 'users' !== $screen->id ) { return; } // Verify user has capability to see these notices. if ( ! current_user_can( 'manage_options' ) ) { return; } // Bulk require 2FA notice. // Use filter_input() for safe GET parameter access. $bulk_require_count = filter_input( INPUT_GET, 'two_factor_extended_bulk_require', FILTER_VALIDATE_INT ); if ( $bulk_require_count && $bulk_require_count > 0 ) { printf( '

%s

', esc_html( sprintf( /* translators: %d: Number of users */ _n( '2FA requirement set for %d user.', '2FA requirement set for %d users.', $bulk_require_count, 'two-factor-extended' ), $bulk_require_count ) ) ); } // Bulk reset grace period notice. $bulk_reset_count = filter_input( INPUT_GET, 'two_factor_extended_bulk_reset', FILTER_VALIDATE_INT ); if ( $bulk_reset_count && $bulk_reset_count > 0 ) { printf( '

%s

', esc_html( sprintf( /* translators: %d: Number of users */ _n( 'Grace period reset for %d user.', 'Grace period reset for %d users.', $bulk_reset_count, 'two-factor-extended' ), $bulk_reset_count ) ) ); } } }