39301-vm/user.php
2026-04-09 18:45:07 +00:00

303 lines
17 KiB
PHP

<?php
declare(strict_types=1);
require_once __DIR__ . '/includes/layout.php';
library_bootstrap();
function user_profile_copy(string $lang): array
{
$copy = [
'en' => [
'meta_title' => 'My Activity',
'meta_description' => 'Review this browser\'s reading history, recent visits, and document activity in the bilingual library.',
'hero_kicker' => 'Reader profile',
'hero_title' => 'Reading history and visit activity',
'hero_copy' => 'This is a lightweight visitor profile for the current browser. It tracks your catalog visits and document reading flow without requiring a login.',
'browser_only' => 'Browser-only profile',
'browser_only_copy' => 'The data below belongs to this browser token only. Opening the site from another browser or device will create a separate profile.',
'visits' => 'Visits',
'activities' => 'Activities',
'documents_opened' => 'Documents opened',
'last_read' => 'Last read',
'first_seen' => 'First seen',
'last_seen' => 'Last seen',
'preferred_language' => 'Preferred interface language',
'last_page' => 'Last page',
'reading_kicker' => 'Reading history',
'reading_title' => 'Recently opened documents',
'reading_empty_title' => 'No reading history yet',
'reading_empty_copy' => 'Open a public document from the catalog and it will appear here.',
'open_count' => 'Opens',
'open_document' => 'Open document',
'open_catalog' => 'Browse catalog',
'open_reader' => 'Reader used',
'never' => 'Not yet',
'unknown_author' => 'Unknown author',
'untitled' => 'Untitled document',
'visits_kicker' => 'Visit sessions',
'visits_title' => 'Recent visits',
'visits_empty' => 'Your visit list will appear after you browse a few pages.',
'entry_page' => 'Entry page',
'page_views' => 'Page views',
'document_actions' => 'Document actions',
'activity_kicker' => 'Activity timeline',
'activity_title' => 'Latest actions',
'activity_empty' => 'Your recent activity will show up here as you browse.',
'activity_path' => 'Path',
'activity_document' => 'Document',
'activity_catalog_viewed' => 'Opened the catalog',
'activity_document_viewed' => 'Opened a document page',
'activity_reader_opened' => 'Opened the fullscreen reader',
'activity_profile_viewed' => 'Viewed the activity page',
'activity_default' => 'Visited a page',
'time_unknown' => 'Unknown',
'language_en' => 'English',
'language_ar' => 'Arabic',
'language_bilingual' => 'Bilingual',
],
'ar' => [
'meta_title' => 'سجل نشاطي',
'meta_description' => 'راجع سجل القراءة والزيارات الأخيرة ونشاط المستندات لهذا المتصفح داخل المكتبة الثنائية اللغة.',
'hero_kicker' => 'ملف القارئ',
'hero_title' => 'سجل القراءة ونشاط الزيارات',
'hero_copy' => 'هذه صفحة زائر خفيفة خاصة بالمتصفح الحالي. وهي تتتبع زيارات الفهرس ومسار قراءة المستندات بدون الحاجة إلى تسجيل دخول.',
'browser_only' => 'ملف خاص بالمتصفح',
'browser_only_copy' => 'البيانات أدناه مرتبطة برمز هذا المتصفح فقط. فتح الموقع من متصفح أو جهاز آخر سيُنشئ ملفاً منفصلاً.',
'visits' => 'الزيارات',
'activities' => 'الأنشطة',
'documents_opened' => 'المستندات المفتوحة',
'last_read' => 'آخر قراءة',
'first_seen' => 'أول ظهور',
'last_seen' => 'آخر ظهور',
'preferred_language' => 'لغة الواجهة المفضلة',
'last_page' => 'آخر صفحة',
'reading_kicker' => 'سجل القراءة',
'reading_title' => 'المستندات المفتوحة مؤخراً',
'reading_empty_title' => 'لا يوجد سجل قراءة بعد',
'reading_empty_copy' => 'افتح مستنداً عاماً من الفهرس وسيظهر هنا.',
'open_count' => 'مرات الفتح',
'open_document' => 'فتح المستند',
'open_catalog' => 'تصفح الفهرس',
'open_reader' => 'استخدام القارئ',
'never' => 'ليس بعد',
'unknown_author' => 'مؤلف غير معروف',
'untitled' => 'مستند بدون عنوان',
'visits_kicker' => 'جلسات الزيارة',
'visits_title' => 'الزيارات الأخيرة',
'visits_empty' => 'ستظهر قائمة زياراتك بعد تصفح عدة صفحات.',
'entry_page' => 'صفحة البداية',
'page_views' => 'مشاهدات الصفحة',
'document_actions' => 'أنشطة المستندات',
'activity_kicker' => 'الخط الزمني للنشاط',
'activity_title' => 'أحدث الإجراءات',
'activity_empty' => 'سيظهر نشاطك الأخير هنا أثناء التصفح.',
'activity_path' => 'المسار',
'activity_document' => 'المستند',
'activity_catalog_viewed' => 'فتح الفهرس',
'activity_document_viewed' => 'فتح صفحة مستند',
'activity_reader_opened' => 'فتح القارئ بملء الشاشة',
'activity_profile_viewed' => 'عرض صفحة النشاط',
'activity_default' => 'زيارة صفحة',
'time_unknown' => 'غير معروف',
'language_en' => 'الإنجليزية',
'language_ar' => 'العربية',
'language_bilingual' => 'ثنائي اللغة',
],
];
return $copy[$lang] ?? $copy['en'];
}
function user_profile_event_label(string $eventType, array $copy): string
{
return $copy['activity_' . $eventType] ?? $copy['activity_default'];
}
function user_profile_datetime(?string $value, array $copy): string
{
if (!$value) {
return $copy['time_unknown'];
}
$timestamp = strtotime($value);
if ($timestamp === false) {
return $copy['time_unknown'];
}
return date('Y-m-d H:i', $timestamp);
}
$lang = library_get_language();
$pageCopy = user_profile_copy($lang);
library_track_request('profile_viewed');
$summary = library_get_current_reader_summary();
$readingHistory = library_get_current_reader_history(8);
$recentActivities = library_get_current_reader_activities(18);
$recentVisits = library_get_current_reader_visits(8);
$preferredLanguageLabel = library_language_label((string) ($summary['preferred_language'] ?? 'en'));
library_render_header($pageCopy['meta_title'], $pageCopy['meta_description'], 'profile');
?>
<section class="row g-4 mb-4">
<div class="col-lg-8">
<div class="hero-surface profile-hero h-100">
<div class="d-flex justify-content-between align-items-start gap-3 flex-wrap mb-3">
<div>
<div class="eyebrow"><?= h($pageCopy['hero_kicker']) ?></div>
<h1 class="display-6 mb-3"><?= h($pageCopy['hero_title']) ?></h1>
<p class="lead text-secondary mb-0"><?= h($pageCopy['hero_copy']) ?></p>
</div>
<span class="badge text-bg-light"><?= h($pageCopy['browser_only']) ?></span>
</div>
<div class="metric-grid profile-metric-grid mt-4">
<article class="metric-card">
<span class="metric-value"><?= h((string) ($summary['total_visits'] ?? 0)) ?></span>
<span class="metric-label"><?= h($pageCopy['visits']) ?></span>
</article>
<article class="metric-card">
<span class="metric-value"><?= h((string) ($summary['total_activities'] ?? 0)) ?></span>
<span class="metric-label"><?= h($pageCopy['activities']) ?></span>
</article>
<article class="metric-card">
<span class="metric-value"><?= h((string) ($summary['documents_opened'] ?? 0)) ?></span>
<span class="metric-label"><?= h($pageCopy['documents_opened']) ?></span>
</article>
<article class="metric-card">
<span class="metric-value metric-value-small"><?= h(user_profile_datetime($summary['last_read_at'] ?? null, $pageCopy)) ?></span>
<span class="metric-label"><?= h($pageCopy['last_read']) ?></span>
</article>
</div>
</div>
</div>
<div class="col-lg-4">
<div class="panel h-100">
<div class="section-kicker"><?= h($pageCopy['browser_only']) ?></div>
<h2 class="h5 mb-3"><?= h($pageCopy['browser_only_copy']) ?></h2>
<dl class="row small mb-0 profile-summary-list">
<dt class="col-5 text-secondary"><?= h($pageCopy['first_seen']) ?></dt>
<dd class="col-7 mb-3"><?= h(user_profile_datetime($summary['first_seen_at'] ?? null, $pageCopy)) ?></dd>
<dt class="col-5 text-secondary"><?= h($pageCopy['last_seen']) ?></dt>
<dd class="col-7 mb-3"><?= h(user_profile_datetime($summary['last_seen_at'] ?? null, $pageCopy)) ?></dd>
<dt class="col-5 text-secondary"><?= h($pageCopy['preferred_language']) ?></dt>
<dd class="col-7 mb-3"><?= h($preferredLanguageLabel) ?></dd>
<dt class="col-5 text-secondary"><?= h($pageCopy['last_page']) ?></dt>
<dd class="col-7 mb-0"><span class="badge text-bg-light text-wrap"><?= h((string) ($summary['last_path'] ?? '/')) ?></span></dd>
</dl>
</div>
</div>
</section>
<section class="row g-4 mb-4">
<div class="col-lg-8">
<div class="panel h-100">
<div class="d-flex justify-content-between align-items-center gap-3 flex-wrap mb-4">
<div>
<div class="section-kicker"><?= h($pageCopy['reading_kicker']) ?></div>
<h2 class="h4 mb-0"><?= h($pageCopy['reading_title']) ?></h2>
</div>
<a class="link-arrow" href="/index.php"><?= h($pageCopy['open_catalog']) ?></a>
</div>
<?php if (!$readingHistory): ?>
<div class="reader-lock text-center">
<div class="empty-icon mb-3">↗</div>
<h3 class="h5"><?= h($pageCopy['reading_empty_title']) ?></h3>
<p class="text-secondary mb-0"><?= h($pageCopy['reading_empty_copy']) ?></p>
</div>
<?php else: ?>
<div class="row g-3">
<?php foreach ($readingHistory as $item): ?>
<?php $docTitle = library_localized_document_title($item, $lang, $pageCopy['untitled']); ?>
<div class="col-md-6">
<a class="recent-card history-card text-decoration-none" href="/document.php?id=<?= (int) $item['id'] ?>">
<div class="d-flex justify-content-between align-items-start gap-3 mb-2">
<div>
<h3 class="h6 mb-1 text-dark"><?= h($docTitle) ?></h3>
<p class="text-secondary small mb-0"><?= h((string) ($item['author'] ?: $pageCopy['unknown_author'])) ?></p>
</div>
<span class="badge text-bg-light"><?= h(library_language_label((string) ($item['document_language'] ?? 'en'))) ?></span>
</div>
<div class="history-card-meta small text-secondary">
<span><?= h($pageCopy['open_count']) ?>: <?= h((string) $item['open_count']) ?></span>
<span><?= h($pageCopy['last_read']) ?>: <?= h(user_profile_datetime($item['last_opened_at'] ?? null, $pageCopy)) ?></span>
<span><?= h($pageCopy['open_reader']) ?>: <?= h(user_profile_datetime($item['last_reader_opened_at'] ?? null, $pageCopy)) ?></span>
</div>
</a>
</div>
<?php endforeach; ?>
</div>
<?php endif; ?>
</div>
</div>
<div class="col-lg-4">
<div class="panel h-100">
<div class="section-kicker"><?= h($pageCopy['visits_kicker']) ?></div>
<h2 class="h5 mb-3"><?= h($pageCopy['visits_title']) ?></h2>
<?php if (!$recentVisits): ?>
<p class="text-secondary mb-0"><?= h($pageCopy['visits_empty']) ?></p>
<?php else: ?>
<div class="visit-stack">
<?php foreach ($recentVisits as $visit): ?>
<article class="visit-card">
<div class="d-flex justify-content-between align-items-start gap-3 mb-2">
<span class="badge text-bg-light">#<?= h((string) $visit['id']) ?></span>
<span class="small text-secondary"><?= h(user_profile_datetime($visit['started_at'] ?? null, $pageCopy)) ?></span>
</div>
<div class="small text-secondary mb-2"><?= h($pageCopy['entry_page']) ?>: <span class="text-dark"><?= h((string) ($visit['entry_path'] ?? '/')) ?></span></div>
<div class="visit-stats small">
<span><?= h($pageCopy['page_views']) ?>: <?= h((string) $visit['page_views']) ?></span>
<span><?= h($pageCopy['activities']) ?>: <?= h((string) $visit['activity_count']) ?></span>
<span><?= h($pageCopy['document_actions']) ?>: <?= h((string) $visit['document_count']) ?></span>
</div>
</article>
<?php endforeach; ?>
</div>
<?php endif; ?>
</div>
</div>
</section>
<section class="panel">
<div class="d-flex justify-content-between align-items-center gap-3 flex-wrap mb-4">
<div>
<div class="section-kicker"><?= h($pageCopy['activity_kicker']) ?></div>
<h2 class="h4 mb-0"><?= h($pageCopy['activity_title']) ?></h2>
</div>
<span class="badge text-bg-light"><?= h((string) count($recentActivities)) ?></span>
</div>
<?php if (!$recentActivities): ?>
<p class="text-secondary mb-0"><?= h($pageCopy['activity_empty']) ?></p>
<?php else: ?>
<div class="timeline-list">
<?php foreach ($recentActivities as $activity): ?>
<?php $activityTitle = !empty($activity['document_id']) ? library_localized_document_title($activity, $lang, $pageCopy['untitled']) : ''; ?>
<article class="timeline-item">
<div class="timeline-dot" aria-hidden="true"></div>
<div class="timeline-content">
<div class="d-flex justify-content-between align-items-start gap-3 flex-wrap mb-1">
<h3 class="h6 mb-0"><?= h(user_profile_event_label((string) $activity['event_type'], $pageCopy)) ?></h3>
<span class="small text-secondary"><?= h(user_profile_datetime($activity['created_at'] ?? null, $pageCopy)) ?></span>
</div>
<div class="small text-secondary d-grid gap-1">
<span><?= h($pageCopy['activity_path']) ?>: <span class="text-dark"><?= h((string) ($activity['page_path'] ?? '/')) ?></span></span>
<?php if ($activityTitle !== ''): ?>
<span><?= h($pageCopy['activity_document']) ?>: <a class="text-decoration-none" href="/document.php?id=<?= (int) $activity['document_id'] ?>"><?= h($activityTitle) ?></a></span>
<?php endif; ?>
</div>
</div>
</article>
<?php endforeach; ?>
</div>
<?php endif; ?>
</section>
<?php library_render_footer();