Ryanhub - file viewer
filename: views/public/register.php
branch: main
back to repo
<?php
$pageTitle = "Register – Seven O'Clock Dinner";

$errors = [];
$successMessage = null;
$showFullForm = false;
$secretWordProvided = '';

if (($_SERVER['REQUEST_METHOD'] ?? 'GET') === 'POST') {
    $secretWordExpected = getenv('SECRET_WORD') ?: 'seven';
    $secretWordProvided = trim($_POST['secret_word'] ?? '');

    if (strcasecmp($secretWordProvided, $secretWordExpected) !== 0) {
        $errors[] = 'Incorrect secret word.';
    } else {
        // Secret is correct for this request
        if (isset($_POST['full_name'])) {
            // Full registration submit, re-validate secret and process registration
            $fullName = trim($_POST['full_name'] ?? '');
            $email = trim($_POST['email'] ?? '');
            $password = $_POST['password'] ?? '';
            $bio = trim($_POST['bio'] ?? '');
            $city = trim($_POST['city'] ?? '');
            $rawLinks = $_POST['links'] ?? [];
            if (!is_array($rawLinks)) {
                $rawLinks = [$rawLinks];
            }

            $links = [];
            foreach ($rawLinks as $rawLink) {
                $url = trim($rawLink ?? '');
                if ($url === '') {
                    continue;
                }
                if (!filter_var($url, FILTER_VALIDATE_URL)) {
                    $errors[] = 'Please enter valid URLs for your links (prepend https://).';
                    break;
                }
                $links[] = $url;
            }

            if ($fullName === '' || $email === '' || $password === '') {
                $errors[] = 'Name, email, and password are required.';
            } elseif (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
                $errors[] = 'Please enter a valid email address.';
            } else {
                $stmt = $db->prepare('SELECT id FROM users WHERE email = ?');
                $stmt->bind_param('s', $email);
                $stmt->execute();
                $stmt->store_result();
                if ($stmt->num_rows > 0) {
                    $errors[] = 'This email is already associated with a member.';
                }
            }

            if (!$errors) {
                $hash = password_hash($password, PASSWORD_DEFAULT);
                $status = 'active';
                $now = (new DateTimeImmutable('now'))->format('Y-m-d H:i:s');

                $stmt = $db->prepare('INSERT INTO users (email, password_hash, full_name, is_admin, status, created_at) VALUES (?, ?, ?, 0, ?, ?)');
                $stmt->bind_param('sssss', $email, $hash, $fullName, $status, $now);
                $stmt->execute();
                $userId = $stmt->insert_id;

                $displayName = $fullName;
                $photoUrl = null;
                if (!empty($_FILES['photo']['name']) && $_FILES['photo']['error'] === UPLOAD_ERR_OK) {
                    $file = $_FILES['photo'];
                    if ($file['size'] > 4 * 1024 * 1024) {
                        $errors[] = 'Profile photos are limited to 4MB.';
                    } else {
                        $finfo = finfo_open(FILEINFO_MIME_TYPE);
                        $mime = finfo_file($finfo, $file['tmp_name']);
                        finfo_close($finfo);
                        if (in_array($mime, ['image/jpeg', 'image/png'], true)) {
                            $ext = $mime === 'image/png' ? 'png' : 'jpg';
                            $profilesDir = __DIR__ . '/../../uploads/profiles';
                            if (!is_dir($profilesDir)) {
                                mkdir($profilesDir, 0775, true);
                            }
                            $basename = bin2hex(random_bytes(12)) . '.' . $ext;
                            $target = $profilesDir . '/' . $basename;
                            if (move_uploaded_file($file['tmp_name'], $target)) {
                                $photoUrl = url('uploads/profiles/' . $basename);
                            }
                        }
                    }
                }

                if (!$errors) {
                    $linksJson = $links ? json_encode($links, JSON_THROW_ON_ERROR) : null;
                    $stmt = $db->prepare('INSERT INTO member_profiles (user_id, display_name, photo_url, short_bio, city, links_json) VALUES (?, ?, ?, ?, ?, ?)');
                    $stmt->bind_param('isssss', $userId, $displayName, $photoUrl, $bio, $city, $linksJson);
                    $stmt->execute();

                    // Log in the new user and send them to the members page focused on their card.
                    loginUser($userId, false);
                    header('Location: ' . url('members') . '?user_id=' . (int)$userId . '#member-' . (int)$userId);
                    exit;
                }
            }

            // If we processed the full form (even with errors), we show the full form again
            $showFullForm = true;
        } else {
            // Secret was correct, show the registration form in this response
            $showFullForm = true;
        }
    }
}
?>

