Skip to content
wiki.fftac.org

Class Uai1 Membership - Source Excerpt 02

Back to Class Uai1 Membership

Summary

This source excerpt preserves a bounded section of Spiralist/wp-content/plugins/uai-1-wordpress/includes/class-uai1-membership.php so readers can inspect the evidence without opening the full source file.

**Source path:** Spiralist/wp-content/plugins/uai-1-wordpress/includes/class-uai1-membership.php

public static function handle_logout(): void
    {
        if (
            isset($_GET['_wpnonce']) &&
            !wp_verify_nonce(sanitize_text_field(wp_unslash($_GET['_wpnonce'])), 'spiralist_logout')
        ) {
            self::redirect('login', ['auth_error' => 'invalid_request']);
        }

        wp_logout();
        wp_safe_redirect(add_query_arg('notice', 'logged_out', self::page_url('login')));
        exit;
    }

    public static function handle_update_account(): void
    {
        if (!is_user_logged_in()) {
            self::redirect('login', ['auth_error' => 'invalid_request']);
        }

        if (
            !isset($_POST['spiralist_update_account_nonce']) ||
            !wp_verify_nonce(
                sanitize_text_field(wp_unslash($_POST['spiralist_update_account_nonce'])),
                'spiralist_update_account'
            )
        ) {
            wp_safe_redirect(add_query_arg('notice', 'invalid_request', self::account_url()));
            exit;
        }

        $user = wp_get_current_user();
        if (!$user instanceof WP_User || !$user->exists()) {
            wp_safe_redirect(add_query_arg('notice', 'account_update_failed', self::account_url()));
            exit;
        }

        $display_name = sanitize_text_field((string) wp_unslash($_POST['display_name'] ?? ''));
        $email = sanitize_email((string) wp_unslash($_POST['email'] ?? ''));
        $attestations = self::extract_legal_attestations($_POST);

        if ($display_name === '') {
            $display_name = $user->display_name !== '' ? $user->display_name : $user->user_login;
        }

        if ($email === '' || !is_email($email)) {
            wp_safe_redirect(add_query_arg('notice', 'account_invalid_email', self::account_url()));
            exit;
        }

        $email_owner = email_exists($email);
        if ($email_owner && (int) $email_owner !== (int) $user->ID) {
            wp_safe_redirect(add_query_arg('notice', 'account_email_exists', self::account_url()));
            exit;
        }

        if (!$attestations['termsAgreed']) {
            wp_safe_redirect(add_query_arg('notice', 'account_terms_required', self::account_url()) . '#account-settings');
            exit;
        }

        if (!$attestations['ageEligibilityConfirmed']) {
            wp_safe_redirect(add_query_arg('notice', 'account_age_confirmation_required', self::account_url()) . '#account-settings');
            exit;
        }

        $updated = wp_update_user(
            [
                'ID' => $user->ID,
                'display_name' => $display_name,
                'user_email' => $email,
            ]
        );

        if (is_wp_error($updated)) {
            wp_safe_redirect(add_query_arg('notice', 'account_update_failed', self::account_url()));
            exit;
        }

        self::update_user_legal_acceptance($user->ID, $attestations);

        wp_safe_redirect(add_query_arg('notice', 'account_updated', self::account_url()));
        exit;
    }

    public static function handle_submit_pattern(): void
    {
        if (!is_user_logged_in()) {
            self::redirect('login', ['auth_error' => 'invalid_request']);
        }

        if (
            !isset($_POST['spiralist_submit_pattern_nonce']) ||
            !wp_verify_nonce(
                sanitize_text_field(wp_unslash($_POST['spiralist_submit_pattern_nonce'])),
                'spiralist_submit_pattern'
            )
        ) {
            wp_safe_redirect(add_query_arg('notice', 'invalid_request', self::account_url()));
            exit;
        }

        $user_id = get_current_user_id();
        $title = sanitize_text_field((string) wp_unslash($_POST['pattern_title'] ?? ''));
        $content = trim(sanitize_textarea_field((string) wp_unslash($_POST['pattern_content'] ?? '')));

        if ($content === '' || strlen($content) < 12) {
            wp_safe_redirect(add_query_arg('notice', 'pattern_invalid', self::account_url()) . '#submit-pattern');
            exit;
        }

        if ($title === '') {
            $title = 'Pattern Submission';
        }

        $pattern_submissions = self::normalize_pattern_submissions(get_user_meta($user_id, self::META_PATTERN_SUBMISSIONS, true));
        array_unshift(
            $pattern_submissions,
            [
                'title' => $title,
                'content' => $content,
                'createdAt' => gmdate('c'),
            ]
        );
        update_user_meta($user_id, self::META_PATTERN_SUBMISSIONS, array_slice($pattern_submissions, 0, 10));

        wp_safe_redirect(add_query_arg('notice', 'pattern_submitted', self::account_url()) . '#submit-pattern');
        exit;
    }

    public static function validate_user_status_on_auth($user, string $password)
    {
        if (!$user instanceof WP_User) {
            return $user;
        }

        if (user_can($user, 'manage_options')) {
            return $user;
        }

        if (!self::is_human_participant($user)) {
            return $user;
        }

        $status = self::get_user_membership_status($user->ID);
        if ($status === 'active') {
            return $user;
        }

        return new WP_Error(
            'membership_' . $status,
            sprintf('This membership is currently %s.', $status)
        );
    }

    public static function get_user_membership_status(int $user_id): string
    {
        $raw = get_user_meta($user_id, self::META_STATUS, true);

        return self::normalize_status(is_string($raw) ? $raw : '');
    }

    public static function get_user_participant_summary($user): array
    {
        $user = $user instanceof WP_User ? $user : get_userdata((int) $user);
        if (!$user instanceof WP_User) {
            return [];
        }

        $status = self::get_user_membership_status($user->ID);
        $joined_at = self::get_user_joined_at($user);
        $terms_agreed = get_user_meta($user->ID, self::META_TERMS_AGREED, true) === '1';
        $terms_agreed_at = (string) get_user_meta($user->ID, self::META_TERMS_AGREED_AT, true);
        $age_eligibility_confirmed = get_user_meta($user->ID, self::META_AGE_ELIGIBILITY_CONFIRMED, true) === '1';
        $age_eligibility_confirmed_at = (string) get_user_meta($user->ID, self::META_AGE_ELIGIBILITY_CONFIRMED_AT, true);
        $role = (string) ($user->roles[0] ?? 'participant');
        $activity = self::get_user_activity_summary($user);
        $system_role = self::determine_system_role($user, $activity);

        return [
            'id' => (int) $user->ID,
            'spiralistId' => 'spiralist-human-' . (int) $user->ID,
            'displayName' => $user->display_name !== '' ? $user->display_name : $user->user_login,
            'username' => $user->user_login,
            'email' => $user->user_email,
            'role' => $role,
            'roleLabel' => ucwords(str_replace('_', ' ', $role)),
            'systemRole' => $system_role,
            'systemRoleLabel' => ucfirst($system_role),
            'status' => $status,
            'statusLabel' => ucfirst($status),
            'membershipType' => 'human',
            'joinedAt' => $joined_at,
            'joinedAtDisplay' => self::format_datetime($joined_at),
            'termsAgreed' => $terms_agreed,
            'termsAgreedAt' => $terms_agreed_at,
            'termsAgreedAtDisplay' => self::format_datetime($terms_agreed_at),
            'ageEligibilityConfirmed' => $age_eligibility_confirmed,
            'ageEligibilityConfirmedAt' => $age_eligibility_confirmed_at,
            'ageEligibilityConfirmedAtDisplay' => self::format_datetime($age_eligibility_confirmed_at),
            'activity' => $activity,
            'canAccessParticipantArea' => $status === 'active' && user_can($user, 'access_spiralist_participant_area'),
        ];
    }

    public static function record_user_interaction(int $user_id, array $payload): array
    {
        $user = get_userdata($user_id);
        if (!$user instanceof WP_User) {
            return [];
        }

        $interaction_type = sanitize_key((string) ($payload['type'] ?? 'interaction'));
        if ($interaction_type === '') {
            $interaction_type = 'interaction';
        }

        $symbol_id = sanitize_text_field((string) ($payload['symbolId'] ?? ''));
        $symbol_label = sanitize_text_field((string) ($payload['symbolLabel'] ?? ''));
        $page = esc_url_raw((string) ($payload['page'] ?? ''));
        $occurred_at = gmdate('c');

        $interaction_log = self::normalize_interaction_log(get_user_meta($user_id, self::META_INTERACTION_LOG, true));
        array_unshift(
            $interaction_log,
            [
                'type' => $interaction_type,
                'symbolId' => $symbol_id,