303 lines
17 KiB
PHP
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();
|