diff --git a/changelog.txt b/changelog.txt index 1e0e2bf..0c678d8 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,5 +1,25 @@ == Changelog == += 1.6.6 = + +_Release date: 2026-09-17_ + +**Added** + +* Forced-enrollment warning on the profile 2FA section: when a user's role enforces 2FA but they have no verification method configured yet, the 2FA section of their profile (wp-admin and the frontend shortcode) opens with a warning notice asking them to activate at least one method. While a grace period is active, the notice appends the remaining days. + +**Compatibility** + +* WordPress: 5.6 – 7.1 +* PHP: 8.0 – 8.5 + +**Tests** + +* PHP Coding Standards: PHP_CodeSniffer 3.13.6 / WPCS 3.4.1 — 0 errors +* PHPStan: level 9 — 0 errors +* PHPCompatibility: 8.0–8.5 — 0 issues +* PHPUnit: 9.6.36 — 93 tests, 189 assertions + = 1.6.5 = _Release date: 2026-09-17_ diff --git a/includes/user/class-profile-settings.php b/includes/user/class-profile-settings.php index ff2ceb8..f136ca0 100644 --- a/includes/user/class-profile-settings.php +++ b/includes/user/class-profile-settings.php @@ -97,6 +97,13 @@ class Profile_Settings { */ private Trusted_Devices $trusted_devices; + /** + * Grace period manager. + * + * @var Grace_Period + */ + private Grace_Period $grace_period; + /** * Whether the post-save redirect should focus the 2FA section. * @@ -113,6 +120,7 @@ class Profile_Settings { $this->otp_manager = new OTP_Manager(); $this->recovery_codes = new Recovery_Codes(); $this->trusted_devices = new Trusted_Devices(); + $this->grace_period = new Grace_Period(); } /** @@ -170,6 +178,57 @@ class Profile_Settings { } + /** + * Render the forced-enrollment warning on the profile 2FA section. + * + * Shown when the user's role enforces 2FA but no verification method is + * configured yet, so the requirement is visible before the login flow + * (grace notice, setup wizard, or login block) kicks in. When a grace + * period is active, the number of remaining days is appended. + * + * @since 1.6.6 + * + * @param \WP_User $user User whose profile section is being rendered. + * + * @return void + */ + public function render_forced_enrollment_warning( \WP_User $user ): void { + $user_settings = $this->user_settings_repository->get_user_settings( $user->ID ); + + if ( empty( $this->config->get_required_methods_for_user( $user ) ) || ! empty( $user_settings['methods'] ) ) { + return; + } + + $grace_note = ''; + $grace_days = $this->config->get_grace_period_days(); + + if ( $grace_days > 0 && $this->grace_period->is_active( $user, $grace_days ) ) { + $remaining = $this->grace_period->get_days_remaining( $user, $grace_days ); + + /* translators: %d: number of days remaining to complete the setup. */ + $grace_note = sprintf( + _n( + 'You have %d day left to complete the setup.', + 'You have %d days left to complete the setup.', + $remaining, + 'robotstxt-2fa' + ), + $remaining + ); + } + ?> +
+ + + + + +
+