diff --git a/TODO.md b/TODO.md
index 6d5c080..fb749aa 100644
--- a/TODO.md
+++ b/TODO.md
@@ -38,16 +38,34 @@
- [ ] Update Dispatcher to handle all routes directly (no require APP_ROOT/public/*.php)
### Phase 2: Single entry point
-- [ ] Create app/public/index.php as front controller
- - [ ] Bootstrap + Dispatcher invocation
-- [ ] Remove direct-access public/*.php (index.php, search.php, tfe.php, apropos.php, licence.php)
-- [ ] Rename old entry points so they can't be hit directly (e.g., prefix with underscore or delete)
+- [x] Create app/public/index.php as front controller
+ - [x] Move bootstrap logic into entry point (bootstrap.php stays for admin)
+ - [x] Load and invoke Dispatcher
+- [x] Move old public/*.php views into templates/public/
+ - [x] search.php → templates/public/search.php
+ - [x] tfe.php → templates/public/tfe.php
+ - [x] apropos.php → templates/public/about.php
+ - [x] repertoire.php → templates/public/repertoire.php
+- [x] Delete old direct-access public/*.php files
+ - [x] Delete public/index.php (replaced by front controller)
+ - [x] Delete public/search.php
+ - [x] Delete public/tfe.php
+ - [x] Delete public/apropos.php
+ - [x] Delete public/licence.php
+ - [x] Delete public/repertoire.php
+- [x] Update Dispatcher.render to use templates/public/ views
+- [x] Update Dispatcher to render full pages (head + header + view + footer) instead of requiring bootstrap
+- [x] Ensure admin/index.php bootstraps its own path (not affected by front controller)
### Phase 3: Server config
- [ ] Update router.php — route all PHP requests to Dispatcher
- [ ] Update nginx config — point all public routes to index.php via try_files
- [ ] Replace per-file `location ~ \.php$` with front-controller pattern
-
-### Phase 4: Cleanup
-- [ ] Delete app/public/live-reload.php (already handled by LiveReloadController)
-- [ ] Test all routes (/, search.php, tfe, repertoire, apropos, licence, media, live-reload)
+- [x] Clean URL updates
+ - [x] Remove .php from all internal links (header, views, controllers)
+ - [x] Add clean routes to Dispatcher (/search, /tfe, /media)
+ - [x] Update og:url tags in controllers to use clean URLs
+ - [x] Update TfeController redirect to /
+ - [x] Update header.php action URLs
+- [x] Commit current state
+- [ ] Test all routes (/, /search, /tfe, /repertoire, /apropos, /licence, /media, /live-reload)
diff --git a/app/public/apropos.php b/app/public/apropos.php
deleted file mode 100644
index 62bc661..0000000
--- a/app/public/apropos.php
+++ /dev/null
@@ -1,152 +0,0 @@
-' . $text . '';
- } else {
- $parts[] = '' . $text . '';
- }
- }
- $count = count($parts);
- if ($count === 1) return $parts[0];
- // Join all but last two with ", ", join last two with " & "
- $prefix = implode(', ', array_slice($parts, 0, $count - 2));
- $suffix = implode(' & ', array_slice($parts, -2));
- return $prefix !== '' ? $prefix . ', ' . $suffix : $suffix;
-}
-
-try {
- $db = Database::getInstance();
-
- // Intro text from pages table
- $aboutPage = $db->getPage('about');
- $rawContent = $aboutPage ? $aboutPage['content'] : '';
- if (empty(trim($rawContent)) || trim($rawContent) === 'Contenu à venir') {
- $rawContent = APROPOS_STATIC_CONTENT;
- }
-
- // Contacts and credits from apropos_contents table
- $contacts = $db->getAproposContent('contacts');
- $credits = $db->getAproposContent('credits');
-
- // Apply defaults if DB returns empty
- $contacts = is_array($contacts) && !empty($contacts) ? $contacts : null;
- $credits = is_array($credits) && !empty($credits) ? $credits : null;
-} catch (Exception $e) {
- error_log("Error loading about page: " . $e->getMessage());
- $rawContent = APROPOS_STATIC_CONTENT;
- $contacts = null;
- $credits = null;
-}
-
-$pd = new Parsedown();
-$pd->setSafeMode(true);
-$aboutHtml = $pd->text($rawContent);
-
-$pageTitle = 'À Propos – Posterg';
-$metaDescription = 'À propos de Posterg, le répertoire des mémoires de fin d\'études de l\'erg – École de Recherches Graphiques de Bruxelles.';
-$ogTags = [
- 'type' => 'website',
- 'title' => $pageTitle,
- 'description' => $metaDescription,
- 'url' => 'https://posterg.erg.be/apropos.php',
- 'site_name' => 'Posterg – ERG',
-];
-$extraCss = ['/assets/css/apropos.css'];
-$bodyClass = 'apropos-body';
-?>
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Crédits
-
-
-
-
- = htmlspecialchars($group['label']) ?>
- - = renderEntries($group['entries'] ?? []) ?>
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/app/public/index.php b/app/public/index.php
index cff0d94..f8e3440 100644
--- a/app/public/index.php
+++ b/app/public/index.php
@@ -1,70 +1,12 @@
handle();
-extract($vars);
-?>
-
-
-
-
-
- Année : = htmlspecialchars($year) ?>
- ✕ Réinitialiser
-
-
- Publication récente
-
-
-
- Mémoires de l'ERG
-
-
-
-
-
-
+$dispatcher = new Dispatcher();
+$dispatcher->dispatch();
diff --git a/app/public/licence.php b/app/public/licence.php
deleted file mode 100644
index a334fb3..0000000
--- a/app/public/licence.php
+++ /dev/null
@@ -1,48 +0,0 @@
-getPage('licenses');
- $content = $dbPage ? $dbPage['content'] : '';
- $licencePageTitle = $dbPage ? $dbPage['title'] : 'Licences';
-} catch (Exception $e) {
- error_log("Error loading licence page: " . $e->getMessage());
- $content = '';
- $licencePageTitle = 'Licences';
-}
-
-$pd = new Parsedown();
-$pd->setSafeMode(true);
-$html = $pd->text($content);
-
-$pageTitle = $licencePageTitle . ' – Posterg';
-$metaDescription = 'Informations sur les licences d\'utilisation des mémoires publiés sur Posterg, le répertoire des TFE de l\'erg.';
-$ogTags = [
- 'type' => 'website',
- 'title' => $pageTitle,
- 'description' => $metaDescription,
- 'url' => 'https://posterg.erg.be/licence.php',
- 'site_name' => 'Posterg – ERG',
-];
-$extraCss = ['/assets/css/apropos.css'];
-$bodyClass = 'apropos-body';
-?>
-
-
-
-
-
-
- = $html ?>
-
-
Contenu à venir.
-
-
-
-
-
diff --git a/app/public/repertoire.php b/app/public/repertoire.php
deleted file mode 100644
index f875465..0000000
--- a/app/public/repertoire.php
+++ /dev/null
@@ -1,21 +0,0 @@
-handleRepertoire());
-?>
-
-
-
-
- Répertoire
-
-
-
-
-
-
diff --git a/app/public/search.php b/app/public/search.php
deleted file mode 100644
index 6114288..0000000
--- a/app/public/search.php
+++ /dev/null
@@ -1,82 +0,0 @@
-handleSearch());
-?>
-
-
-
-
- ⚠ = htmlspecialchars($validationError) ?>
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Aucun résultat pour cette recherche.
-
-
-
-
diff --git a/app/public/tfe.php b/app/public/tfe.php
deleted file mode 100644
index 8764961..0000000
--- a/app/public/tfe.php
+++ /dev/null
@@ -1,263 +0,0 @@
-handle());
-?>
-
-
-
-
-
-
-
-
-
- = htmlspecialchars($data['authors'] ?? 'Auteur inconnu') ?>
-
-
- = htmlspecialchars($data['title']) ?>
-
- – = htmlspecialchars($data['subtitle']) ?>
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Durée :
- - = htmlspecialchars($data['file_size_info']) ?>
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Accès :
- - = htmlspecialchars($data['access_type']) ?>
-
-
-
-
-
-
- Licence :
- - = htmlspecialchars($data['license_type']) ?>
-
-
-
-
-
-
- Note :
- - = nl2br(htmlspecialchars($data['context_note'])) ?>
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- = nl2br(htmlspecialchars($data['synopsis'])) ?>
-
-
-
-
-
-
-
-
-
-
-
diff --git a/app/router.php b/app/router.php
index 195be70..a972369 100644
--- a/app/router.php
+++ b/app/router.php
@@ -2,25 +2,19 @@
/**
* Router script for PHP built-in development server (php -S).
*
- * Routes /partage/ to public/partage/index.php, since the built-in
- * server has no URL rewriting like nginx's try_files.
+ * All non-asset requests go through the front controller (public/index.php).
+ * Static files (CSS, JS, images, fonts) are served directly.
*/
$uri = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);
-// Route /partage/ and /partage// to the partage entry
-if (preg_match('#^/partage(/.*)?$#', $uri)) {
- $_SERVER['SCRIPT_NAME'] = '/partage/index.php';
- require __DIR__ . '/public/partage/index.php';
- return true;
+// Static assets: let the dev server handle them directly
+$ext = pathinfo($uri, PATHINFO_EXTENSION);
+if (in_array($ext, ['css', 'js', 'png', 'jpg', 'jpeg', 'gif', 'ico', 'svg', 'woff', 'woff2', 'ttf', 'eot', 'map'], true)) {
+ return false; // serve directly
}
-// Route /tfe/<...> to tfe.php
-if (preg_match('#^/tfe(/.*)?$#', $uri)) {
- $_SERVER['SCRIPT_NAME'] = '/tfe.php';
- require __DIR__ . '/public/tfe.php';
- return true;
-}
-
-// Default: serve static files if they exist
-return false;
+// Send everything else through the front controller
+$_SERVER['SCRIPT_NAME'] = '/index.php';
+require __DIR__ . '/public/index.php';
+return true;
diff --git a/app/src/Controllers/AboutController.php b/app/src/Controllers/AboutController.php
index d38ad56..3880f5d 100644
--- a/app/src/Controllers/AboutController.php
+++ b/app/src/Controllers/AboutController.php
@@ -30,7 +30,7 @@ class AboutController {
$pd->setSafeMode(true);
return [
- 'nav' => 'apropos',
+ 'currentNav' => 'apropos',
'aboutHtml' => $pd->text($rawContent),
'contacts' => $contacts,
'credits' => $credits,
diff --git a/app/src/Controllers/SearchController.php b/app/src/Controllers/SearchController.php
index d837ed0..767b9e4 100644
--- a/app/src/Controllers/SearchController.php
+++ b/app/src/Controllers/SearchController.php
@@ -124,7 +124,7 @@ class SearchController
'type' => 'website',
'title' => 'Recherche – Posterg',
'description' => "Résultats de recherche dans le répertoire des TFE de l'erg.",
- 'url' => 'https://posterg.erg.be/search.php',
+ 'url' => 'https://posterg.erg.be/search',
'site_name' => 'Posterg – ERG',
],
'currentNav' => 'repertoire',
@@ -174,7 +174,7 @@ class SearchController
'type' => 'website',
'title' => 'Répertoire – Posterg',
'description' => "Parcourez le répertoire des mémoires de fin d'études (TFE) de l'erg – École de Recherches Graphiques de Bruxelles.",
- 'url' => 'https://posterg.erg.be/repertoire.php',
+ 'url' => 'https://posterg.erg.be/repertoire',
'site_name' => 'Posterg – ERG',
],
'currentNav' => 'repertoire',
diff --git a/app/src/Controllers/TfeController.php b/app/src/Controllers/TfeController.php
index e9b5d0e..72248ba 100644
--- a/app/src/Controllers/TfeController.php
+++ b/app/src/Controllers/TfeController.php
@@ -168,7 +168,7 @@ class TfeController
'type' => 'article',
'title' => $title,
'description' => $metaDescription,
- 'url' => self::BASE_URL . '/tfe.php?id=' . $thesisId,
+ 'url' => self::BASE_URL . '/tfe?id=' . $thesisId,
'image' => $ogImage,
'image_alt' => $imageAlt,
'site_name' => 'Posterg – ERG',
@@ -241,7 +241,7 @@ class TfeController
*/
private function redirectHome(): never
{
- header('Location: index.php');
+ header('Location: /');
exit;
}
}
diff --git a/app/src/Dispatcher.php b/app/src/Dispatcher.php
index 36bf265..79665a8 100644
--- a/app/src/Dispatcher.php
+++ b/app/src/Dispatcher.php
@@ -22,10 +22,12 @@ class Dispatcher {
'' => ['controller' => 'HomeController', 'action' => 'handle', 'view' => 'public/home'],
'/' => ['controller' => 'HomeController', 'action' => 'handle', 'view' => 'public/home'],
'/index.php' => ['controller' => 'HomeController', 'action' => 'handle', 'view' => 'public/home'],
- '/search.php' => ['controller' => 'SearchController', 'action' => 'handle', 'view' => 'public/search'],
+ '/search' => ['controller' => 'SearchController', 'action' => 'handleSearch', 'view' => 'public/search'],
+ '/search.php' => ['controller' => 'SearchController', 'action' => 'handleSearch', 'view' => 'public/search'],
'/repertoire' => ['controller' => 'SearchController', 'action' => 'handleRepertoire', 'view' => 'public/repertoire'],
'/repertoire.php' => ['controller' => 'SearchController', 'action' => 'handleRepertoire', 'view' => 'public/repertoire'],
- '/tfe.php' => ['controller' => 'TfeController', 'action' => 'handle', 'view' => 'public/tfe'],
+ '/tfe' => ['controller' => 'TfeController', 'action' => 'handle', 'view' => 'public/tfe'],
+ '/tfe.php' => ['controller' => 'TfeController', 'action' => 'handle', 'view' => 'public/tfe'],
'/apropos' => ['controller' => 'AboutController', 'action' => 'handle', 'view' => 'public/about'],
'/apropos.php' => ['controller' => 'AboutController', 'action' => 'handle', 'view' => 'public/about'],
'/licence' => ['controller' => 'LicenceController', 'action' => 'handle', 'view' => 'public/licence'],
@@ -127,16 +129,17 @@ class Dispatcher {
return self::ROUTES[$path];
}
- // /tfe?id= pattern (TFeController handles the id param internally)
+ // /tfe?id= pattern (TfeController handles the id param internally)
if (preg_match('#^/tfe$#', $path) && isset($_GET['id'])) {
- return self::ROUTES['/tfe.php'];
+ return self::ROUTES['/tfe'];
}
return null;
}
/**
- * Render a view template, passing controller data through extract().
+ * Render a view template wrapped in the full page layout.
+ * Includes head.php, header.php, the view, and footer.php.
*/
private function render(string $view, array $vars): void {
$viewPath = APP_ROOT . '/templates/' . $view . '.php';
@@ -146,6 +149,9 @@ class Dispatcher {
return;
}
extract($vars);
+ include APP_ROOT . '/templates/head.php';
+ include APP_ROOT . '/templates/header.php';
include $viewPath;
+ include APP_ROOT . '/templates/footer.php';
}
}
diff --git a/app/templates/head.php b/app/templates/head.php
index aeb118b..659f565 100644
--- a/app/templates/head.php
+++ b/app/templates/head.php
@@ -72,7 +72,7 @@
diff --git a/app/templates/public/search.php b/app/templates/public/search.php
new file mode 100644
index 0000000..11a12d6
--- /dev/null
+++ b/app/templates/public/search.php
@@ -0,0 +1,67 @@
+
+⚠ = htmlspecialchars($validationError) ?>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Aucun résultat pour cette recherche.
+
+
diff --git a/app/templates/public/tfe.php b/app/templates/public/tfe.php
new file mode 100644
index 0000000..4e3035e
--- /dev/null
+++ b/app/templates/public/tfe.php
@@ -0,0 +1,245 @@
+
+
+
+
+
+
+ = htmlspecialchars($data['authors'] ?? 'Auteur inconnu') ?>
+
+
+ = htmlspecialchars($data['title']) ?>
+
+ – = htmlspecialchars($data['subtitle']) ?>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
- Durée :
+ - = htmlspecialchars($data['file_size_info']) ?>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
- Accès :
+ - = htmlspecialchars($data['access_type']) ?>
+
+
+
+
+
+
- Licence :
+ - = htmlspecialchars($data['license_type']) ?>
+
+
+
+
+
+
- Note :
+ - = nl2br(htmlspecialchars($data['context_note'])) ?>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ = nl2br(htmlspecialchars($data['synopsis'])) ?>
+
+
+
+
+
+
+
+
+
diff --git a/app/templates/search-bar.php b/app/templates/search-bar.php
index 94ab4f3..610905f 100644
--- a/app/templates/search-bar.php
+++ b/app/templates/search-bar.php
@@ -3,7 +3,7 @@
// $searchValue: current search query (optional)
$_sbValue = $searchBarValue ?? $_GET['query'] ?? '';
?>
-