Files
xamxam/public/tfe.php
Pontoporeia 42af4644c5 perf+a11y: WAL mode for SQLite, skip links, :focus-visible, .sr-only
SQLite performance (Database::__construct):
- PRAGMA journal_mode = WAL: eliminates full-DB read locks on write, safe
  for concurrent PHP-FPM workers
- PRAGMA synchronous = NORMAL: durable on commit without full fsync per write
- PRAGMA cache_size = -8000: ~8 MB page cache per connection

Accessibility foundation (WCAG 2.1 AA):
- common.css: add .sr-only utility, .skip-link (hidden until focused),
  global :focus-visible (2px purple outline, 2px offset),
  prefers-reduced-motion guard; remove bare outline:none from
  .site-search__input
- admin.css: same :focus-visible, skip-link, and motion guard scoped to
  admin purple; remove outline:none from .admin-input/.admin-select/
  .admin-textarea and .admin-filters select (both had :focus border rules
  already, so focus is still visually communicated)
- search.css: remove outline:none from .search-filter-select (already has
  :focus border-color rule)
- All 5 public pages (index, search, tfe, apropos, licence): add
  <a href="#main-content" class="skip-link"> as first child of <body>;
  add id="main-content" to <main>
- templates/admin/head.php: same skip link; aria-label="Navigation admin"
  on <nav>; id="main-content" on all 10 admin <main> elements

All 4 test suites pass (unit, integration, security, rate-limit).
2026-03-27 13:45:01 +01:00

