$atts Shortcode attributes (unused — reserved for future options). */ public function render( array $atts ): string { // phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.Found if ( is_user_logged_in() ) { return '

' . esc_html__( 'You already have an account and are logged in.', 'unsupervised-schedular' ) . '

'; } // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- token identifies the invite; the form submit is nonce-checked below. $token = sanitize_text_field( wp_unslash( $_REQUEST['us_invite'] ?? '' ) ); $invite = '' !== $token ? $this->invites->findByToken( $token ) : null; $error = ''; $success = false; if ( isset( $_POST['us_register'] ) && check_admin_referer( 'us_student_register' ) ) { $result = $this->handleSubmit( $invite ); if ( true === $result ) { $success = true; } else { $error = $result; } } $policyForms = $this->signupPolicies(); $canRegister = null !== $invite && $invite->isPending(); ob_start(); include USC_PLUGIN_DIR . 'templates/frontend/register-page.php'; return (string) ob_get_clean(); } /** * Redirect to the configured registration page when an invite token lands * elsewhere (e.g. a link generated before the page was selected). Hooked on * `template_redirect`. */ public function maybeRedirectToRegistrationPage(): void { if ( is_admin() ) { return; } // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- read-only token used only to build the redirect target. $token = sanitize_text_field( wp_unslash( $_GET['us_invite'] ?? '' ) ); if ( '' === $token ) { return; } $pageId = (int) get_option( RegistrationController::OPTION_PAGE, 0 ); if ( $pageId <= 0 || is_page( $pageId ) ) { return; } wp_safe_redirect( add_query_arg( 'us_invite', rawurlencode( $token ), (string) get_permalink( $pageId ) ) ); exit; } /** * Process the submitted registration. Returns true on success or an error * message string on failure. */ private function handleSubmit( ?Invite $invite ): string|bool { if ( null === $invite || ! $invite->isPending() ) { return esc_html__( 'This invitation is invalid or has already been used.', 'unsupervised-schedular' ); } // The submit nonce is verified by the caller (render) before this runs. // phpcs:disable WordPress.Security.NonceVerification.Missing // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- passwords must not be sanitized. $password = (string) wp_unslash( $_POST['password'] ?? '' ); $displayName = sanitize_text_field( wp_unslash( $_POST['display_name'] ?? '' ) ); if ( strlen( $password ) < 8 ) { return esc_html__( 'Please choose a password of at least 8 characters.', 'unsupervised-schedular' ); } $policyForms = $this->signupPolicies(); $accepted = array_map( 'absint', (array) ( $_POST['accept'] ?? [] ) ); // phpcs:enable WordPress.Security.NonceVerification.Missing foreach ( $policyForms as $form ) { if ( ! in_array( (int) $form['version']->id, $accepted, true ) ) { return esc_html__( 'You must accept all required policies to register.', 'unsupervised-schedular' ); } } if ( email_exists( $invite->email ) ) { return esc_html__( 'An account already exists for this email.', 'unsupervised-schedular' ); } $userId = wp_insert_user( [ 'user_login' => $invite->email, 'user_email' => $invite->email, 'user_pass' => $password, 'display_name' => '' !== $displayName ? $displayName : $invite->email, 'role' => $invite->role, ] ); if ( is_wp_error( $userId ) ) { return esc_html__( 'Could not create the account. Please contact the studio.', 'unsupervised-schedular' ); } $this->recordAcceptances( $policyForms, (int) $userId ); $this->invites->markAccepted( (int) $invite->id, (int) $userId ); wp_set_current_user( (int) $userId ); wp_set_auth_cookie( (int) $userId ); return true; } /** * Record account-time acceptances for each signup policy version. * * @param list $policyForms */ private function recordAcceptances( array $policyForms, int $userId ): void { // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized, WordPress.Security.ValidatedSanitizedInput.MissingUnslash -- IP is stored verbatim for audit. $ip = sanitize_text_field( wp_unslash( $_SERVER['REMOTE_ADDR'] ?? '' ) ); foreach ( $policyForms as $form ) { $this->acceptances->insert( new PolicyAcceptance( policyVersionId: (int) $form['version']->id, studentId: $userId, registrationType: PolicyAcceptance::REG_ACCOUNT, registrationId: $userId, ipAddress: '' !== $ip ? $ip : null, ) ); } } /** * Signup-scoped policies that have a current published version. * * @return list */ private function signupPolicies(): array { $out = []; foreach ( $this->policies->findForScope( Policy::SCOPE_SIGNUP ) as $policy ) { if ( null === $policy->currentVersionId ) { continue; } $version = $this->versions->findById( $policy->currentVersionId ); if ( null === $version || ! $version->isPublished() ) { continue; } $out[] = [ 'policy' => $policy, 'version' => $version, ]; } return $out; } }