Class Uai1 Membership - Source Excerpt 02
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,