CI / Tests (PHP 8.1) (pull_request) Successful in 1m18s
CI / Tests (PHP 8.2) (pull_request) Successful in 1m18s
CI / No Debug Code (pull_request) Successful in 2s
CI / PHPStan (pull_request) Successful in 3m20s
CI / Coding Standards (pull_request) Successful in 3m25s
CI / Tests (PHP 8.3) (pull_request) Successful in 2m33s
CI / Build Plugin Zip (pull_request) Skipped
Students could previously join by invite only. Add an optional self-approval mode, toggled from Studio Settings → Registration: anyone may sign up on the existing [us_student_register] page, confirm their email via a tokenised link, and then be approved by a studio admin before the account is usable. - Enabling the toggle mirrors WordPress's own membership settings (users_can_register + default_role = us_student) and snapshots their previous values so disabling restores them. - WordPress's native registration form is blocked while open registration is on (login_init redirect + registration_errors fail-safe + register_url) so it cannot bypass signup policy acceptance. - Pending accounts: unconfirmed email cannot log in; confirmed but unapproved can log in but the booking capability is withheld and the booking page shows an "awaiting approval" screen. - Approve/reject from Students → Pending Students; reject hard-deletes the account so the email is freed to re-apply. - Invite registration is unchanged; both modes coexist. Account lifecycle lives in user meta (RegistrationStatus); no new tables. Closes #63 Co-Authored-By: Claude Opus 4.8 <[email protected]>
103 lines
3.0 KiB
PHP
103 lines
3.0 KiB
PHP
<?php
|
|
declare(strict_types=1);
|
|
|
|
namespace Unsupervised\Schedular\Auth;
|
|
|
|
use Unsupervised\Schedular\Val;
|
|
|
|
/**
|
|
* Admin page (Students → Pending Students) for reviewing self-signup accounts:
|
|
* approve a confirmed applicant into a full student, or reject (delete) them.
|
|
* Only relevant while open registration is enabled.
|
|
*/
|
|
class RegistrationApprovalController {
|
|
|
|
public const PAGE_SLUG = 'us-pending-students';
|
|
public const NONCE_ACTION = 'usc_registration_approval';
|
|
|
|
public function __construct( private RegistrationMailer $mailer ) {}
|
|
|
|
public function renderPage(): void {
|
|
if ( ! current_user_can( RoleManager::CAP_MANAGE_STUDENTS ) ) {
|
|
wp_die( esc_html__( 'You do not have permission to manage student registrations.', 'unsupervised-schedular' ) );
|
|
}
|
|
|
|
if ( isset( $_POST['usc_action'] ) && check_admin_referer( self::NONCE_ACTION ) ) {
|
|
$this->handleAction();
|
|
}
|
|
|
|
$awaitingApproval = [];
|
|
$awaitingConfirmation = [];
|
|
foreach ( $this->pendingUsers() as $user ) {
|
|
if ( RegistrationStatus::emailConfirmed( (int) $user->ID ) ) {
|
|
$awaitingApproval[] = $user;
|
|
} else {
|
|
$awaitingConfirmation[] = $user;
|
|
}
|
|
}
|
|
|
|
include USC_PLUGIN_DIR . 'templates/admin/registrations.php';
|
|
}
|
|
|
|
/**
|
|
* Approve or reject the posted user. Approval clears the pending flags and
|
|
* emails the student; rejection emails them, then hard-deletes the account so
|
|
* the email is freed to re-apply.
|
|
*/
|
|
private function handleAction(): void {
|
|
// Nonce is verified by the caller (renderPage) before this method runs.
|
|
// phpcs:disable WordPress.Security.NonceVerification.Missing
|
|
$action = sanitize_key( Val::string( wp_unslash( $_POST['usc_action'] ?? '' ) ) );
|
|
$userId = absint( Val::int( $_POST['user_id'] ?? 0 ) );
|
|
// phpcs:enable WordPress.Security.NonceVerification.Missing
|
|
|
|
if ( $userId <= 0 || ! RegistrationStatus::isAwaitingApproval( $userId ) ) {
|
|
return;
|
|
}
|
|
|
|
if ( 'approve' === $action ) {
|
|
RegistrationStatus::approve( $userId );
|
|
$user = get_user_by( 'id', $userId );
|
|
if ( $user instanceof \WP_User ) {
|
|
$this->mailer->sendApproved( $user );
|
|
}
|
|
return;
|
|
}
|
|
|
|
if ( 'reject' === $action ) {
|
|
$user = get_user_by( 'id', $userId );
|
|
$email = $user instanceof \WP_User ? (string) $user->user_email : '';
|
|
if ( '' !== $email ) {
|
|
$this->mailer->sendRejected( $email );
|
|
}
|
|
|
|
if ( ! function_exists( 'wp_delete_user' ) ) {
|
|
require_once ABSPATH . 'wp-admin/includes/user.php';
|
|
}
|
|
wp_delete_user( $userId );
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Every account still awaiting approval (confirmed or not).
|
|
*
|
|
* @return list<\WP_User>
|
|
*/
|
|
private function pendingUsers(): array {
|
|
return array_values(
|
|
array_filter(
|
|
get_users(
|
|
[
|
|
'meta_key' => RegistrationStatus::META_AWAITING_APPROVAL, // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_key
|
|
'meta_value' => '1', // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_value
|
|
'number' => 500,
|
|
'orderby' => 'user_registered',
|
|
'order' => 'ASC',
|
|
]
|
|
),
|
|
static fn( mixed $user ): bool => $user instanceof \WP_User
|
|
)
|
|
);
|
|
}
|
|
}
|