<div class="page-grid">
    <div class="card" data-animate-initial>
        <div class="muted" style="font-size: 11px; letter-spacing: 0.18em; text-transform: uppercase; margin-bottom: 10px;">
            Register
        </div>
        <h1 style="font-family: 'Georgia', 'Times New Roman', serif; font-weight: 400; font-size: 26px; margin: 0 0 12px;">
            <?= $showFullForm ? 'Create your account.' : 'Guess the secret word to enter.' ?>
        </h1>
        <div class="muted" style="font-size: 11px; letter-spacing: 0.18em; text-transform: uppercase; margin-bottom: 10px;">
            or <a href="<?= url('signin') ?>" style="text-decoration: underline; color: var(--text-color);">sign in</a>
        </div>
        <?php if ($errors): ?>
            <ul style="margin-top: 16px; padding-left: 18px; color: #9b2c2c; font-size: 13px;">
                <?php foreach ($errors as $err): ?>
                    <li><?= htmlspecialchars($err, ENT_QUOTES, 'UTF-8') ?></li>
                <?php endforeach; ?>
            </ul>
        <?php endif; ?>
        <?php if ($successMessage): ?>
            <p style="margin-top: 14px; font-size: 13px; color: #22543d;">
                <?= htmlspecialchars($successMessage, ENT_QUOTES, 'UTF-8') ?>
            </p>
        <?php endif; ?>
    </div>

    <?php if (!$showFullForm): ?>
        <div class="card" data-animate>
            <form method="post" style="display: grid; gap: 10px; font-size: 13px;">
                <label>
                    <div class="muted" style="font-size: 11px; letter-spacing: 0.18em; text-transform: uppercase; margin-bottom: 10px;">
                        Secret word<br>
                    </div>
                    <input name="secret_word" type="text" required placeholder="Are you in the know?" style="width: 100%; padding: 8px 10px; border-radius: 8px; border: 1px solid rgba(0,0,0,0.12); background: rgba(255,255,255,0.8);">
                </label>
                <button type="submit" class="pill pill-accent" style="margin-top: 6px; justify-content: center;">
                    Enter
                </button>
            </form>
        </div>
    <?php else: ?>
        <div class="card" data-animate>
            <form method="post" enctype="multipart/form-data" style="display: grid; gap: 10px; font-size: 13px;">
                <input type="hidden" name="secret_word" value="<?= htmlspecialchars($secretWordProvided, ENT_QUOTES, 'UTF-8') ?>">
                <label>
                    Full name<br>
                    <input name="full_name" type="text" required style="width: 100%; padding: 8px 10px; border-radius: 8px; border: 1px solid rgba(0,0,0,0.12); background: rgba(255,255,255,0.8);">
                </label>
                <label>
                    Email<br>
                    <input name="email" type="email" required style="width: 100%; padding: 8px 10px; border-radius: 8px; border: 1px solid rgba(0,0,0,0.12); background: rgba(255,255,255,0.8);">
                </label>
                <label>
                    Password<br>
                    <input name="password" type="password" required style="width: 100%; padding: 8px 10px; border-radius: 8px; border: 1px solid rgba(0,0,0,0.12); background: rgba(255,255,255,0.8);">
                </label>
                <label>
                    City (optional)<br>
                    <input name="city" type="text" style="width: 100%; padding: 8px 10px; border-radius: 8px; border: 1px solid rgba(0,0,0,0.12); background: rgba(255,255,255,0.8);">
                </label>
                <label>
                    Links (optional)<br>
                    <div id="link-inputs" style="display: grid; gap: 6px; margin-top: 4px;">
                        <div class="link-row" style="display: flex; gap: 6px;">
                            <input name="links[]" type="url" placeholder="Website, portfolio, etc." style="flex: 1; padding: 8px 10px; border-radius: 8px; border: 1px solid rgba(0,0,0,0.12); background: rgba(255,255,255,0.8);">
                        </div>
                    </div>
                    <button type="button" id="add-link-btn" class="pill" style="margin-top: 6px; font-size: 11px; padding: 4px 10px;">
                        + Add another link
                    </button>
                </label>
                <label>
                    Profile photo (optional)<br>
                    <input name="photo" type="file" accept="image/jpeg,image/png" style="width: 100%; padding: 6px 0;">
                </label>
                <label>
                    Short introduction<br>
                    <textarea name="bio" rows="3" style="width: 100%; padding: 8px 10px; border-radius: 8px; border: 1px solid rgba(0,0,0,0.12); background: rgba(255,255,255,0.8);"></textarea>
                </label>
                <button type="submit" class="pill pill-accent" style="margin-top: 6px; justify-content: center;">
                    Submit application
                </button>
            </form>
            <script>
                (function () {
                    var container = document.getElementById('link-inputs');
                    var addBtn = document.getElementById('add-link-btn');
                    if (!container || !addBtn) return;

                    function createLinkRow() {
                        var row = document.createElement('div');
                        row.className = 'link-row';
                        row.style.display = 'flex';
                        row.style.gap = '6px';

                        var input = document.createElement('input');
                        input.type = 'url';
                        input.name = 'links[]';
                        input.placeholder = 'Another link (optional)';
                        input.style.flex = '1';
                        input.style.padding = '8px 10px';
                        input.style.borderRadius = '8px';
                        input.style.border = '1px solid rgba(0,0,0,0.12)';
                        input.style.background = 'rgba(255,255,255,0.8)';

                        var removeBtn = document.createElement('button');
                        removeBtn.type = 'button';
                        removeBtn.textContent = '×';
                        removeBtn.title = 'Remove';
                        removeBtn.style.border = 'none';
                        removeBtn.style.background = 'transparent';
                        removeBtn.style.cursor = 'pointer';
                        removeBtn.style.fontSize = '16px';
                        removeBtn.style.lineHeight = '1';
                        removeBtn.style.padding = '0 4px';
                        removeBtn.style.color = 'rgba(0,0,0,0.45)';

                        removeBtn.addEventListener('mouseover', function () {
                            removeBtn.style.color = 'rgba(0,0,0,0.7)';
                        });
                        removeBtn.addEventListener('mouseout', function () {
                            removeBtn.style.color = 'rgba(0,0,0,0.45)';
                        });

                        removeBtn.addEventListener('click', function () {
                            container.removeChild(row);
                        });

                        row.appendChild(input);
                        row.appendChild(removeBtn);
                        return row;
                    }

                    addBtn.addEventListener('click', function () {
                        container.appendChild(createLinkRow());
                    });
                })();
            </script>
        </div>
    <?php endif; ?>
</div>