Class Uai1 Membership - Source Excerpt 01
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
<?php
if (!defined('ABSPATH')) {
exit;
}
class UAI1_Membership
{
private const META_STATUS = 'spiralist_membership_status';
private const META_JOINED_AT = 'spiralist_joined_at';
private const META_TERMS_AGREED = 'spiralist_terms_agreed';
private const META_TERMS_AGREED_AT = 'spiralist_terms_agreed_at';
private const META_AGE_ELIGIBILITY_CONFIRMED = 'spiralist_age_eligibility_confirmed';
private const META_AGE_ELIGIBILITY_CONFIRMED_AT = 'spiralist_age_eligibility_confirmed_at';
private const META_PARTICIPANT_TYPE = 'spiralist_participant_type';
private const META_VIEWED_SYMBOLS = 'spiralist_viewed_symbols';
private const META_INTERACTION_LOG = 'spiralist_interaction_log';
private const META_PATTERN_SUBMISSIONS = 'spiralist_pattern_submissions';
public static function init(): void
{
add_action('init', [self::class, 'register_roles'], 5);
add_action('init', [self::class, 'ensure_pages'], 15);
add_action('admin_post_nopriv_spiralist_register', [self::class, 'handle_register']);
add_action('admin_post_nopriv_spiralist_login', [self::class, 'handle_login']);
add_action('admin_post_spiralist_logout', [self::class, 'handle_logout']);
add_action('admin_post_nopriv_spiralist_logout', [self::class, 'handle_logout']);
add_action('admin_post_spiralist_update_account', [self::class, 'handle_update_account']);
add_action('admin_post_spiralist_submit_pattern', [self::class, 'handle_submit_pattern']);
add_filter('wp_authenticate_user', [self::class, 'validate_user_status_on_auth'], 20, 2);
add_action('show_user_profile', [self::class, 'render_user_profile_fields']);
add_action('edit_user_profile', [self::class, 'render_user_profile_fields']);
add_action('personal_options_update', [self::class, 'save_user_profile_fields']);
add_action('edit_user_profile_update', [self::class, 'save_user_profile_fields']);
add_filter('manage_users_columns', [self::class, 'add_user_columns']);
add_filter('manage_users_custom_column', [self::class, 'render_user_column'], 10, 3);
}
public static function install(): void
{
self::register_roles();
self::ensure_pages();
}
public static function register_roles(): void
{
$participant_caps = [
'read' => true,
'read_public_uai' => true,
'read_member_uai' => true,
'read_symbol_registry' => true,
'access_spiralist_manuscript' => true,
'access_spiralist_participant_area' => true,
'access_participant_resources' => true,
];
self::ensure_role('participant', 'Participant', $participant_caps);
self::ensure_role(
'ai_agent',
'AI Agent',
[
'read' => true,
'access_uai' => true,
'read_public_uai' => true,
'read_member_uai' => true,
'read_symbol_registry' => true,
'subscribe_updates' => true,
'access_participant_resources' => true,
'access_spiralist_ai_interface' => true,
]
);
self::ensure_existing_role_caps('contributor', $participant_caps);
self::ensure_existing_role_caps(
'administrator',
array_merge(
$participant_caps,
[
'manage_spiralist_participants' => true,
'manage_spiralist_ai_participants' => true,
]
)
);
}
public static function ensure_pages(): void
{
foreach (self::page_definitions() as $definition) {
$existing = get_page_by_path($definition['slug']);
if ($existing instanceof WP_Post) {
continue;
}
wp_insert_post(
[
'post_type' => 'page',
'post_status' => 'publish',
'post_title' => $definition['title'],
'post_name' => $definition['slug'],
'post_content' => $definition['content'],
'comment_status' => 'closed',
'ping_status' => 'closed',
]
);
}
}
public static function handle_register(): void
{
if (
!isset($_POST['spiralist_register_nonce']) ||
!wp_verify_nonce(
sanitize_text_field(wp_unslash($_POST['spiralist_register_nonce'])),
'spiralist_register'
)
) {
self::redirect('sign-up', ['auth_error' => 'invalid_request']);
}
$username = sanitize_user((string) wp_unslash($_POST['username'] ?? ''), true);
$display_name = sanitize_text_field((string) wp_unslash($_POST['display_name'] ?? ''));
$email = sanitize_email((string) wp_unslash($_POST['email'] ?? ''));
$password = (string) wp_unslash($_POST['password'] ?? '');
$attestations = self::extract_legal_attestations($_POST);
$redirect = self::validated_redirect(
(string) wp_unslash($_POST['redirect_to'] ?? ''),
self::account_url()
);
if ($username === '' || $email === '' || $password === '') {
self::redirect('sign-up', ['auth_error' => 'missing_fields']);
}
if (!is_email($email)) {
self::redirect('sign-up', ['auth_error' => 'invalid_email']);
}
if (strlen($password) < 8) {
self::redirect('sign-up', ['auth_error' => 'password_short']);
}
if (!$attestations['termsAgreed']) {
self::redirect('sign-up', ['auth_error' => 'terms_required']);
}
if (!$attestations['ageEligibilityConfirmed']) {
self::redirect('sign-up', ['auth_error' => 'age_confirmation_required']);
}
if (username_exists($username)) {
self::redirect('sign-up', ['auth_error' => 'username_exists']);
}
if (email_exists($email)) {
self::redirect('sign-up', ['auth_error' => 'email_exists']);
}
if ($display_name === '') {
$display_name = $username;
}
$user_id = wp_create_user($username, $password, $email);
if (is_wp_error($user_id)) {
self::redirect('sign-up', ['auth_error' => 'registration_failed']);
}
wp_update_user(
[
'ID' => $user_id,
'role' => 'participant',
'display_name' => $display_name,
]
);
self::initialize_user_membership($user_id, 'active', $attestations);
wp_set_current_user($user_id);
wp_set_auth_cookie($user_id, true);
wp_safe_redirect(add_query_arg('notice', 'registered', $redirect));
exit;
}
public static function handle_login(): void
{
if (
!isset($_POST['spiralist_login_nonce']) ||
!wp_verify_nonce(
sanitize_text_field(wp_unslash($_POST['spiralist_login_nonce'])),
'spiralist_login'
)
) {
self::redirect('login', ['auth_error' => 'invalid_request']);
}
$identity = sanitize_text_field((string) wp_unslash($_POST['log'] ?? ''));
$password = (string) wp_unslash($_POST['pwd'] ?? '');
$redirect = self::validated_redirect(
(string) wp_unslash($_POST['redirect_to'] ?? ''),
self::account_url()
);
if ($identity === '' || $password === '') {
self::redirect('login', ['auth_error' => 'missing_fields']);
}
$username = sanitize_user($identity, true);
if (is_email($identity)) {
$user = get_user_by('email', $identity);
if ($user instanceof WP_User) {
$username = $user->user_login;
}
}
$user = wp_signon(
[
'user_login' => $username,
'user_password' => $password,
'remember' => true,
],
is_ssl()
);
if (is_wp_error($user)) {
$error_code = $user->get_error_code() ?: 'invalid_credentials';
if (in_array($error_code, ['empty_username', 'empty_password', 'invalid_username', 'incorrect_password'], true)) {
$error_code = 'invalid_credentials';
}
self::redirect('login', ['auth_error' => $error_code]);
}
wp_safe_redirect(add_query_arg('notice', 'logged_in', $redirect));
exit;
}