663fde3909
Full source for loveandrosary.com: slide-based Rosary/novena/Divine Mercy Chaplet presentation tool with multi-user roles, SVG bead ring, audio uploads, donate strip, and public session profiles. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
161 lines
5.7 KiB
PHP
161 lines
5.7 KiB
PHP
<?php
|
|
/**
|
|
* profile.php — Public user profile page.
|
|
* Route: /username → ?username=X via .htaccess
|
|
*/
|
|
require_once __DIR__ . '/config/db.php';
|
|
require_once __DIR__ . '/includes/auth.php';
|
|
|
|
_auth_start();
|
|
$pdo = get_pdo();
|
|
$site_name = get_setting('site_name', APP_NAME);
|
|
$logged_in = !empty($_SESSION['user_id']);
|
|
|
|
$req_username = trim($_GET['username'] ?? '');
|
|
|
|
if ($req_username === '') {
|
|
header('Location: ' . BASE_URL . '/');
|
|
exit;
|
|
}
|
|
|
|
// Load user
|
|
$user_stmt = $pdo->prepare('SELECT id, username, display_name FROM users WHERE username = ? LIMIT 1');
|
|
$user_stmt->execute([$req_username]);
|
|
$profile_user = $user_stmt->fetch();
|
|
|
|
if (!$profile_user) {
|
|
http_response_code(404);
|
|
echo '<!DOCTYPE html><html><body style="font-family:system-ui;max-width:500px;margin:60px auto;text-align:center"><h1>404 — User not found</h1><a href="' . BASE_URL . '/">Home</a></body></html>';
|
|
exit;
|
|
}
|
|
|
|
$uid = (int)$profile_user['id'];
|
|
$disp_name = $profile_user['display_name'] ?: $profile_user['username'];
|
|
$initials = strtoupper(mb_substr($disp_name, 0, 1));
|
|
|
|
// Load public sessions
|
|
$sessions = $pdo->prepare("
|
|
SELECT id, name, occasion, mystery_set, subject_name, photo_path, slug, created_at
|
|
FROM sessions
|
|
WHERE user_id = ? AND is_public = 1 AND occasion != 'novena_deceased'
|
|
ORDER BY created_at DESC
|
|
");
|
|
$sessions->execute([$uid]);
|
|
$sessions = $sessions->fetchAll();
|
|
|
|
// Load public novena groups
|
|
$novenas = $pdo->prepare("
|
|
SELECT ng.id, ng.name, ng.mystery_set, ng.subject_name, ng.photo_path, ng.slug, ng.created_at,
|
|
COUNT(s.id) AS day_count
|
|
FROM novena_groups ng
|
|
LEFT JOIN sessions s ON s.novena_group_id = ng.id
|
|
WHERE ng.user_id = ? AND ng.is_public = 1
|
|
GROUP BY ng.id
|
|
ORDER BY ng.created_at DESC
|
|
");
|
|
$novenas->execute([$uid]);
|
|
$novenas = $novenas->fetchAll();
|
|
|
|
// Merge
|
|
$all = [];
|
|
foreach ($sessions as $r) { $r['_type'] = 'session'; $all[] = $r; }
|
|
foreach ($novenas as $r) { $r['_type'] = 'novena'; $all[] = $r; }
|
|
usort($all, fn($a, $b) => strcmp($b['created_at'], $a['created_at']));
|
|
|
|
$mystery_labels = [
|
|
'sorrowful' => 'Sorrowful Mysteries',
|
|
'joyful' => 'Joyful Mysteries',
|
|
'glorious' => 'Glorious Mysteries',
|
|
'luminous' => 'Luminous Mysteries',
|
|
'by_day_of_week' => 'By Day of Week',
|
|
];
|
|
$occasion_labels = [
|
|
'general_rosary' => 'General Rosary',
|
|
'memorial' => 'Memorial',
|
|
'novena_deceased' => 'Novena for Deceased',
|
|
];
|
|
?>
|
|
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<link rel="icon" type="image/svg+xml" href="<?= BASE_URL ?>/favicon.svg">
|
|
<title><?= htmlspecialchars($disp_name) ?> — <?= htmlspecialchars($site_name) ?></title>
|
|
<link rel="stylesheet" href="<?= BASE_URL ?>/assets/css/public.css">
|
|
</head>
|
|
<body>
|
|
|
|
<nav class="pub-nav">
|
|
<a href="<?= BASE_URL ?>/" class="pub-nav-brand">✝ <span><?= htmlspecialchars($site_name) ?></span></a>
|
|
<div class="pub-nav-links">
|
|
<?php if ($logged_in): ?>
|
|
<a href="<?= BASE_URL ?>/admin/">Dashboard</a>
|
|
<a href="<?= BASE_URL ?>/logout">Logout</a>
|
|
<?php else: ?>
|
|
<a href="<?= BASE_URL ?>/login">Sign In</a>
|
|
<a href="<?= BASE_URL ?>/register" class="btn-nav">Get Started</a>
|
|
<?php endif; ?>
|
|
</div>
|
|
</nav>
|
|
|
|
<div class="profile-header">
|
|
<div class="profile-header-inner">
|
|
<div class="profile-avatar"><?= htmlspecialchars($initials) ?></div>
|
|
<div class="profile-info">
|
|
<h1><?= htmlspecialchars($disp_name) ?></h1>
|
|
<p>@<?= htmlspecialchars($profile_user['username']) ?> • <?= count($all) ?> public rosary<?= count($all) !== 1 ? 's' : '' ?></p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="pub-section">
|
|
<h2>Public Rosaries</h2>
|
|
|
|
<?php if (empty($all)): ?>
|
|
<div class="pub-empty">
|
|
<span class="cross">✝</span>
|
|
<p><?= htmlspecialchars($disp_name) ?> has no public rosary sessions yet.</p>
|
|
</div>
|
|
<?php else: ?>
|
|
<div class="card-grid">
|
|
<?php foreach ($all as $row):
|
|
$slug = $row['slug'] ?? '';
|
|
$url = $slug ? (BASE_URL . '/' . rawurlencode($profile_user['username']) . '/' . rawurlencode($slug)) : '#';
|
|
?>
|
|
<a href="<?= htmlspecialchars($url) ?>" class="rosary-card">
|
|
<?php if (!empty($row['photo_path'])): ?>
|
|
<img class="rosary-card-photo"
|
|
src="<?= htmlspecialchars('/' . ltrim($row['photo_path'], '/')) ?>"
|
|
alt="">
|
|
<?php else: ?>
|
|
<div class="rosary-card-photo-placeholder">✝</div>
|
|
<?php endif; ?>
|
|
<div class="rosary-card-body">
|
|
<div class="rosary-card-title"><?= htmlspecialchars($row['name']) ?></div>
|
|
<div class="rosary-card-meta">
|
|
<?php if ($row['_type'] === 'novena'): ?>
|
|
<span class="badge-novena">9-Day Novena</span>
|
|
<?php if ($row['subject_name']): ?>
|
|
<?= htmlspecialchars($row['subject_name']) ?>
|
|
<?php endif; ?>
|
|
<?php else: ?>
|
|
<?= htmlspecialchars($occasion_labels[$row['occasion']] ?? $row['occasion']) ?> •
|
|
<?= htmlspecialchars($mystery_labels[$row['mystery_set']] ?? $row['mystery_set']) ?>
|
|
<?php endif; ?>
|
|
</div>
|
|
</div>
|
|
</a>
|
|
<?php endforeach; ?>
|
|
</div>
|
|
<?php endif; ?>
|
|
</div>
|
|
|
|
<footer class="pub-footer">
|
|
© <?= date('Y') ?> <?= htmlspecialchars($site_name) ?>
|
|
• <a href="<?= BASE_URL ?>/" style="color:inherit">Home</a>
|
|
</footer>
|
|
|
|
</body>
|
|
</html>
|