227 lines
10 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
require_once __DIR__ . '/../config/bootstrap.php';
require_once APP_ROOT . '/src/Database.php';
if (isset($_GET['id'])) {
$thesisId = intval($_GET['id']);
try {
$db = Database::getInstance();
$data = $db->getThesisById($thesisId);
if (!$data) { header('Location: index.php'); exit; }
} catch (Exception $e) {
error_log("Error loading thesis: " . $e->getMessage());
header('Location: index.php'); exit;
}
} else {
header('Location: index.php'); exit;
}
$currentNav = '';
?>
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title><?= htmlspecialchars($data['title']) ?> Posterg</title>
<link rel="icon" type="image/svg+xml" href="/assets/admin_favicon.svg">
<link rel="stylesheet" href="assets/modern-normalize.min.css">
<link rel="stylesheet" href="assets/common.css">
<link rel="stylesheet" href="assets/tfe.css">
<?php if (php_sapi_name() === 'cli-server'): ?>
<script>
(function poll(){
fetch('/live-reload.php').then(r=>r.json()).then(d=>{
if(d.changed) location.reload(); else setTimeout(poll,1000);
}).catch(()=>setTimeout(poll,2000));
})();
</script>
<?php endif; ?>
</head>
<body class="tfe-body">
<a href="#main-content" class="skip-link">Aller au contenu principal</a>
<?php include APP_ROOT . '/templates/nav.php'; ?>
<?php include APP_ROOT . '/templates/search-bar.php'; ?>
<main class="tfe-main" id="main-content">
<div class="tfe-layout">
<!-- LEFT: info -->
<div class="tfe-left">
<h1 class="tfe-author"><?= htmlspecialchars($data['authors'] ?? 'Auteur inconnu') ?></h1>
<h2 class="tfe-title">
<?= htmlspecialchars($data['title']) ?>
<?php if (!empty($data['subtitle'])): ?>
<?= htmlspecialchars($data['subtitle']) ?>
<?php endif; ?>
</h2>
<div class="tfe-meta-list">
<?php if (!empty($data['orientation'])): ?>
<div class="tfe-meta-item">
<span class="label">Orientation :</span>
<span class="value"><?= htmlspecialchars($data['orientation']) ?></span>
</div>
<?php endif; ?>
<?php if (!empty($data['ap_program'])): ?>
<div class="tfe-meta-item">
<span class="label">Atelier pluridisciplinaire :</span>
<span class="value"><?= htmlspecialchars($data['ap_program']) ?></span>
</div>
<?php endif; ?>
<?php if (!empty($data['year'])): ?>
<div class="tfe-meta-item">
<span class="label">Date :</span>
<span class="value"><?= htmlspecialchars($data['year']) ?></span>
</div>
<?php endif; ?>
<?php if (!empty($data['languages'])): ?>
<div class="tfe-meta-item">
<span class="label">Langue :</span>
<span class="value"><?= htmlspecialchars($data['languages']) ?></span>
</div>
<?php endif; ?>
<?php if (!empty($data['formats'])): ?>
<div class="tfe-meta-item">
<span class="label">Format :</span>
<span class="value"><?= htmlspecialchars($data['formats']) ?></span>
</div>
<?php endif; ?>
<?php if (!empty($data['file_size_info'])): ?>
<div class="tfe-meta-item">
<span class="label">Durée :</span>
<span class="value"><?= htmlspecialchars($data['file_size_info']) ?></span>
</div>
<?php endif; ?>
<?php if (!empty($data['keywords'])): ?>
<div class="tfe-meta-item">
<span class="label">Mots-clés :</span>
<span class="value"><?= htmlspecialchars($data['keywords']) ?></span>
</div>
<?php endif; ?>
<?php if (!empty($data['jury_president'])): ?>
<div class="tfe-meta-item">
<span class="label">Président·e du jury :</span>
<span class="value"><?= htmlspecialchars($data['jury_president']) ?></span>
</div>
<?php endif; ?>
<?php if (!empty($data['jury_promoteurs'])): ?>
<div class="tfe-meta-item">
<span class="label">Promoteur·ice :</span>
<span class="value"><?= htmlspecialchars($data['jury_promoteurs']) ?></span>
</div>
<?php endif; ?>
<?php if (!empty($data['jury_lecteurs'])): ?>
<div class="tfe-meta-item">
<span class="label">Lecteur·ices :</span>
<span class="value"><?= htmlspecialchars($data['jury_lecteurs']) ?></span>
</div>
<?php endif; ?>
<?php if (!empty($data['access_type'])): ?>
<div class="tfe-meta-item">
<span class="label">Accès :</span>
<span class="value"><?= htmlspecialchars($data['access_type']) ?></span>
</div>
<?php endif; ?>
<?php if (!empty($data['license_type'])): ?>
<div class="tfe-meta-item">
<span class="label">Licence :</span>
<span class="value"><?= htmlspecialchars($data['license_type']) ?></span>
</div>
<?php endif; ?>
<?php if (!empty($data['context_note'])): ?>
<div class="tfe-meta-item" style="align-items:start;">
<span class="label">Note :</span>
<span class="value" style="font-style:italic;"><?= nl2br(htmlspecialchars($data['context_note'])) ?></span>
</div>
<?php endif; ?>
<?php if (!empty($data['baiu_link'])): ?>
<div class="tfe-meta-item">
<span class="label">Contact :</span>
<span class="value">
<a href="<?= htmlspecialchars($data['baiu_link']) ?>" target="_blank" rel="noopener">
<?= htmlspecialchars($data['baiu_link']) ?>
</a>
</span>
</div>
<?php endif; ?>
</div>
<?php if (!empty($data['synopsis'])): ?>
<div class="tfe-synopsis-text">
<?= nl2br(htmlspecialchars($data['synopsis'])) ?>
</div>
<?php endif; ?>
<div style="margin-top:1.5rem;">
<a href="index.php" style="font-size:.88rem;color:#666;text-decoration:underline;text-underline-offset:2px;">
← Retour
</a>
</div>
</div>
<!-- RIGHT: media -->
<div class="tfe-right">
<?php
// Determine effective access: need raw access_type_id
// The view exposes 'access_type' (name string). Fetch raw id for gate.
$accessTypeId = null;
try {
$accessStmt = $db->getConnection()->prepare(
"SELECT access_type_id FROM theses WHERE id = ?"
);
$accessStmt->execute([$thesisId]);
$accessTypeId = (int)($accessStmt->fetchColumn() ?? 1);
} catch (\Throwable $e) {}
$isInterdit = ($accessTypeId === 3);
?>
<?php if ($isInterdit): ?>
<p class="tfe-no-files" style="color:#999;font-style:italic;">
Ce TFE n'est pas disponible en ligne.
</p>
<?php elseif (!empty($data['files'])): ?>
<?php foreach ($data['files'] as $file): ?>
<?php $ext = strtolower(pathinfo($file['file_path'], PATHINFO_EXTENSION)); ?>
<div class="tfe-media-block">
<?php if ($ext === 'pdf'): ?>
<embed src="/media.php?path=<?= urlencode($file['file_path']) ?>"
type="application/pdf" width="100%" height="700px">
<?php elseif (in_array($ext, ['jpg','jpeg','png','gif','bmp','webp'])): ?>
<img src="/media.php?path=<?= urlencode($file['file_path']) ?>"
alt="<?= htmlspecialchars($file['file_name']) ?>">
<?php elseif ($ext === 'mp4'): ?>
<video width="100%" controls>
<source src="/media.php?path=<?= urlencode($file['file_path']) ?>" type="video/mp4">
</video>
<?php endif; ?>
<?php if (!empty($file['description'])): ?>
<p class="tfe-file-caption"><?= htmlspecialchars($file['description']) ?></p>
<?php endif; ?>
</div>
<?php endforeach; ?>
<?php else: ?>
<p class="tfe-no-files">Aucun fichier disponible pour ce TFE.</p>
<?php endif; // end !$isInterdit ?>
</div>
</div>
</main>
</body>
</html>