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>