From d2b3c6ca67119b73d49555dd6f2e4a9ce87ccf19 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9ophile=20Gervreau-Mercier?= Date: Thu, 5 Feb 2026 20:07:05 +0100 Subject: [PATCH] Major refactor - update the structure to have monolithic setup - updated deployments - added live-reloading for devops --- .gitignore | 11 + {apps/admin => admin}/.gitignore | 0 {apps/admin => admin}/.htaccess | 0 {apps/admin => admin}/README.md | 0 .../Théophile Gervreau-Mercier_2024_.png | Bin {apps/admin => admin}/edit.php | 2 +- {apps/admin => admin}/formulaire.php | 2 +- {apps/admin => admin}/import.php | 2 +- admin/index.php | 300 ++++++++ {apps/admin => admin}/list.php | 2 +- {apps/admin => admin}/publish.php | 2 +- {apps/admin => admin}/thanks.php | 2 +- apps/admin/assets/normalize.css | 349 --------- apps/admin/assets/simple.css | 673 ------------------ apps/admin/composer.json | 8 - apps/admin/index.php | 301 -------- apps/admin/struct.txt | 14 - apps/admin/test.db-journal | Bin 12824 -> 0 bytes apps/public/.gitignore | 11 - apps/public/README.md | 44 -- apps/public/apropos.php | 67 -- apps/public/assets/fonts/Combinedd.otf | Bin 13164 -> 0 bytes apps/public/assets/normalize.css | 349 --------- apps/public/assets/posterg.css | 559 --------------- .../ad921d60486366258809553a3db49a4a.json | 1 - .../f528764d624db129b32c21fbca0cb8d6.json | 1 - apps/public/composer.json | 8 - apps/public/composer.lock | 388 ---------- apps/public/contact.php | 19 - apps/public/error.log | 113 --- apps/public/inc/header.php | 30 - apps/public/index.php | 87 --- apps/public/licences.php | 18 - apps/public/test_db.php | 40 -- apps/public/tests/Integration/SearchTest.php | 121 ---- apps/public/tests/MIGRATION_SUMMARY.md | 306 -------- apps/public/tests/README.md | 108 --- apps/public/tests/Security/SecurityTest.php | 119 ---- apps/public/tests/Unit/RateLimitTest.php | 58 -- .../assets/grid.css => assets/admin.css | 0 .../icon.svg => assets/admin_favicon.svg | 0 .../assets/posterg.css => assets/common.css | 0 .../assets => assets/fonts}/Combinedd.otf | Bin {apps/public/assets => assets}/icons.svg | 0 assets/main.css | 0 assets/modern-normalize.min.css | 9 + DATABASE_CONFIG.md => docs/DATABASE_CONFIG.md | 0 docs/DEVELOPMENT_GUIDE.md | 507 +++++++++++++ docs/LIVE_RELOAD_SETUP.md | 297 ++++++++ docs/MIGRATION_GUIDE.md | 476 +++++++++++++ .../REPOSITORY_STRUCTURE_ANALYSIS.md | 0 docs/RESTRUCTURE_PLAN.md | 135 ++++ docs/TEST_CENTRALIZATION.md | 299 ++++++++ {apps/public/inc => inc}/footer.php | 0 inc/header.php | 38 + index.php | 95 +++ justfile | 406 +++++++---- justfile.old | 205 ++++++ {shared => lib}/Database.php | 0 {shared => lib}/RateLimit.php | 0 .../ad921d60486366258809553a3db49a4a.json | 1 + .../f528764d624db129b32c21fbca0cb8d6.json | 1 + {shared => lib}/config.php | 0 apps/public/memoire.php => memoire.php | 2 +- scripts/migrate-structure.sh | 169 +++++ scripts/setup-dev.sh | 65 ++ apps/public/search.php => search.php | 4 +- .../ad921d60486366258809553a3db49a4a.json | 1 - .../f528764d624db129b32c21fbca0cb8d6.json | 1 - tests/Integration/SearchTest.php | 49 ++ tests/README.md | 234 ++++++ tests/Security/SecurityTest.php | 67 ++ tests/Unit/DatabaseTest.php | 58 ++ tests/Unit/RateLimitTest.php | 54 ++ {apps/public => tests}/run-tests.php | 58 +- 75 files changed, 3359 insertions(+), 3987 deletions(-) rename {apps/admin => admin}/.gitignore (100%) rename {apps/admin => admin}/.htaccess (100%) rename {apps/admin => admin}/README.md (100%) rename {apps/admin => admin}/data/cover/Théophile Gervreau-Mercier_2024_.png (100%) rename {apps/admin => admin}/edit.php (99%) rename {apps/admin => admin}/formulaire.php (99%) rename {apps/admin => admin}/import.php (99%) create mode 100644 admin/index.php rename {apps/admin => admin}/list.php (99%) rename {apps/admin => admin}/publish.php (98%) rename {apps/admin => admin}/thanks.php (99%) delete mode 100644 apps/admin/assets/normalize.css delete mode 100644 apps/admin/assets/simple.css delete mode 100644 apps/admin/composer.json delete mode 100644 apps/admin/index.php delete mode 100644 apps/admin/struct.txt delete mode 100644 apps/admin/test.db-journal delete mode 100644 apps/public/.gitignore delete mode 100644 apps/public/README.md delete mode 100644 apps/public/apropos.php delete mode 100644 apps/public/assets/fonts/Combinedd.otf delete mode 100644 apps/public/assets/normalize.css delete mode 100644 apps/public/assets/posterg.css delete mode 100644 apps/public/cache/rate_limit/ad921d60486366258809553a3db49a4a.json delete mode 100644 apps/public/cache/rate_limit/f528764d624db129b32c21fbca0cb8d6.json delete mode 100644 apps/public/composer.json delete mode 100644 apps/public/composer.lock delete mode 100644 apps/public/contact.php delete mode 100644 apps/public/error.log delete mode 100644 apps/public/inc/header.php delete mode 100644 apps/public/index.php delete mode 100644 apps/public/licences.php delete mode 100644 apps/public/test_db.php delete mode 100644 apps/public/tests/Integration/SearchTest.php delete mode 100644 apps/public/tests/MIGRATION_SUMMARY.md delete mode 100644 apps/public/tests/README.md delete mode 100644 apps/public/tests/Security/SecurityTest.php delete mode 100644 apps/public/tests/Unit/RateLimitTest.php rename apps/public/assets/grid.css => assets/admin.css (100%) rename apps/admin/assets/icon.svg => assets/admin_favicon.svg (100%) rename apps/admin/assets/posterg.css => assets/common.css (100%) rename {apps/admin/assets => assets/fonts}/Combinedd.otf (100%) rename {apps/public/assets => assets}/icons.svg (100%) create mode 100644 assets/main.css create mode 100644 assets/modern-normalize.min.css rename DATABASE_CONFIG.md => docs/DATABASE_CONFIG.md (100%) create mode 100644 docs/DEVELOPMENT_GUIDE.md create mode 100644 docs/LIVE_RELOAD_SETUP.md create mode 100644 docs/MIGRATION_GUIDE.md rename REPOSITORY_STRUCTURE_ANALYSIS.md => docs/REPOSITORY_STRUCTURE_ANALYSIS.md (100%) create mode 100644 docs/RESTRUCTURE_PLAN.md create mode 100644 docs/TEST_CENTRALIZATION.md rename {apps/public/inc => inc}/footer.php (100%) create mode 100644 inc/header.php create mode 100644 index.php create mode 100644 justfile.old rename {shared => lib}/Database.php (100%) rename {shared => lib}/RateLimit.php (100%) create mode 100644 lib/cache/rate_limit/ad921d60486366258809553a3db49a4a.json create mode 100644 lib/cache/rate_limit/f528764d624db129b32c21fbca0cb8d6.json rename {shared => lib}/config.php (100%) rename apps/public/memoire.php => memoire.php (98%) create mode 100755 scripts/migrate-structure.sh create mode 100755 scripts/setup-dev.sh rename apps/public/search.php => search.php (99%) delete mode 100644 shared/cache/rate_limit/ad921d60486366258809553a3db49a4a.json delete mode 100644 shared/cache/rate_limit/f528764d624db129b32c21fbca0cb8d6.json create mode 100644 tests/Integration/SearchTest.php create mode 100644 tests/README.md create mode 100644 tests/Security/SecurityTest.php create mode 100644 tests/Unit/DatabaseTest.php create mode 100644 tests/Unit/RateLimitTest.php rename {apps/public => tests}/run-tests.php (55%) diff --git a/.gitignore b/.gitignore index ea1d52f..e7c979f 100644 --- a/.gitignore +++ b/.gitignore @@ -19,3 +19,14 @@ front-backend/data/cover/* ### Logs ### formulaire/error.log + +# Vendor directory (third-party code) +vendor/ + +# OS files +.DS_Store +Thumbs.db + +# IDE +.vscode/ +.idea/ diff --git a/apps/admin/.gitignore b/admin/.gitignore similarity index 100% rename from apps/admin/.gitignore rename to admin/.gitignore diff --git a/apps/admin/.htaccess b/admin/.htaccess similarity index 100% rename from apps/admin/.htaccess rename to admin/.htaccess diff --git a/apps/admin/README.md b/admin/README.md similarity index 100% rename from apps/admin/README.md rename to admin/README.md diff --git a/apps/admin/data/cover/Théophile Gervreau-Mercier_2024_.png b/admin/data/cover/Théophile Gervreau-Mercier_2024_.png similarity index 100% rename from apps/admin/data/cover/Théophile Gervreau-Mercier_2024_.png rename to admin/data/cover/Théophile Gervreau-Mercier_2024_.png diff --git a/apps/admin/edit.php b/admin/edit.php similarity index 99% rename from apps/admin/edit.php rename to admin/edit.php index 08e37b5..cade481 100644 --- a/apps/admin/edit.php +++ b/admin/edit.php @@ -7,7 +7,7 @@ if (empty($_SESSION['csrf_token'])) { $_SESSION['csrf_token'] = bin2hex(random_bytes(32)); } -require_once __DIR__ . '/../../shared/Database.php'; +require_once __DIR__ . '/../lib/Database.php'; $thesisId = isset($_GET['id']) ? intval($_GET['id']) : 0; $error = null; diff --git a/apps/admin/formulaire.php b/admin/formulaire.php similarity index 99% rename from apps/admin/formulaire.php rename to admin/formulaire.php index a3ba832..e5a1529 100644 --- a/apps/admin/formulaire.php +++ b/admin/formulaire.php @@ -18,7 +18,7 @@ if (!isset($_POST['csrf_token']) || !isset($_SESSION['csrf_token']) || // Log the content of the $_FILES array error_log("FILES array: " . print_r($_FILES, true)); -require_once __DIR__ . '/../../shared/Database.php'; +require_once __DIR__ . '/../lib/Database.php'; // Helper function to sanitize string input function sanitize_string($input) { diff --git a/apps/admin/import.php b/admin/import.php similarity index 99% rename from apps/admin/import.php rename to admin/import.php index 3413c9f..c47c052 100644 --- a/apps/admin/import.php +++ b/admin/import.php @@ -9,7 +9,7 @@ if (empty($_SESSION['csrf_token'])) { $_SESSION['csrf_token'] = bin2hex(random_bytes(32)); } -require_once __DIR__ . '/../../shared/Database.php'; +require_once __DIR__ . '/../lib/Database.php'; $message = ''; $errors = []; diff --git a/admin/index.php b/admin/index.php new file mode 100644 index 0000000..9285ed2 --- /dev/null +++ b/admin/index.php @@ -0,0 +1,300 @@ +getAllOrientations(); + $apPrograms = $db->getAllAPPrograms(); + $finalityTypes = $db->getAllFinalityTypes(); + $languages = $db->getAllLanguages(); + $formatTypes = $db->getAllFormatTypes(); +} catch (Exception $e) { + error_log("Failed to load form data: " . $e->getMessage()); + die("Erreur lors du chargement du formulaire. Veuillez réessayer plus tard."); +} + +// Get error message and preserved form data from session (if redirected back from error) +$error = isset($_SESSION["form_error"]) ? $_SESSION["form_error"] : null; +$formData = isset($_SESSION["form_data"]) ? $_SESSION["form_data"] : []; + +// Clear session data after retrieving +unset($_SESSION["form_error"]); +unset($_SESSION["form_data"]); + +// Helper function to get old form value +function old($key, $default = "") +{ + global $formData; + return isset($formData[$key]) + ? htmlspecialchars($formData[$key]) + : $default; +} + +// Helper function to check if value was previously selected +function wasSelected($key, $value) +{ + global $formData; + if (!isset($formData[$key])) { + return false; + } + if (is_array($formData[$key])) { + return in_array($value, $formData[$key]); + } + return $formData[$key] == $value; +} +?> + + + + + + + + Formulaire + + + + + + +
+

Formulaire Posterg

+ +
+ +
+ +
+ ⚠️ Erreur: +
+ + +
+ + "> + +

Informations de base

+ +
+ + " required> +
+ +
+ + "> +
+ +
+ + " placeholder="" value="" required> +
+ +

Informations académiques

+ + +
+ + + +
+ + + +
+ +
+ + + +
+ +
+ + "> + +
+ +

À propos du TFE

+ + +
+ + " required> + +
+ +
+ + "> +
+ +
+ + +
+ +
+ + +
+ +
+ + + + +
+ +
+ + + + +
+ +
+ + "> + Séparez les mots-clés par des virgules. Maximum 10 mots-clés. +
+ +
+ + "> + Indiquez la durée (en minutes) ou le nombre de pages de votre TFE. +
+ +
+ + "> +
+ +

Fichiers

+ +
+ + Formats acceptés : JPG, PNG. Taille max : 10MB. + +
+ +
+ + Formats acceptés : PDF, JPG, PNG, MP4, ZIP. Taille max par fichier : 50MB. + Si vous voulez importer un dossier, créez une archive ZIP. + +
+ +
+ + +
+ + + + + \ No newline at end of file diff --git a/apps/admin/list.php b/admin/list.php similarity index 99% rename from apps/admin/list.php rename to admin/list.php index dfb61e6..4be9941 100644 --- a/apps/admin/list.php +++ b/admin/list.php @@ -7,7 +7,7 @@ if (empty($_SESSION['csrf_token'])) { $_SESSION['csrf_token'] = bin2hex(random_bytes(32)); } -require_once __DIR__ . '/../../shared/Database.php'; +require_once __DIR__ . '/../lib/Database.php'; try { $db = new Database(); diff --git a/apps/admin/publish.php b/admin/publish.php similarity index 98% rename from apps/admin/publish.php rename to admin/publish.php index 1608b21..4169018 100644 --- a/apps/admin/publish.php +++ b/admin/publish.php @@ -4,7 +4,7 @@ */ session_start(); -require_once __DIR__ . '/../../shared/Database.php'; +require_once __DIR__ . '/../lib/Database.php'; // Verify CSRF token if (!isset($_POST['csrf_token']) || !isset($_SESSION['csrf_token']) || !hash_equals($_SESSION['csrf_token'], $_POST['csrf_token'])) { diff --git a/apps/admin/thanks.php b/admin/thanks.php similarity index 99% rename from apps/admin/thanks.php rename to admin/thanks.php index 8262507..6f9ad87 100644 --- a/apps/admin/thanks.php +++ b/admin/thanks.php @@ -4,7 +4,7 @@ ini_set('display_errors', 0); ini_set('log_errors', 1); ini_set('error_log', 'error.log'); -require __DIR__ . '/../../shared/Database.php'; +require __DIR__ . '/../lib/Database.php'; // Security: Validate thesis ID parameter $thesisId = null; diff --git a/apps/admin/assets/normalize.css b/apps/admin/assets/normalize.css deleted file mode 100644 index 192eb9c..0000000 --- a/apps/admin/assets/normalize.css +++ /dev/null @@ -1,349 +0,0 @@ -/*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */ - -/* Document - ========================================================================== */ - -/** - * 1. Correct the line height in all browsers. - * 2. Prevent adjustments of font size after orientation changes in iOS. - */ - -html { - line-height: 1.15; /* 1 */ - -webkit-text-size-adjust: 100%; /* 2 */ -} - -/* Sections - ========================================================================== */ - -/** - * Remove the margin in all browsers. - */ - -body { - margin: 0; -} - -/** - * Render the `main` element consistently in IE. - */ - -main { - display: block; -} - -/** - * Correct the font size and margin on `h1` elements within `section` and - * `article` contexts in Chrome, Firefox, and Safari. - */ - -h1 { - font-size: 2em; - margin: 0.67em 0; -} - -/* Grouping content - ========================================================================== */ - -/** - * 1. Add the correct box sizing in Firefox. - * 2. Show the overflow in Edge and IE. - */ - -hr { - box-sizing: content-box; /* 1 */ - height: 0; /* 1 */ - overflow: visible; /* 2 */ -} - -/** - * 1. Correct the inheritance and scaling of font size in all browsers. - * 2. Correct the odd `em` font sizing in all browsers. - */ - -pre { - font-family: monospace, monospace; /* 1 */ - font-size: 1em; /* 2 */ -} - -/* Text-level semantics - ========================================================================== */ - -/** - * Remove the gray background on active links in IE 10. - */ - -a { - background-color: transparent; -} - -/** - * 1. Remove the bottom border in Chrome 57- - * 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari. - */ - -abbr[title] { - border-bottom: none; /* 1 */ - text-decoration: underline; /* 2 */ - text-decoration: underline dotted; /* 2 */ -} - -/** - * Add the correct font weight in Chrome, Edge, and Safari. - */ - -b, -strong { - font-weight: bolder; -} - -/** - * 1. Correct the inheritance and scaling of font size in all browsers. - * 2. Correct the odd `em` font sizing in all browsers. - */ - -code, -kbd, -samp { - font-family: monospace, monospace; /* 1 */ - font-size: 1em; /* 2 */ -} - -/** - * Add the correct font size in all browsers. - */ - -small { - font-size: 80%; -} - -/** - * Prevent `sub` and `sup` elements from affecting the line height in - * all browsers. - */ - -sub, -sup { - font-size: 75%; - line-height: 0; - position: relative; - vertical-align: baseline; -} - -sub { - bottom: -0.25em; -} - -sup { - top: -0.5em; -} - -/* Embedded content - ========================================================================== */ - -/** - * Remove the border on images inside links in IE 10. - */ - -img { - border-style: none; -} - -/* Forms - ========================================================================== */ - -/** - * 1. Change the font styles in all browsers. - * 2. Remove the margin in Firefox and Safari. - */ - -button, -input, -optgroup, -select, -textarea { - font-family: inherit; /* 1 */ - font-size: 100%; /* 1 */ - line-height: 1.15; /* 1 */ - margin: 0; /* 2 */ -} - -/** - * Show the overflow in IE. - * 1. Show the overflow in Edge. - */ - -button, -input { /* 1 */ - overflow: visible; -} - -/** - * Remove the inheritance of text transform in Edge, Firefox, and IE. - * 1. Remove the inheritance of text transform in Firefox. - */ - -button, -select { /* 1 */ - text-transform: none; -} - -/** - * Correct the inability to style clickable types in iOS and Safari. - */ - -button, -[type="button"], -[type="reset"], -[type="submit"] { - -webkit-appearance: button; -} - -/** - * Remove the inner border and padding in Firefox. - */ - -button::-moz-focus-inner, -[type="button"]::-moz-focus-inner, -[type="reset"]::-moz-focus-inner, -[type="submit"]::-moz-focus-inner { - border-style: none; - padding: 0; -} - -/** - * Restore the focus styles unset by the previous rule. - */ - -button:-moz-focusring, -[type="button"]:-moz-focusring, -[type="reset"]:-moz-focusring, -[type="submit"]:-moz-focusring { - outline: 1px dotted ButtonText; -} - -/** - * Correct the padding in Firefox. - */ - -fieldset { - padding: 0.35em 0.75em 0.625em; -} - -/** - * 1. Correct the text wrapping in Edge and IE. - * 2. Correct the color inheritance from `fieldset` elements in IE. - * 3. Remove the padding so developers are not caught out when they zero out - * `fieldset` elements in all browsers. - */ - -legend { - box-sizing: border-box; /* 1 */ - color: inherit; /* 2 */ - display: table; /* 1 */ - max-width: 100%; /* 1 */ - padding: 0; /* 3 */ - white-space: normal; /* 1 */ -} - -/** - * Add the correct vertical alignment in Chrome, Firefox, and Opera. - */ - -progress { - vertical-align: baseline; -} - -/** - * Remove the default vertical scrollbar in IE 10+. - */ - -textarea { - overflow: auto; -} - -/** - * 1. Add the correct box sizing in IE 10. - * 2. Remove the padding in IE 10. - */ - -[type="checkbox"], -[type="radio"] { - box-sizing: border-box; /* 1 */ - padding: 0; /* 2 */ -} - -/** - * Correct the cursor style of increment and decrement buttons in Chrome. - */ - -[type="number"]::-webkit-inner-spin-button, -[type="number"]::-webkit-outer-spin-button { - height: auto; -} - -/** - * 1. Correct the odd appearance in Chrome and Safari. - * 2. Correct the outline style in Safari. - */ - -[type="search"] { - -webkit-appearance: textfield; /* 1 */ - outline-offset: -2px; /* 2 */ -} - -/** - * Remove the inner padding in Chrome and Safari on macOS. - */ - -[type="search"]::-webkit-search-decoration { - -webkit-appearance: none; -} - -/** - * 1. Correct the inability to style clickable types in iOS and Safari. - * 2. Change font properties to `inherit` in Safari. - */ - -::-webkit-file-upload-button { - -webkit-appearance: button; /* 1 */ - font: inherit; /* 2 */ -} - -/* Interactive - ========================================================================== */ - -/* - * Add the correct display in Edge, IE 10+, and Firefox. - */ - -details { - display: block; -} - -/* - * Add the correct display in all browsers. - */ - -summary { - display: list-item; -} - -/* Misc - ========================================================================== */ - -/** - * Add the correct display in IE 10+. - */ - -template { - display: none; -} - -/** - * Add the correct display in IE 10. - */ - -[hidden] { - display: none; -} diff --git a/apps/admin/assets/simple.css b/apps/admin/assets/simple.css deleted file mode 100644 index b67d07a..0000000 --- a/apps/admin/assets/simple.css +++ /dev/null @@ -1,673 +0,0 @@ -/* Global variables. */ -:root, -::backdrop { - /* Set sans-serif & mono fonts */ - --sans-font: -apple-system, BlinkMacSystemFont, "Avenir Next", Avenir, - "Nimbus Sans L", Roboto, "Noto Sans", "Segoe UI", Arial, Helvetica, - "Helvetica Neue", sans-serif; - --mono-font: Consolas, Menlo, Monaco, "Andale Mono", "Ubuntu Mono", monospace; - --standard-border-radius: 5px; - - /* Default (light) theme */ - --bg: #fff; - --accent-bg: #f5f7ff; - --text: #212121; - --text-light: #585858; - --border: #898EA4; - --accent: #0d47a1; - --code: #d81b60; - --preformatted: #444; - --marked: #ffdd33; - --disabled: #efefef; -} - -/* Dark theme */ -@media (prefers-color-scheme: dark) { - :root, - ::backdrop { - color-scheme: dark; - --bg: #212121; - --accent-bg: #2b2b2b; - --text: #dcdcdc; - --text-light: #ababab; - --accent: #ffb300; - --code: #f06292; - --preformatted: #ccc; - --disabled: #111; - } - /* Add a bit of transparency so light media isn't so glaring in dark mode */ - img, - video { - opacity: 0.8; - } -} - -/* Reset box-sizing */ -*, *::before, *::after { - box-sizing: border-box; -} - -/* Reset default appearance */ -textarea, -select, -input, -progress { - appearance: none; - -webkit-appearance: none; - -moz-appearance: none; -} - -html { - /* Set the font globally */ - font-family: var(--sans-font); - scroll-behavior: smooth; -} - -/* Make the body a nice central block */ -body { - color: var(--text); - background-color: var(--bg); - font-size: 1.15rem; - line-height: 1.5; - display: grid; - grid-template-columns: 1fr min(45rem, 90%) 1fr; - margin: 0; -} -body > * { - grid-column: 2; -} - -/* Make the header bg full width, but the content inline with body */ -body > header { - background-color: var(--accent-bg); - border-bottom: 1px solid var(--border); - text-align: center; - padding: 0 0.5rem 2rem 0.5rem; - grid-column: 1 / -1; -} - -body > header h1 { - max-width: 1200px; - margin: 1rem auto; -} - -body > header p { - max-width: 40rem; - margin: 1rem auto; -} - -/* Add a little padding to ensure spacing is correct between content and header > nav */ -main { - padding-top: 1.5rem; -} - -body > footer { - margin-top: 4rem; - padding: 2rem 1rem 1.5rem 1rem; - color: var(--text-light); - font-size: 0.9rem; - text-align: center; - border-top: 1px solid var(--border); -} - -/* Format headers */ -h1 { - font-size: 3rem; -} - -h2 { - font-size: 2.6rem; - margin-top: 3rem; -} - -h3 { - font-size: 2rem; - margin-top: 3rem; -} - -h4 { - font-size: 1.44rem; -} - -h5 { - font-size: 1.15rem; -} - -h6 { - font-size: 0.96rem; -} - -/* Prevent long strings from overflowing container */ -p, h1, h2, h3, h4, h5, h6 { - overflow-wrap: break-word; -} - -/* Fix line height when title wraps */ -h1, -h2, -h3 { - line-height: 1.1; -} - -/* Reduce header size on mobile */ -@media only screen and (max-width: 720px) { - h1 { - font-size: 2.5rem; - } - - h2 { - font-size: 2.1rem; - } - - h3 { - font-size: 1.75rem; - } - - h4 { - font-size: 1.25rem; - } -} - -/* Format links & buttons */ -a, -a:visited { - color: var(--accent); -} - -a:hover { - text-decoration: none; -} - -button, -[role="button"], -input[type="submit"], -input[type="reset"], -input[type="button"], -label[type="button"] { - border: none; - border-radius: var(--standard-border-radius); - background-color: var(--accent); - font-size: 1rem; - color: var(--bg); - padding: 0.7rem 0.9rem; - margin: 0.5rem 0; -} - -button[disabled], -[role="button"][aria-disabled="true"], -input[type="submit"][disabled], -input[type="reset"][disabled], -input[type="button"][disabled], -input[type="checkbox"][disabled], -input[type="radio"][disabled], -select[disabled] { - cursor: not-allowed; -} - -input:disabled, -textarea:disabled, -select:disabled, -button[disabled] { - cursor: not-allowed; - background-color: var(--disabled); - color: var(--text-light) -} - -input[type="range"] { - padding: 0; -} - -/* Set the cursor to '?' on an abbreviation and style the abbreviation to show that there is more information underneath */ -abbr[title] { - cursor: help; - text-decoration-line: underline; - text-decoration-style: dotted; -} - -button:enabled:hover, -[role="button"]:not([aria-disabled="true"]):hover, -input[type="submit"]:enabled:hover, -input[type="reset"]:enabled:hover, -input[type="button"]:enabled:hover, -label[type="button"]:hover { - filter: brightness(1.4); - cursor: pointer; -} - -button:focus-visible:where(:enabled, [role="button"]:not([aria-disabled="true"])), -input:enabled:focus-visible:where( - [type="submit"], - [type="reset"], - [type="button"] -) { - outline: 2px solid var(--accent); - outline-offset: 1px; -} - -/* Format navigation */ -header > nav { - font-size: 1rem; - line-height: 2; - padding: 1rem 0 0 0; -} - -/* Use flexbox to allow items to wrap, as needed */ -header > nav ul, -header > nav ol { - align-content: space-around; - align-items: center; - display: flex; - flex-direction: row; - flex-wrap: wrap; - justify-content: center; - list-style-type: none; - margin: 0; - padding: 0; -} - -/* List items are inline elements, make them behave more like blocks */ -header > nav ul li, -header > nav ol li { - display: inline-block; -} - -header > nav a, -header > nav a:visited { - margin: 0 0.5rem 1rem 0.5rem; - border: 1px solid var(--border); - border-radius: var(--standard-border-radius); - color: var(--text); - display: inline-block; - padding: 0.1rem 1rem; - text-decoration: none; -} - -header > nav a:hover { - border-color: var(--accent); - color: var(--accent); - cursor: pointer; -} - -/* Reduce nav side on mobile */ -@media only screen and (max-width: 720px) { - header > nav a { - border: none; - padding: 0; - text-decoration: underline; - line-height: 1; - } -} - -/* Consolidate box styling */ -aside, details, pre, progress { - background-color: var(--accent-bg); - border: 1px solid var(--border); - border-radius: var(--standard-border-radius); - margin-bottom: 1rem; -} - -aside { - font-size: 1rem; - width: 30%; - padding: 0 15px; - margin-left: 15px; - float: right; -} - -/* Make aside full-width on mobile */ -@media only screen and (max-width: 720px) { - aside { - width: 100%; - float: none; - margin-left: 0; - } -} - -article, fieldset, dialog { - border: 1px solid var(--border); - padding: 1rem; - border-radius: var(--standard-border-radius); - margin-bottom: 1rem; -} - -article h2:first-child, -section h2:first-child { - margin-top: 1rem; -} - -section { - border-top: 1px solid var(--border); - border-bottom: 1px solid var(--border); - padding: 2rem 1rem; - margin: 3rem 0; -} - -/* Don't double separators when chaining sections */ -section + section, -section:first-child { - border-top: 0; - padding-top: 0; -} - -section:last-child { - border-bottom: 0; - padding-bottom: 0; -} - -details { - padding: 0.7rem 1rem; -} - -summary { - cursor: pointer; - font-weight: bold; - padding: 0.7rem 1rem; - margin: -0.7rem -1rem; - word-break: break-all; -} - -details[open] > summary + * { - margin-top: 0; -} - -details[open] > summary { - margin-bottom: 0.5rem; -} - -details[open] > :last-child { - margin-bottom: 0; -} - -/* Format tables */ -table { - border-collapse: collapse; - margin: 1.5rem 0; -} - -td, -th { - border: 1px solid var(--border); - text-align: left; - padding: 0.5rem; -} - -th { - background-color: var(--accent-bg); - font-weight: bold; -} - -tr:nth-child(even) { - /* Set every other cell slightly darker. Improves readability. */ - background-color: var(--accent-bg); -} - -table caption { - font-weight: bold; - margin-bottom: 0.5rem; -} - -/* Format forms */ -textarea, -select, -input { - font-size: inherit; - font-family: inherit; - padding: 0.5rem; - margin-bottom: 0.5rem; - color: var(--text); - background-color: var(--bg); - border: 1px solid var(--border); - border-radius: var(--standard-border-radius); - box-shadow: none; - max-width: 100%; - display: inline-block; -} -label { - display: block; -} -textarea:not([cols]) { - width: 100%; -} - -/* Add arrow to drop-down */ -select:not([multiple]) { - background-image: linear-gradient(45deg, transparent 49%, var(--text) 51%), - linear-gradient(135deg, var(--text) 51%, transparent 49%); - background-position: calc(100% - 15px), calc(100% - 10px); - background-size: 5px 5px, 5px 5px; - background-repeat: no-repeat; - padding-right: 25px; -} - -/* checkbox and radio button style */ -input[type="checkbox"], -input[type="radio"] { - vertical-align: middle; - position: relative; - width: min-content; -} - -input[type="checkbox"] + label, -input[type="radio"] + label { - display: inline-block; -} - -input[type="radio"] { - border-radius: 100%; -} - -input[type="checkbox"]:checked, -input[type="radio"]:checked { - background-color: var(--accent); -} - -input[type="checkbox"]:checked::after { - /* Creates a rectangle with colored right and bottom borders which is rotated to look like a check mark */ - content: " "; - width: 0.18em; - height: 0.32em; - border-radius: 0; - position: absolute; - top: 0.05em; - left: 0.17em; - background-color: transparent; - border-right: solid var(--bg) 0.08em; - border-bottom: solid var(--bg) 0.08em; - font-size: 1.8em; - transform: rotate(45deg); -} -input[type="radio"]:checked::after { - /* creates a colored circle for the checked radio button */ - content: " "; - width: 0.25em; - height: 0.25em; - border-radius: 100%; - position: absolute; - top: 0.125em; - background-color: var(--bg); - left: 0.125em; - font-size: 32px; -} - -/* Makes input fields wider on smaller screens */ -@media only screen and (max-width: 720px) { - textarea, - select, - input { - width: 100%; - } -} - -/* Set a height for color input */ -input[type="color"] { - height: 2.5rem; - padding: 0.2rem; -} - -/* do not show border around file selector button */ -input[type="file"] { - border: 0; -} - -/* Misc body elements */ -hr { - border: none; - height: 1px; - background: var(--border); - margin: 1rem auto; -} - -mark { - padding: 2px 5px; - border-radius: var(--standard-border-radius); - background-color: var(--marked); - color: black; -} - -img, -video { - max-width: 100%; - height: auto; - border-radius: var(--standard-border-radius); -} - -figure { - margin: 0; - display: block; - overflow-x: auto; -} - -figcaption { - text-align: center; - font-size: 0.9rem; - color: var(--text-light); - margin-bottom: 1rem; -} - -blockquote { - margin: 2rem 0 2rem 2rem; - padding: 0.4rem 0.8rem; - border-left: 0.35rem solid var(--accent); - color: var(--text-light); - font-style: italic; -} - -cite { - font-size: 0.9rem; - color: var(--text-light); - font-style: normal; -} - -dt { - color: var(--text-light); -} - -/* Use mono font for code elements */ -code, -pre, -pre span, -kbd, -samp { - font-family: var(--mono-font); - color: var(--code); -} - -kbd { - color: var(--preformatted); - border: 1px solid var(--preformatted); - border-bottom: 3px solid var(--preformatted); - border-radius: var(--standard-border-radius); - padding: 0.1rem 0.4rem; -} - -pre { - padding: 1rem 1.4rem; - max-width: 100%; - overflow: auto; - color: var(--preformatted); -} - -/* Fix embedded code within pre */ -pre code { - color: var(--preformatted); - background: none; - margin: 0; - padding: 0; -} - -/* Progress bars */ -/* Declarations are repeated because you */ -/* cannot combine vendor-specific selectors */ -progress { - width: 100%; -} - -progress:indeterminate { - background-color: var(--accent-bg); -} - -progress::-webkit-progress-bar { - border-radius: var(--standard-border-radius); - background-color: var(--accent-bg); -} - -progress::-webkit-progress-value { - border-radius: var(--standard-border-radius); - background-color: var(--accent); -} - -progress::-moz-progress-bar { - border-radius: var(--standard-border-radius); - background-color: var(--accent); - transition-property: width; - transition-duration: 0.3s; -} - -progress:indeterminate::-moz-progress-bar { - background-color: var(--accent-bg); -} - -dialog { - max-width: 40rem; - margin: auto; -} - -dialog::backdrop { - background-color: var(--bg); - opacity: 0.8; -} - -@media only screen and (max-width: 720px) { - dialog { - max-width: 100%; - margin: auto 1em; - } -} - -/* Classes for buttons and notices */ -.button, -.button:visited { - display: inline-block; - text-decoration: none; - border: none; - border-radius: 5px; - background: var(--accent); - font-size: 1rem; - color: var(--bg); - padding: 0.7rem 0.9rem; - margin: 0.5rem 0; -} - -.button:hover, -.button:focus { - filter: brightness(1.4); - cursor: pointer; -} - -.notice { - background: var(--accent-bg); - border: 2px solid var(--border); - border-radius: 5px; - padding: 1.5rem; - margin: 2rem 0; -} \ No newline at end of file diff --git a/apps/admin/composer.json b/apps/admin/composer.json deleted file mode 100644 index f6204fb..0000000 --- a/apps/admin/composer.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "require": { - "symfony/polyfill-iconv": "^1.27", - "symfony/yaml": "^6.2", - "symfony/intl": "^6.2", - "behat/transliterator": "^1.5" - } -} diff --git a/apps/admin/index.php b/apps/admin/index.php deleted file mode 100644 index b671d74..0000000 --- a/apps/admin/index.php +++ /dev/null @@ -1,301 +0,0 @@ -getAllOrientations(); - $apPrograms = $db->getAllAPPrograms(); - $finalityTypes = $db->getAllFinalityTypes(); - $languages = $db->getAllLanguages(); - $formatTypes = $db->getAllFormatTypes(); -} catch (Exception $e) { - error_log("Failed to load form data: " . $e->getMessage()); - die( - "Erreur lors du chargement du formulaire. Veuillez réessayer plus tard." - ); -} - -// Get error message and preserved form data from session (if redirected back from error) -$error = isset($_SESSION["form_error"]) ? $_SESSION["form_error"] : null; -$formData = isset($_SESSION["form_data"]) ? $_SESSION["form_data"] : []; - -// Clear session data after retrieving -unset($_SESSION["form_error"]); -unset($_SESSION["form_data"]); - -// Helper function to get old form value -function old($key, $default = "") -{ - global $formData; - return isset($formData[$key]) - ? htmlspecialchars($formData[$key]) - : $default; -} - -// Helper function to check if value was previously selected -function wasSelected($key, $value) -{ - global $formData; - if (!isset($formData[$key])) { - return false; - } - if (is_array($formData[$key])) { - return in_array($value, $formData[$key]); - } - return $formData[$key] == $value; -} -?> - - - - - - - Formulaire - - - - - - - -
-

Formulaire Posterg

- -
- -
- -
- ⚠️ Erreur: -
- - -
- - "> - -

Informations de base

- -
- - " required> -
- -
- - "> -
- -
- - " placeholder="" value="" required> -
- -

Informations académiques

- - -
- - - -
- - - -
- -
- - - -
- -
- - "> - -
- -

À propos du TFE

- - -
- - " required> - -
- -
- - "> -
- -
- - -
- -
- - -
- -
- - - - -
- -
- - - - -
- -
- - "> - Séparez les mots-clés par des virgules. Maximum 10 mots-clés. -
- -
- - "> - Indiquez la durée (en minutes) ou le nombre de pages de votre TFE. -
- -
- - "> -
- -

Fichiers

- -
- - Formats acceptés : JPG, PNG. Taille max : 10MB. - -
- -
- - Formats acceptés : PDF, JPG, PNG, MP4, ZIP. Taille max par fichier : 50MB. - Si vous voulez importer un dossier, créez une archive ZIP. - -
- -
- - -
- - - - diff --git a/apps/admin/struct.txt b/apps/admin/struct.txt deleted file mode 100644 index 197348d..0000000 --- a/apps/admin/struct.txt +++ /dev/null @@ -1,14 +0,0 @@ -. -├── assets -├── composer.json -├── composer.lock -├── data -├── error.log -├── formulaire.php -├── index.php -├── README.md -├── struct.txt -├── thanks.php -└── vendor - -4 directories, 8 files diff --git a/apps/admin/test.db-journal b/apps/admin/test.db-journal deleted file mode 100644 index 6cf88e7d4bb05458446db6d4061bcc4cedd2ef7e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 12824 zcmeI&y-EW?6o%oMopbg_kRwn0XE_lSVXW0f+UyWUD;_RHbM$Pun`4O zEMnu?RTGxYnetj#B={6;=3#DBG}Yk?a}US zVmEf>12hB>KmY**5I_I{1Q0*~0R#|ODuF^`jJcF;supdE=DEZwiniUP(q<+pm&%uC z>QSZdbD!?2@2IrP<{aS7p6tQy?AES*fQA492q1s}0tg_000IagfB*u^D4=C(jsvvt zP}ST+R5ZoP!#hbed1NA4s(fMe_s=;%JUrMx84c>=)3f2xsNV6LATfr$y7rBZ{qg%D zv$o}x%z7cjU-hDL9D3(d_1R=E&6>V_*!x$x=o7{fKmY**5I_I{1Q0*~0R#|0;4cKW Pd;XWsroT4UoBIHthXyH3 diff --git a/apps/public/.gitignore b/apps/public/.gitignore deleted file mode 100644 index 48a0889..0000000 --- a/apps/public/.gitignore +++ /dev/null @@ -1,11 +0,0 @@ -vendor/ -compose.lock - -### Data et Mémoire### -formulaire/data/yaml/* -formulaire/data/content/* -formulaire/data/cover/* - -front-backend/data/yaml/* -front-backend/data/content/* -front-backend/data/cover/* diff --git a/apps/public/README.md b/apps/public/README.md deleted file mode 100644 index 6e5ae93..0000000 --- a/apps/public/README.md +++ /dev/null @@ -1,44 +0,0 @@ -# PostERG - Site web public - -Site web affichant publiquement les mémoires et travaux de fin d'études soumis par les étudiant.e.s de l'ERG. - -## Fonctionnalités - -- Affichage paginé des mémoires -- Visualisation détaillée de chaque mémoire -- Pages d'information (à propos, contact, licences) - -## Technologies - -- PHP -- [Symfony YAML](https://symfony.com/doc/current/components/yaml.html) pour la lecture des métadonnées -- CSS (Bulma) - -## Installation - -```shell -composer install -``` - -## Lancement - -```shell -php -S 127.0.0.1:3001 -``` - -Puis ouvrir [127.0.0.1:3001](http://127.0.0.1:3001) dans votre navigateur. - -## Structure - -``` -front-backend/ -├── assets/ # Fichiers CSS et ressources -├── data/ -│ └── yaml/ # Fichiers YAML des mémoires -├── inc/ # Fichiers inclus (header, footer) -├── index.php # Page d'accueil avec liste paginée -├── memoire.php # Page de détail d'un mémoire -├── apropos.php # Page à propos -├── contact.php # Page de contact -└── licences.php # Page des licences -``` diff --git a/apps/public/apropos.php b/apps/public/apropos.php deleted file mode 100644 index dfca020..0000000 --- a/apps/public/apropos.php +++ /dev/null @@ -1,67 +0,0 @@ - - -
-
-

À propos

- -

Travail en Cours.

- -
- -
-

Nous sommes un groupe d'étudiant.e.s en design numérique avec projet concernant les mémoires de l'année - passée qui demande votre aide ! - - Qu'en est-il des mémoires après notre master ? - Quelle est la visibilité de notre travail après notre départ de l'ERG ? Certains mémoires finissent à la - bibliothèque exposés, mais lesquels et pourquoi ? - Actuellement, la bibliothèque (BAUI) sert de lieux d'archives (collection de documents anciens, classés à des - fins historiques ; lieu où les archives sont conservées) des mémoires pour l'erg, st-Luc et UCL mais pourquoi - sont-ils si peu à être exposés ?

- -

Actuellement, les mémoires sélectionnés sont ceux avec une grande disctinction (16/20). Cette note obtenue - dépend de la cotation de lecture de mémoire et sa défense.

- -

Mais pourquoi cette moyenne de 14/20 ?

-

Et où finissent les autres mémoires ?

- -

En l'occurence, la bibliothèque n'est pas un lieu de diffusion et de monstration " juste ", car les mémoires - dépendent de la note attribuée en fin de Master et de la place disponible dans les étagères ; sans parler de - l'état déplorable de certains mémoires due aux conditions de stockages : couverture plastifiée, stickers, etc - - nous travaillons un visuel qui finalement sera " dégradé " lors de son exposition à la bibliothèque, si - exposé. De plus, les mémoires sont visible en bibliothèque de manière tangible (style édition).

- -

Qu'en t-il des formats numérique, audio ou vidéo ?

- -

De fait, notre recherche se pencherait sur un dispositif de partage/diffusion plus adéquat et en phase avec - la multitude de format et forme de monstration plus contemporain. -
- Notre lieu d'archive/exposition prendrait la forme d'un site web, idéalement en ligne (ou en local en fonction - du RE - propriété intellectuelle et droit d'auteur ?). Il contiendrait tout types de mémoire ainsi qu'une - interrogation autour de sa licence et sa notion de partage. -
- En paralèlle, nous donnerons quelques tips et bon plans pour : " comment licencier son mémoire : pour protéger - ses valeurs et notions de partage s'il y'a" . Dans un premier temps, nous allons collecter un maximum de - mémoires et - tenter de recontacter leur auteurice pour échanger avec eux et obtenir des pdf, vidéos, photos. Dans un second - temps, nous trouverons un relais pour les futures étudiant.e.s et organiseront un formulaire qui publiera - automatiquement les mémoires sur notre site. -

- -

Un projet depuis 2022

- -

- Théo Hennequin
- www.theohennequin.com
- Théophile Gervreau-Mercier
- tgm.happyngreen.fr
- Olivia Marly
- oli98marly@gmail.com -

-
-
-
- - \ No newline at end of file diff --git a/apps/public/assets/fonts/Combinedd.otf b/apps/public/assets/fonts/Combinedd.otf deleted file mode 100644 index d3305b82ab71c8c5b1e43b42b41fbbe03c6767b8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 13164 zcmbVz2UrwIw|33+gcfiN*ocaNfC35#2!a7cPy{iHC}tEyT?rBl7#J}u&|t>Ju!;c; zm`H|I6mwRLtZUviAf{bct)8iY`cKaQx_iIpyZ>|XnRm`qSDiX_LWNW9kdTle$QY>+ zgIxUs0<7EVUdAINzlMoh1}JIi%Ie}<3UwCKdSjfyP;f#0vdBk_mG_!y1R zm-7g99*B_4Iy!uA9GWl`s!GfO6fxn^kz+XHcJNyb{F}zbCM443$Q|U{2hXjMM68O? z$)=iS6Pxt>f{d9eC_(pJ@gDv6lmRkYR=Ys==vmoU zJtxC61CB!o1I4 z<*2EoYaOkC@-VQdMAis8^A$VP(-PDaO{t@$NQD;F(G1jCUq{QJj;4;5BWp>EI$8nz z`y+o8i{j8c6pyB(C^QWvf;0}m(-FSikt=dTUdRdPK`0VMK%H=)Vo?lAfSQre3i-#z z&5NHNH7(KFVTz-*yQ`a*ll7p;i0R>jVq+2#BgGofAR0}E#xZ&(&?I{D^q9y9goZ;w z6li58@P(SgBcqaLh653x?;}zF9S=1kL4xk^`8O*=sT)Y?jJm?7USbpw8gwj=>goz5 z{}$}KF^aY_d@KysG*|%2ERYNd@GXu^hFYU`$R2dL>*s)~JKNZFgwf2ekRE?L~B>uIq%DSD6 zt)Zq}`wsRFjvYHWIlFXrb#w3H;n~&8yIc1jJ$>}g{g8h@6g(IW89EGHFJ#2XP=rQ} zhDTvz5E?sfyn)I8ioJRpBJw|1>=S_c_eFt02n~P=gJ4?x{;zI+p-aR$@PGbEkW7Xo z5=ocE-rWP{K!4O1^@Disj{;E;=pq;mgqZ}C57Y86mm7pW&D6B+f5D{n4S%`|u=n8oEZHSHvbPK%v z9z@7*=s9`{o<2bww;c#M0^O3tO0p#^rZARW+ro4#>ocB)UrS29`Yc0hC;5e zS0pG(6_=H+%KL0Edy%D$+B6!_sH71#axxlhw9M$Eaj@|z;|C`F8e24uZG2GWtXiSE z-(+l)15H~s-QJ98=G&~Gxx9I+=A)XInKn0FWBR1Upccnlyx~l_>D)6jXR~Q$MP{#B zHfrhBazx9*mXFO9=Ht!P=3C9*x4mmQ+VVH6rB+2&TI&$&pRLbXziKCI=R(f$WkSC) zNm&^#FJsEg1Uq4m&Q4LLo5KnHaJjCZQl(q3(uMG^DWg>$nA040|KRq>v;wcgPYNkG1-hKYysWnC(+7T)PayswrcY{G3XBALdIay7Z=;${cnhj#Awg zL{ZCdHKl*P2g-VoFU)9z%Xya4Ju7_mAg9RwAXK>Jqo&By8&P_yCBSbN%0++&RquhS z`T%8*w)#@~fLDV!z?DKtl|&8gT?Yd1j_rZB=Z^pr2~>NZ0qjyt)U2we2}#%}xmU>q zk_c{r5;I@03pP12jZaINl^R8CdlKa!E0xZZuaaQz4-B4ewrkstd@KIv)Qt<*v7w5E zsi_P2d2HOagnUbtE*STEjhDTWU^}Q9D)3zXwrvfSM?vMqhRR91EXi{jJls{!so`Pl zHh8GgMUkY9LdmP}~_G_Cl7Ow|kbwO+VI@u^W(#I^@WXNd!a5-@cBc70ciMY$Td z#cfXlx$tQIwKkq23&moIVEO?M!Oa+jeu0^CG}Epums21HpkQ7<@M zJ7Py}X$Rsr*z(~%?460t$#@nwQ;^oUglwYpwnFqSp4_MOsYb0Eq8o&FkWd_E^_j;G zH#g$VtiNI{u}$#}hzI7bxwEy$De4S^{kxjMV?S{j+7Rr-%W($wC(FqRD_+-|EF4Ap z?__hRtchrdT)o_Qph9zkkI@^aCKv?ej|j643uzR&h?;Z9h{0QqaKbF1&0$>|Ax+2^ zpQvk^Q}PZ1AEQ8FFV7#5tET797Je=l=2GM#HVhIQmI^;}LYs1EH&<-uVQBYOqgG%P zBv)ZmUa+Im3woub++{SE1UrssLKHkcv~s@HI>YdEHl#hN&<@h8ssMXs;Z|hAe^ON? zrOz5vH40SqiO06rb8*(Hy=kXK8(anvx0F+9OR+;7`A#r@&u9i1{F2sGL0fYnM%j@f zA5gwtJMVa2*dmzoyl%^Tp4aY|t8`r(?1+25Vem#XTO(BuQ2$48!c;6_gjDTEPWM*H z6YGsJ*)fIIqlv6*9BCQe^?7G(!uq(%UpZp=xVJp}33t1VS6OLhDpgOvF&*#*SH5M0 z&B6&Ta-aJTOVcp-8VBM*Jho(C2f)k%vRQW44^>_ikr%>GT~5+TA?ZG24x2%x!xZXz zm#pS9gc+I32;E_=VCkr3&YNla4v9S#p}uI@bmB-&7Zs=BMP-`T_}LAwJq_d+Lkx-@N;d; z<+W}X;E5RhauxrLl~tJ#a-Pwc8CWf+x^7?q56WCnsJqp1Dg#c9g*LT~vGO8tXf1NU zK3V}{sAn~2XM~$-E^+ORR3V`Mixk<4j@P?dU~Vx&2bicP|LC-e&tiq0FO_)e4bqM@ zwc^PX;y-f4a`q0Db`_)GtbUx;Lac)4x2;^CiAV3Ke6Ty8=l}jI19#8B&0Hv&3DMq0 zjA2iZRn(N=@`?+?Ru8*=bq!q}k`uF=?XDfl;gHMNmUJz#$DMQ%dvct#wM$E1lAe}6 zcXRtEK{*rGCB>&qBQtS;qoy%F`<4~P)LL;1$zZo@Q+Mv$@$%&v?C=))232!^Y@S2e&x?pRVL04qJ#BN2Dis@^}GW-A!#WV3jJl~SX zS9NblSTuHt#*SBUFm48-&O1l~cq`mSv81#!MTbGeafYzgmQd6ctS?AEGMaOSzH3S- zy{u7#BgQK}2JzYw{1-3J+{{zd0+hO$RaNn4smxNe=PN7|j$ChO*}+KlqTbS7(zr8+ z1UGHT2z@&uP+bss{4UCf-7~Na(1244OB?*!LUyWd?3n5XLRCj08+R34p}iTjXi00g zYt;5sGTzX<5+rocB@j=YFUhA&F_cOv6-yu2o;iIG4;Esvn{KWA%t4{E_K;k(Zk=eF z(#ss#BrL=;skE~~J%A^JXl)bd@MkLdQ=#rk={4YTPIsLI=@y;faW|n4PS*92s{$HW z;7BtePmgTt%|4=CQY^vh)0|MOEzni* zGo}a&cwK(&LE!^`2o?a+GIzE_2zkv2Avlm5sYsvnj)@k-}PP4*@2h^Yv0cJCOfWE}7l>B|t3foxz!M{8I z3m$+O!Iu=_E~NQ1mN@7zC&-mNUjp08uA{NlL~Kcxc$3v0WGd;4B}1{CRS{GEjzlo5@@Uul$tUON`B#T4(v;~!5vPk(`r#(T<^=RKKP+Z!FXE!_De>Ug zf=SQ%y~{kz@5?SZc%m@xDCmW7FSW0fDlh&v7WPYoE!P=ruCe4CjD$%ffpauck-NFG zHJu6sFW9knu5#IBS|yZf`*K+&2`A_NJmjLU823Dxv6%#0nf^&u6GgPrvKzeZJ-VmEzJDNx9>XI0cb#Vn3cGZrbhK$<*rpN9=zi0sV-R z-|R_?$dKiMc-ti0=MFZ*HnUj4yQVG7N5fzy4I-^b8wb+&N$iUyr3G7f{@gI!XBcjQ zjqg{K;Ax~S?#c2?PyK0$O_kI69=*~$veJkHMOis&;^IoYI^|I69Ho9ty7PP=j}3h{ zg`SDNIA>+Xj%6z{*tB%V_!z(FUdMd4%)K3eogDBe?10npkUN0^YT>zZ^Tq9V_vYN*jYElfp5ISV zLx}gXsVuo)Ei6>>`!Y|Y9%EIy2(U`Y9BlKhId1f+(<{?IVJ%z9#Uv)gSn>1L&EL3~ zJ)_u|y>1i#Gn=_PVMi>_npPdFUCC)mTx&{{DzX{3e<<9YgWJEA;3KaXd{OJpomFgD zm%WMK!tP4k5d*AZE%~|G^Eafj_iKLVG?C8A6*V66Rl|Qu*)(%&#M*#?>hPtc(fjyI z^NaY^Wt$J=>|VVti=FjQK1DqxA|)}R_YOaB4!(dGKb?Igxh%i*V$r#!FGYoff$&%6 zP>F|rt6=^HAB0GMZ-#d&`HRt~=4|i%*du9J=#}3;V)@2fYxb_%*W(@=<{__kpPU5? z&P|pwjTCBUeK5blxe%%q_X*@T>8ZTO;xDfsm=tppZvhXGmfVfrytBZwl83SXPzll_rhzHT0B}C^`;yaH=V(7GBgj4lolPr;ovLzA?)J$5|k{-A-1uk}}eq zwXpekgl)LlBWKKvP9=98NN-In-gXDu7QS#e)DsrzmPGAL7O^*kMG!{gUV0oMP7es2 zfDh-uPJ%QU;zVhyDD^cGeu;$ORvoa<&$z>;O5Ab7q5ZG-;{KhmJFAMLwU74tz(Nx? z^6VVRRoLdb3tPAqg_*@UMTfDpioZMk#?0eGHpk-Lq{NZ9^L)5>w^)ag(OV-fBo(lo z4lA;hS+wTiUSEz%Do2(p?~-QNF6>_JrOhXIo%juR#)pX)o*i}Khr^z?9Wy5J)76us z!Xjsl7w0A1%sB&g-@Equ%2g{hzreg&2m;t>R9rQlhsP0z!=wp&g;c|iWHN~)-lvH5 zQDTLcJ;7^Pyb8{LZ}U##uE0sR=8g89D`}?WeQ_INXX(y63>!xV5=Pe#&R8DU^Z=|0 zF<_`w2PBVPFv5C`8|RIsE?YhMK)#Q6G|$#ddZ95>@`YH1TUegvb3IoL6b~Lezi9u$ zt*pvlI4TU{oXUb{64O0Fn-=q_XBXYrhnZtrvHS#PiCKOqi<>A&6FifIMvPuSdX1Sz zeE0P@Pi*l!?7pY{9kPzz!q>@5W7V>x<9vdgAvIspMRu zs~{M9cxQ+Nw_A^bKH3nB99+5Avk_0OC%(XjMX@MOcYr@|BfY_nTefrY0v75 zuHe8^hF)%gMw&w}r6A*Pni9?lY~n>m*^$Ad<=?~%%W)X)#@Z-`I%bn5e-ZO73HzpP zjcG@Q6GVD}YrKS90zN8#L2Q2`j_|vhhkdzDu1eOF&XIh4jc>yVY>lRr)BT}L9ung> zbCG{E8H?q!t5ORuVg>)4#g(0PFcr*5ACo_~E$0}|=ji5; zHqj|*i?iqEv59*Z#_6?J7kE)opbIIkoc^fvZ+Pr2%_L|id$BS{Jm3Rc3sVP;c z{P?q)erF`lz~M@8V+KDt%fYTi=%QI5*L5Pzcam@`tWpHvxc5%j0ye}(yw0s=p1fHpMvAA^}mgO*} zudcaNpKubq_?P4kmKMuR-;u{u2HWQlND{(v*0r|i%xpNbTGlOX!js+1|9|=i=4(Ux zhu00^b^b6FjKZT+S)x?(*z0G)SzS@^1-K8HXIZZNv>La$w-K-UJ}tzb7J8No6Ur&- zt2{u_AaOA6)sgw80$zQZ>uhBDS$Eb#R?F?M#>`ZhG9ejSq);nKW1jR9$F81%Te9mi z)@7~(cdBQGSx~imHU24|F>oQJ7IG&p(VEDn65!EWi5v5{*Nc6)WokxhX6koM>*NU5 zr06*;Z{TpjLZ!qk;1@;kH-4ZRtXwdftA|@7O84U8f zj17!QG<`$f2ItgSy&3+|;0#GA-b+;865 zWw+N|+y^sjyNYW#_%+@<3@`V^=HH!I>s%hrt=KAGX!iZlRX0NOQ1tnR({0_T|Hm1Z zu!2?;xuIg6zl%&6QK2ucn?9*OOm{FM;ldX#E-5k2%Jd}cRw?~Up@wZ{xguv>Hbp9p z8g{MuN!w#B4e_C}gAH^LxQcT=E_=lYvv3gSZuClM!gVo%G}{->!${>0Rr*6Hs^R$! zZ8+y?}o_Z&AebuGZotI zrf7+F0!4xHQc91}_g75Ig>!g4?2f2x7^N?A*Hc=1qcu=#+rXWH{d1t6&x11CHI#mO z0#}(eVL?E>yGJQW+Cb^w85>0k4jOt&llT{aygFD*SFt6dLj?HdpqG6TuJQs0&G;nz%KWEpp%&l)T@Ote3|`> zqNdxz;Fcq|f}$Yz2uLq4=|R!(5jJo&QULkIk%0q1XiGwoTSRY=gNvc4%d`~e{Z)Io z;Yb-kk!)`eMa?q0QKX1C3sj4%6tPMUIQO@s$T(mbMXi;=Af@?aNP4V-dM!`=0urSD z1=l5Jle0jqEzhCO1V3ok_XbdfP*L(@GDTKnA&)1y`WmQB6`-xp`{62K&QGB8V^FW@ zqfjWb9SFS6tcB5>_J<7FiDdjuTxDgCe#0>I7H5>l%HKT*`_9Volf{WDw^W%z;GyW9ggKZjHL z>*Q-Nv@?HDdLKQc$jCPhTKuq{B4sJ`_2f`B44??S+Zh^q@A3ceqsMW-)a9!U>2HHHn8T$0O3HADcyZ^Zm#@cj0 zbor?x*peF#KKfxO)N8r33y5k0e$Z;%VDPmEODSrzv=0pA6}W8MrGLVJXMuU!et`(9 z;myV63mm}60@Nfa0IKb2xQdfx0HwqbYR@sWKMLVebLA!2zC}Kz8o!0$H+R~B6m9#6 zl6{4Uiu(Z8UFrb+*!KtBC-wj-WY<{J+=d_5K>?#gF?COY@4*u=oMB7ZJ^Vf%uIi+N z4nU7L-NC~@hQb(~;-Sa&pdZzh(-4slz@@F$g2g_5P6gT3woxUgq(IHc(Ll*M@Z;u+p(swmaB|jK`}QL9C-!! zx?Kg-m%o8ot^IHvtD2@y3JLQo;r=tK@@BqZaUGbZR+d}HR&jBPeWWGs?T$@&wo(bJ zHDL}Vd}T9r8#!+yAsmuNtG1@SO}H**y}FB8FUUnTWW5r_tk<*t?|9q^vR+P`KeIuK z-rptBl6QqYjk~+(vM9qHNKENvkgNh3szC%GK!nv?f;D04_X#Gj!i)O^mYn!bf?SZ` zTAc)QA@BB23Bn;8W{|-7-x5fZ^TVEj1eaLV4A6pRzmS|Kp;F@@uN$C(r9r+LE3Wj( zqx=t3aHDZWq*rD*tFu=m<0d87;Rqm1{^#&Z`iM!tm@#51t`v7A;_$`1K2z5WI|?rM#d1Xy>J(OA!y_ z?`N@j%6fZEd@V67bNm0D%)<|a$6UiAc2j>^!#!aa;|i;$k9IN_ye8m?-)%!zut&G? z@7r%;@KQrpd6$YVM?9QWDRxEr*G{faCF$%9d`wjpp*c;QWirDi}xC$w4!1LEMw!8mFW-jE`wqo%>u>bD^(fVR&$+gr3+AkGzf@$>RJ`aErPkLD!75C5SaPY(~Ys=sz~2NB+xZ)HsON zT!5Qgp1gJK-gC`jyS1eSDE zF6c|ZnJIz%fh9Cds2ZE=>yLQlc5 zT<0iE5K78xOlu>R)dK{$S~x9l;Qx;~K)aKg@h!WEFYQZ0mGFCtH=hTim!xc&EgC(u-ss6|{X*M`M*lsa-sraq z_r8V%+iS=pX9}m&RlQlbxqdl}tvIOmvM59%bVe}ciEq#L- zh3uIqbdEWImNMmN8nXgzfmZ-anH^{ZGZ|imOGUe-cabeK7ae9;^pmVBie>B&&s;~r z()XxF&B=G8__+k3OxWtb*K5=Z~xavPyXp1D3*1*^t zm}@9l{}JV``$)^s80l3sT`zMU8p=peC;f*(PVns{c|);eHT}xiB3JPv%A)@VUBe6G z;(NRM;Z;qgM1_oy6$kSXm5Xh&B9cAznU_Vf=x2f7Z>kzNAqNH+mG(f5GP zbUdIdeGceGe*<)1Dtk^a7v{y#Y9cMgtC|xq!pyUclir7H|Ze z2N*(k0*<8FfT8-QVRSdpW9Tl_7GyI9>_9yL?WsMW1N8#zNQVPD(KUe1)Dh5?4hD47 zm%CFxpu5o7fF86rpeIcN>{|O1pqIYBHw^>28yy1Joq7WHpzQ#A($;{mcLp3nO#p|| z#(=|UBf#PMnj@$;&>?zBM(XKM+6_uZ>E#)%$1uGNV*ss?5lsfPqZ1z>|zCDxdG@ycffnEb~Ijm%bG?2+Jk-JJ$d>QU`M(S(1}(9I@4i*u3#x^=zS}o zJ1qq4LMs71Xbzw!c!f3ez8TPq{srhwuL6o51@E)d-vN7o9j&4F4S+r{IveOc46p+Y z2DAr%uz}t~0XxEYZJ_r&KxgU%=t?62-9TC!=sggy3vjc6-p2uY!vD9hf!@Obz34SS zZ+QFI271o~><&`dK<^^~d(!!UJ~U2z-xo$|i`r8jumkvmEyxfL=s<@8I?|{Bu}Of#^xTHSsBIxS76OLoWgAI<1UeL=&lY^_ x0N`kP5ipGQ034&2d@P*<^f>*v#zQRoLZmMQ6z$Rn=mqc=vl#n+P*P>U{|DZeXJh~X diff --git a/apps/public/assets/normalize.css b/apps/public/assets/normalize.css deleted file mode 100644 index 192eb9c..0000000 --- a/apps/public/assets/normalize.css +++ /dev/null @@ -1,349 +0,0 @@ -/*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */ - -/* Document - ========================================================================== */ - -/** - * 1. Correct the line height in all browsers. - * 2. Prevent adjustments of font size after orientation changes in iOS. - */ - -html { - line-height: 1.15; /* 1 */ - -webkit-text-size-adjust: 100%; /* 2 */ -} - -/* Sections - ========================================================================== */ - -/** - * Remove the margin in all browsers. - */ - -body { - margin: 0; -} - -/** - * Render the `main` element consistently in IE. - */ - -main { - display: block; -} - -/** - * Correct the font size and margin on `h1` elements within `section` and - * `article` contexts in Chrome, Firefox, and Safari. - */ - -h1 { - font-size: 2em; - margin: 0.67em 0; -} - -/* Grouping content - ========================================================================== */ - -/** - * 1. Add the correct box sizing in Firefox. - * 2. Show the overflow in Edge and IE. - */ - -hr { - box-sizing: content-box; /* 1 */ - height: 0; /* 1 */ - overflow: visible; /* 2 */ -} - -/** - * 1. Correct the inheritance and scaling of font size in all browsers. - * 2. Correct the odd `em` font sizing in all browsers. - */ - -pre { - font-family: monospace, monospace; /* 1 */ - font-size: 1em; /* 2 */ -} - -/* Text-level semantics - ========================================================================== */ - -/** - * Remove the gray background on active links in IE 10. - */ - -a { - background-color: transparent; -} - -/** - * 1. Remove the bottom border in Chrome 57- - * 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari. - */ - -abbr[title] { - border-bottom: none; /* 1 */ - text-decoration: underline; /* 2 */ - text-decoration: underline dotted; /* 2 */ -} - -/** - * Add the correct font weight in Chrome, Edge, and Safari. - */ - -b, -strong { - font-weight: bolder; -} - -/** - * 1. Correct the inheritance and scaling of font size in all browsers. - * 2. Correct the odd `em` font sizing in all browsers. - */ - -code, -kbd, -samp { - font-family: monospace, monospace; /* 1 */ - font-size: 1em; /* 2 */ -} - -/** - * Add the correct font size in all browsers. - */ - -small { - font-size: 80%; -} - -/** - * Prevent `sub` and `sup` elements from affecting the line height in - * all browsers. - */ - -sub, -sup { - font-size: 75%; - line-height: 0; - position: relative; - vertical-align: baseline; -} - -sub { - bottom: -0.25em; -} - -sup { - top: -0.5em; -} - -/* Embedded content - ========================================================================== */ - -/** - * Remove the border on images inside links in IE 10. - */ - -img { - border-style: none; -} - -/* Forms - ========================================================================== */ - -/** - * 1. Change the font styles in all browsers. - * 2. Remove the margin in Firefox and Safari. - */ - -button, -input, -optgroup, -select, -textarea { - font-family: inherit; /* 1 */ - font-size: 100%; /* 1 */ - line-height: 1.15; /* 1 */ - margin: 0; /* 2 */ -} - -/** - * Show the overflow in IE. - * 1. Show the overflow in Edge. - */ - -button, -input { /* 1 */ - overflow: visible; -} - -/** - * Remove the inheritance of text transform in Edge, Firefox, and IE. - * 1. Remove the inheritance of text transform in Firefox. - */ - -button, -select { /* 1 */ - text-transform: none; -} - -/** - * Correct the inability to style clickable types in iOS and Safari. - */ - -button, -[type="button"], -[type="reset"], -[type="submit"] { - -webkit-appearance: button; -} - -/** - * Remove the inner border and padding in Firefox. - */ - -button::-moz-focus-inner, -[type="button"]::-moz-focus-inner, -[type="reset"]::-moz-focus-inner, -[type="submit"]::-moz-focus-inner { - border-style: none; - padding: 0; -} - -/** - * Restore the focus styles unset by the previous rule. - */ - -button:-moz-focusring, -[type="button"]:-moz-focusring, -[type="reset"]:-moz-focusring, -[type="submit"]:-moz-focusring { - outline: 1px dotted ButtonText; -} - -/** - * Correct the padding in Firefox. - */ - -fieldset { - padding: 0.35em 0.75em 0.625em; -} - -/** - * 1. Correct the text wrapping in Edge and IE. - * 2. Correct the color inheritance from `fieldset` elements in IE. - * 3. Remove the padding so developers are not caught out when they zero out - * `fieldset` elements in all browsers. - */ - -legend { - box-sizing: border-box; /* 1 */ - color: inherit; /* 2 */ - display: table; /* 1 */ - max-width: 100%; /* 1 */ - padding: 0; /* 3 */ - white-space: normal; /* 1 */ -} - -/** - * Add the correct vertical alignment in Chrome, Firefox, and Opera. - */ - -progress { - vertical-align: baseline; -} - -/** - * Remove the default vertical scrollbar in IE 10+. - */ - -textarea { - overflow: auto; -} - -/** - * 1. Add the correct box sizing in IE 10. - * 2. Remove the padding in IE 10. - */ - -[type="checkbox"], -[type="radio"] { - box-sizing: border-box; /* 1 */ - padding: 0; /* 2 */ -} - -/** - * Correct the cursor style of increment and decrement buttons in Chrome. - */ - -[type="number"]::-webkit-inner-spin-button, -[type="number"]::-webkit-outer-spin-button { - height: auto; -} - -/** - * 1. Correct the odd appearance in Chrome and Safari. - * 2. Correct the outline style in Safari. - */ - -[type="search"] { - -webkit-appearance: textfield; /* 1 */ - outline-offset: -2px; /* 2 */ -} - -/** - * Remove the inner padding in Chrome and Safari on macOS. - */ - -[type="search"]::-webkit-search-decoration { - -webkit-appearance: none; -} - -/** - * 1. Correct the inability to style clickable types in iOS and Safari. - * 2. Change font properties to `inherit` in Safari. - */ - -::-webkit-file-upload-button { - -webkit-appearance: button; /* 1 */ - font: inherit; /* 2 */ -} - -/* Interactive - ========================================================================== */ - -/* - * Add the correct display in Edge, IE 10+, and Firefox. - */ - -details { - display: block; -} - -/* - * Add the correct display in all browsers. - */ - -summary { - display: list-item; -} - -/* Misc - ========================================================================== */ - -/** - * Add the correct display in IE 10+. - */ - -template { - display: none; -} - -/** - * Add the correct display in IE 10. - */ - -[hidden] { - display: none; -} diff --git a/apps/public/assets/posterg.css b/apps/public/assets/posterg.css deleted file mode 100644 index 48136ff..0000000 --- a/apps/public/assets/posterg.css +++ /dev/null @@ -1,559 +0,0 @@ -/* ============================================ - POST-ERG - Minimalistic CSS - ============================================ */ - -/* Custom Font */ -@font-face { - font-family: 'Combined'; - src: url("fonts/Combinedd.otf"); -} - -/* ============================================ - VARIABLES & BASE - ============================================ */ - -:root { - --color-primary: #c104fc; - --color-secondary: #4da870; - --color-text: #333; - --color-text-light: #666; - --color-border: #ddd; - --color-bg: #fff; - --color-bg-light: #f9f9f9; - --spacing-sm: 0.75rem; - --spacing: 1.5rem; - --spacing-lg: 3rem; - --spacing-xl: 4rem; - --border-radius: 8px; - --max-width: 1400px; -} - -* { - box-sizing: border-box; -} - -body { - font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif; - line-height: 1.7; - color: var(--color-text); - background: var(--color-bg-light); - margin: 0; - padding: 0; - font-size: 16px; -} - -/* ============================================ - NAVBAR - ============================================ */ - -.navbar { - font-family: 'Combined', sans-serif; - background: linear-gradient(280deg, var(--color-secondary) 0%, var(--color-primary) 85%); - padding: 2rem 3rem; - color: white; - position: sticky; - top: 0; - z-index: 100; - box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); -} - -.navbar-brand { - margin-bottom: 1rem; -} - -.navbar-brand h1 { - margin: 0; - font-size: 2.5rem; - font-weight: normal; - letter-spacing: 0.5px; -} - -.navbar-menu { - display: flex; - gap: 2rem; - align-items: center; - flex-wrap: wrap; -} - -.navbar-item { - color: white; - text-decoration: none; - transition: color 0.2s; - font-size: 1.1rem; - padding: 0.5rem 0; -} - -.navbar-item:hover { - color: var(--color-secondary); -} - -/* ============================================ - LAYOUT - ============================================ */ - -.section { - padding: var(--spacing-xl) var(--spacing-lg); - background: var(--color-bg-light); -} - -.container { - max-width: var(--max-width); - margin: 0 auto; - padding: 0 var(--spacing-lg); -} - -/* Grid System */ -.columns { - display: grid; - gap: 2rem; - grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); -} - -.columns.is-multiline { - grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); -} - -.column { - min-width: 0; /* Fix overflow issues */ -} - -.column.is-one-third { - grid-column: span 1; -} - -.column.is-one-fifth { - /* Will use auto-fill for responsive grid */ -} - -/* Two-column layout for detail pages */ -.columns.is-variable { - grid-template-columns: 1fr 2fr; - gap: var(--spacing-lg); -} - -@media (max-width: 768px) { - .columns.is-variable { - grid-template-columns: 1fr; - } -} - -/* ============================================ - CARDS - ============================================ */ - -.card { - background: var(--color-bg); - border: 2px solid var(--color-border); - border-radius: var(--border-radius); - overflow: hidden; - transition: transform 0.2s, box-shadow 0.2s, border-color 0.2s; - height: 100%; - display: flex; - flex-direction: column; - box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05); -} - -.card-link { - text-decoration: none; - color: inherit; - display: block; - height: 100%; -} - -.card-link:hover .card { - transform: translateY(-6px); - box-shadow: 0 8px 24px rgba(193, 4, 252, 0.2); - border-color: var(--color-primary); -} - -.card-image { - width: 100%; - overflow: hidden; - background: var(--color-bg-light); -} - -.card-image img { - width: 100%; - height: 240px; - object-fit: cover; - display: block; -} - -.card-content { - padding: 1.75rem; - flex: 1; - display: flex; - flex-direction: column; - gap: 0.75rem; -} - -/* ============================================ - TYPOGRAPHY - ============================================ */ - -h1, h2, h3, h4, h5, h6 { - margin: 0 0 0.75rem 0; - line-height: 1.3; - font-weight: 600; -} - -.title { - font-size: 1.5rem; - color: var(--color-text); - margin-bottom: 0.75rem; - line-height: 1.4; -} - -.title.is-1 { - font-size: 2.5rem; -} - -.title.is-4 { - font-size: 1.35rem; - line-height: 1.4; -} - -.title.is-6 { - font-size: 1rem; -} - -.subtitle { - font-size: 1.1rem; - color: var(--color-text-light); - margin-bottom: 0.75rem; - font-weight: 500; -} - -.content { - font-size: 1rem; - line-height: 1.7; - color: var(--color-text-light); -} - -.block { - margin-top: auto; /* Push to bottom of card */ - padding-top: 0.5rem; -} - -/* ============================================ - TAG - ============================================ */ - -.tag { - display: inline-block; - padding: 0.5rem 1rem; - background: var(--color-bg-light); - border: 2px solid var(--color-border); - border-radius: var(--border-radius); - font-size: 0.95rem; - margin-bottom: 0.75rem; - font-weight: 500; -} - -.tag.is-link { - background: rgba(193, 4, 252, 0.08); - border-color: var(--color-primary); - color: var(--color-primary); -} - -.tag.is-light { - background: var(--color-bg-light); -} - -/* ============================================ - FORMS - ============================================ */ - -.field { - margin-bottom: var(--spacing); -} - -.label { - display: block; - margin-bottom: 0.25rem; - font-weight: 500; - font-size: 0.9rem; -} - -.input, -.textarea, -select { - width: 100%; - padding: 0.75rem 1rem; - border: 2px solid var(--color-border); - border-radius: var(--border-radius); - font-size: 1rem; - font-family: inherit; - transition: border-color 0.2s, box-shadow 0.2s; - background: var(--color-bg); -} - -.input:focus, -.textarea:focus, -select:focus { - outline: none; - border-color: var(--color-primary); - box-shadow: 0 0 0 3px rgba(193, 4, 252, 0.1); -} - -.textarea { - min-height: 150px; - resize: vertical; - line-height: 1.6; -} - -/* ============================================ - BUTTONS - ============================================ */ - -.button { - display: inline-block; - padding: 0.85rem 2rem; - background: var(--color-primary); - color: white; - border: none; - border-radius: var(--border-radius); - font-size: 1.05rem; - text-decoration: none; - cursor: pointer; - transition: background 0.2s, transform 0.1s, box-shadow 0.2s; - font-family: inherit; - font-weight: 500; - box-shadow: 0 2px 4px rgba(193, 4, 252, 0.2); -} - -.button:hover { - background: #a003d1; - transform: translateY(-2px); - box-shadow: 0 4px 8px rgba(193, 4, 252, 0.3); -} - -.button:active { - transform: translateY(0); - box-shadow: 0 2px 4px rgba(193, 4, 252, 0.2); -} - -.button.is-link { - background: var(--color-primary); -} - -.button.is-light { - background: var(--color-bg); - color: var(--color-text); - border: 2px solid var(--color-border); - box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05); -} - -.button.is-light:hover { - background: var(--color-bg-light); - border-color: var(--color-primary); - box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1); -} - -/* ============================================ - NOTIFICATIONS - ============================================ */ - -.notification { - padding: 1.5rem 2rem; - border-radius: var(--border-radius); - margin-bottom: var(--spacing); - border: 2px solid; - font-size: 1.05rem; - line-height: 1.6; -} - -.notification.is-danger { - background: #fee; - border-color: #fcc; - color: #c00; -} - -.notification.is-success { - background: #efe; - border-color: #cfc; - color: #060; -} - -.notification.is-info { - background: #eef; - border-color: #ccf; - color: #006; -} - -/* ============================================ - BOX - ============================================ */ - -.box { - background: var(--color-bg); - border: 2px solid var(--color-border); - border-radius: var(--border-radius); - padding: 2rem; - margin-bottom: var(--spacing); - box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05); -} - -/* ============================================ - MEDIA - ============================================ */ - -img, -video, -iframe, -embed { - max-width: 100%; - height: auto; - border-radius: var(--border-radius); -} - -embed { - display: block; - width: 100%; - max-width: 800px; - height: 700px; - margin: 0 auto; - border: 1px solid var(--color-border); -} - -/* ============================================ - PAGINATION - ============================================ */ - -.pagination { - display: flex; - gap: 0.5rem; - justify-content: center; - margin-top: var(--spacing-lg); - flex-wrap: wrap; -} - -.pagination a, -.pagination span { - padding: 0.5rem 0.75rem; - border: 1px solid var(--color-border); - border-radius: var(--border-radius); - text-decoration: none; - color: var(--color-text); - transition: all 0.2s; -} - -.pagination a:hover { - background: var(--color-primary); - color: white; - border-color: var(--color-primary); -} - -.pagination .current { - background: var(--color-primary); - color: white; - border-color: var(--color-primary); -} - -/* ============================================ - FOOTER - ============================================ */ - -.footer { - background: var(--color-bg); - border-top: 2px solid var(--color-border); - padding: 3rem 2rem; - text-align: center; - margin-top: var(--spacing-xl); - font-size: 1rem; - color: var(--color-text-light); -} - -/* ============================================ - UTILITIES - ============================================ */ - -.has-text-centered { - text-align: center; -} - -.is-flex { - display: flex; -} - -.is-justify-content-space-between { - justify-content: space-between; -} - -.is-align-items-center { - align-items: center; -} - -.mt-4 { - margin-top: var(--spacing-lg); -} - -.mb-4 { - margin-bottom: var(--spacing-lg); -} - -/* ============================================ - RESPONSIVE - ============================================ */ - -@media (max-width: 768px) { - :root { - --spacing-lg: 1.5rem; - --spacing-xl: 2rem; - } - - .navbar { - padding: 1.5rem 1rem; - } - - .navbar-brand h1 { - font-size: 1.8rem; - } - - .navbar-menu { - gap: 1rem; - } - - .navbar-item { - font-size: 1rem; - } - - .columns { - grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); - gap: 1.5rem; - } - - .section { - padding: 2rem 1rem; - } - - .card-content { - padding: 1.5rem; - } - - embed { - height: 400px; - } - - .title.is-4 { - font-size: 1.2rem; - } -} - -@media (max-width: 480px) { - .columns { - grid-template-columns: 1fr; - } - - .navbar { - padding: 1.25rem 1rem; - } - - .navbar-brand h1 { - font-size: 1.5rem; - } - - .section { - padding: 1.5rem 1rem; - } -} diff --git a/apps/public/cache/rate_limit/ad921d60486366258809553a3db49a4a.json b/apps/public/cache/rate_limit/ad921d60486366258809553a3db49a4a.json deleted file mode 100644 index 049349c..0000000 --- a/apps/public/cache/rate_limit/ad921d60486366258809553a3db49a4a.json +++ /dev/null @@ -1 +0,0 @@ -[1769594004,1769594004,1769594004,1769594004,1769594004] \ No newline at end of file diff --git a/apps/public/cache/rate_limit/f528764d624db129b32c21fbca0cb8d6.json b/apps/public/cache/rate_limit/f528764d624db129b32c21fbca0cb8d6.json deleted file mode 100644 index fa08ea8..0000000 --- a/apps/public/cache/rate_limit/f528764d624db129b32c21fbca0cb8d6.json +++ /dev/null @@ -1 +0,0 @@ -[1769593359,1769593362,1769593367,1769593372,1769593375,1769593380] \ No newline at end of file diff --git a/apps/public/composer.json b/apps/public/composer.json deleted file mode 100644 index f6204fb..0000000 --- a/apps/public/composer.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "require": { - "symfony/polyfill-iconv": "^1.27", - "symfony/yaml": "^6.2", - "symfony/intl": "^6.2", - "behat/transliterator": "^1.5" - } -} diff --git a/apps/public/composer.lock b/apps/public/composer.lock deleted file mode 100644 index ccdc25f..0000000 --- a/apps/public/composer.lock +++ /dev/null @@ -1,388 +0,0 @@ -{ - "_readme": [ - "This file locks the dependencies of your project to a known state", - "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", - "This file is @generated automatically" - ], - "content-hash": "e941923040be085b6ce94a2d66270369", - "packages": [ - { - "name": "behat/transliterator", - "version": "v1.5.0", - "source": { - "type": "git", - "url": "https://github.com/Behat/Transliterator.git", - "reference": "baac5873bac3749887d28ab68e2f74db3a4408af" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/Behat/Transliterator/zipball/baac5873bac3749887d28ab68e2f74db3a4408af", - "reference": "baac5873bac3749887d28ab68e2f74db3a4408af", - "shasum": "" - }, - "require": { - "php": ">=7.2" - }, - "require-dev": { - "chuyskywalker/rolling-curl": "^3.1", - "php-yaoi/php-yaoi": "^1.0", - "phpunit/phpunit": "^8.5.25 || ^9.5.19" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.x-dev" - } - }, - "autoload": { - "psr-4": { - "Behat\\Transliterator\\": "src/Behat/Transliterator" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "Artistic-1.0" - ], - "description": "String transliterator", - "keywords": [ - "i18n", - "slug", - "transliterator" - ], - "support": { - "issues": "https://github.com/Behat/Transliterator/issues", - "source": "https://github.com/Behat/Transliterator/tree/v1.5.0" - }, - "time": "2022-03-30T09:27:43+00:00" - }, - { - "name": "symfony/intl", - "version": "v6.2.10", - "source": { - "type": "git", - "url": "https://github.com/symfony/intl.git", - "reference": "860c99e53149d22df1900d3aefdaeb17adb7669d" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/intl/zipball/860c99e53149d22df1900d3aefdaeb17adb7669d", - "reference": "860c99e53149d22df1900d3aefdaeb17adb7669d", - "shasum": "" - }, - "require": { - "php": ">=8.1" - }, - "require-dev": { - "symfony/filesystem": "^5.4|^6.0", - "symfony/finder": "^5.4|^6.0" - }, - "type": "library", - "autoload": { - "psr-4": { - "Symfony\\Component\\Intl\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Bernhard Schussek", - "email": "bschussek@gmail.com" - }, - { - "name": "Eriksen Costa", - "email": "eriksen.costa@infranology.com.br" - }, - { - "name": "Igor Wiedler", - "email": "igor@wiedler.ch" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Provides access to the localization data of the ICU library", - "homepage": "https://symfony.com", - "keywords": [ - "i18n", - "icu", - "internationalization", - "intl", - "l10n", - "localization" - ], - "support": { - "source": "https://github.com/symfony/intl/tree/v6.2.10" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2023-04-14T16:23:31+00:00" - }, - { - "name": "symfony/polyfill-ctype", - "version": "v1.27.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-ctype.git", - "reference": "5bbc823adecdae860bb64756d639ecfec17b050a" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/5bbc823adecdae860bb64756d639ecfec17b050a", - "reference": "5bbc823adecdae860bb64756d639ecfec17b050a", - "shasum": "" - }, - "require": { - "php": ">=7.1" - }, - "provide": { - "ext-ctype": "*" - }, - "suggest": { - "ext-ctype": "For best performance" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "1.27-dev" - }, - "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" - } - }, - "autoload": { - "files": [ - "bootstrap.php" - ], - "psr-4": { - "Symfony\\Polyfill\\Ctype\\": "" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Gert de Pagter", - "email": "BackEndTea@gmail.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony polyfill for ctype functions", - "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "ctype", - "polyfill", - "portable" - ], - "support": { - "source": "https://github.com/symfony/polyfill-ctype/tree/v1.27.0" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2022-11-03T14:55:06+00:00" - }, - { - "name": "symfony/polyfill-iconv", - "version": "v1.27.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-iconv.git", - "reference": "927013f3aac555983a5059aada98e1907d842695" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-iconv/zipball/927013f3aac555983a5059aada98e1907d842695", - "reference": "927013f3aac555983a5059aada98e1907d842695", - "shasum": "" - }, - "require": { - "php": ">=7.1" - }, - "provide": { - "ext-iconv": "*" - }, - "suggest": { - "ext-iconv": "For best performance" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "1.27-dev" - }, - "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" - } - }, - "autoload": { - "files": [ - "bootstrap.php" - ], - "psr-4": { - "Symfony\\Polyfill\\Iconv\\": "" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony polyfill for the Iconv extension", - "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "iconv", - "polyfill", - "portable", - "shim" - ], - "support": { - "source": "https://github.com/symfony/polyfill-iconv/tree/v1.27.0" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2022-11-03T14:55:06+00:00" - }, - { - "name": "symfony/yaml", - "version": "v6.2.10", - "source": { - "type": "git", - "url": "https://github.com/symfony/yaml.git", - "reference": "61916f3861b1e9705b18cfde723921a71dd1559d" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/yaml/zipball/61916f3861b1e9705b18cfde723921a71dd1559d", - "reference": "61916f3861b1e9705b18cfde723921a71dd1559d", - "shasum": "" - }, - "require": { - "php": ">=8.1", - "symfony/polyfill-ctype": "^1.8" - }, - "conflict": { - "symfony/console": "<5.4" - }, - "require-dev": { - "symfony/console": "^5.4|^6.0" - }, - "suggest": { - "symfony/console": "For validating YAML files using the lint command" - }, - "bin": [ - "Resources/bin/yaml-lint" - ], - "type": "library", - "autoload": { - "psr-4": { - "Symfony\\Component\\Yaml\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Loads and dumps YAML files", - "homepage": "https://symfony.com", - "support": { - "source": "https://github.com/symfony/yaml/tree/v6.2.10" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2023-04-28T13:25:36+00:00" - } - ], - "packages-dev": [], - "aliases": [], - "minimum-stability": "stable", - "stability-flags": [], - "prefer-stable": false, - "prefer-lowest": false, - "platform": [], - "platform-dev": [], - "plugin-api-version": "2.3.0" -} diff --git a/apps/public/contact.php b/apps/public/contact.php deleted file mode 100644 index 7f29f9c..0000000 --- a/apps/public/contact.php +++ /dev/null @@ -1,19 +0,0 @@ - -
-
-

Contact

-
- -

Laurent Leprince,
- Bibliothèque d'architecture, d'ingénierie architecturale, d'urbanisme (BAIU) :
- laurent.leprince@uclouvain.be

- -

Xavier Gorgol,
- Responsable des mémoires de l'ERG :
- xavier.gorgol@erg.be

- -

Brigitte Ledune,
- Cours de suivi de mémoire :
- brigitte.ledune@erg.be

- - \ No newline at end of file diff --git a/apps/public/error.log b/apps/public/error.log deleted file mode 100644 index 522fd69..0000000 --- a/apps/public/error.log +++ /dev/null @@ -1,113 +0,0 @@ -[02-May-2023 18:18:59 UTC] PHP Fatal error: Uncaught Error: Call to undefined function yaml_parse_file() in /home/lockpick/Projects/posterg-website/index.php:17 -Stack trace: -#0 {main} - thrown in /home/lockpick/Projects/posterg-website/index.php on line 17 -[02-May-2023 18:19:05 UTC] PHP Fatal error: Uncaught Error: Call to undefined function yaml_parse_file() in /home/lockpick/Projects/posterg-website/index.php:17 -Stack trace: -#0 {main} - thrown in /home/lockpick/Projects/posterg-website/index.php on line 17 -[02-May-2023 18:19:09 UTC] PHP Fatal error: Uncaught Error: Call to undefined function yaml_parse_file() in /home/lockpick/Projects/posterg-website/index.php:17 -Stack trace: -#0 {main} - thrown in /home/lockpick/Projects/posterg-website/index.php on line 17 -[02-May-2023 18:52:59 UTC] PHP Warning: include(header.php): Failed to open stream: No such file or directory in /home/lockpick/Projects/posterg-website/index.php on line 20 -[02-May-2023 18:52:59 UTC] PHP Warning: include(): Failed opening 'header.php' for inclusion (include_path='.:') in /home/lockpick/Projects/posterg-website/index.php on line 20 -[02-May-2023 18:52:59 UTC] PHP Warning: include(footer.php): Failed to open stream: No such file or directory in /home/lockpick/Projects/posterg-website/index.php on line 38 -[02-May-2023 18:52:59 UTC] PHP Warning: include(): Failed opening 'footer.php' for inclusion (include_path='.:') in /home/lockpick/Projects/posterg-website/index.php on line 38 -[02-May-2023 18:53:19 UTC] PHP Warning: include(header.php): Failed to open stream: No such file or directory in /home/lockpick/Projects/posterg-website/index.php on line 20 -[02-May-2023 18:53:19 UTC] PHP Warning: include(): Failed opening 'header.php' for inclusion (include_path='.:') in /home/lockpick/Projects/posterg-website/index.php on line 20 -[02-May-2023 18:53:19 UTC] PHP Warning: include(footer.php): Failed to open stream: No such file or directory in /home/lockpick/Projects/posterg-website/index.php on line 38 -[02-May-2023 18:53:19 UTC] PHP Warning: include(): Failed opening 'footer.php' for inclusion (include_path='.:') in /home/lockpick/Projects/posterg-website/index.php on line 38 -[02-May-2023 18:53:20 UTC] PHP Warning: include(header.php): Failed to open stream: No such file or directory in /home/lockpick/Projects/posterg-website/index.php on line 20 -[02-May-2023 18:53:20 UTC] PHP Warning: include(): Failed opening 'header.php' for inclusion (include_path='.:') in /home/lockpick/Projects/posterg-website/index.php on line 20 -[02-May-2023 18:53:20 UTC] PHP Warning: include(footer.php): Failed to open stream: No such file or directory in /home/lockpick/Projects/posterg-website/index.php on line 38 -[02-May-2023 18:53:20 UTC] PHP Warning: include(): Failed opening 'footer.php' for inclusion (include_path='.:') in /home/lockpick/Projects/posterg-website/index.php on line 38 -[02-May-2023 18:53:38 UTC] PHP Warning: include(header.php): Failed to open stream: No such file or directory in /home/lockpick/Projects/posterg-website/index.php on line 20 -[02-May-2023 18:53:38 UTC] PHP Warning: include(): Failed opening 'header.php' for inclusion (include_path='.:') in /home/lockpick/Projects/posterg-website/index.php on line 20 -[02-May-2023 18:53:38 UTC] PHP Warning: include(footer.php): Failed to open stream: No such file or directory in /home/lockpick/Projects/posterg-website/index.php on line 38 -[02-May-2023 18:53:38 UTC] PHP Warning: include(): Failed opening 'footer.php' for inclusion (include_path='.:') in /home/lockpick/Projects/posterg-website/index.php on line 38 -[02-May-2023 18:53:39 UTC] PHP Warning: include(header.php): Failed to open stream: No such file or directory in /home/lockpick/Projects/posterg-website/index.php on line 20 -[02-May-2023 18:53:39 UTC] PHP Warning: include(): Failed opening 'header.php' for inclusion (include_path='.:') in /home/lockpick/Projects/posterg-website/index.php on line 20 -[02-May-2023 18:53:39 UTC] PHP Warning: include(footer.php): Failed to open stream: No such file or directory in /home/lockpick/Projects/posterg-website/index.php on line 38 -[02-May-2023 18:53:39 UTC] PHP Warning: include(): Failed opening 'footer.php' for inclusion (include_path='.:') in /home/lockpick/Projects/posterg-website/index.php on line 38 -[04-May-2023 19:41:18 UTC] PHP Warning: Undefined array key "title" in /home/lockpick/Projects/posterg-website/memoire.php on line 21 -[04-May-2023 19:41:18 UTC] PHP Warning: Undefined array key "author" in /home/lockpick/Projects/posterg-website/memoire.php on line 24 -[04-May-2023 19:41:32 UTC] PHP Warning: Undefined array key "title" in /home/lockpick/Projects/posterg-website/memoire.php on line 21 -[04-May-2023 19:41:32 UTC] PHP Warning: Undefined array key "author" in /home/lockpick/Projects/posterg-website/memoire.php on line 24 -[04-May-2023 20:09:45 UTC] PHP Warning: foreach() argument must be of type array|object, string given in /home/lockpick/Projects/posterg-website/memoire.php on line 42 -[04-May-2023 20:12:06 UTC] PHP Warning: foreach() argument must be of type array|object, string given in /home/lockpick/Projects/posterg-website/memoire.php on line 46 -[04-May-2023 20:12:49 UTC] PHP Warning: foreach() argument must be of type array|object, string given in /home/lockpick/Projects/posterg-website/memoire.php on line 46 -[04-May-2023 20:13:06 UTC] PHP Warning: foreach() argument must be of type array|object, string given in /home/lockpick/Projects/posterg-website/memoire.php on line 46 -[04-May-2023 20:13:20 UTC] PHP Warning: foreach() argument must be of type array|object, string given in /home/lockpick/Projects/posterg-website/memoire.php on line 46 -[04-May-2023 20:15:28 UTC] PHP Warning: foreach() argument must be of type array|object, string given in /home/lockpick/Projects/posterg-website/memoire.php on line 50 -[04-May-2023 20:16:10 UTC] PHP Warning: foreach() argument must be of type array|object, string given in /home/lockpick/Projects/posterg-website/memoire.php on line 70 -[04-May-2023 20:16:30 UTC] PHP Warning: foreach() argument must be of type array|object, string given in /home/lockpick/Projects/posterg-website/memoire.php on line 70 -[04-May-2023 20:16:47 UTC] PHP Warning: foreach() argument must be of type array|object, string given in /home/lockpick/Projects/posterg-website/memoire.php on line 70 -[04-May-2023 20:17:04 UTC] PHP Warning: foreach() argument must be of type array|object, string given in /home/lockpick/Projects/posterg-website/memoire.php on line 70 -[04-May-2023 20:17:41 UTC] PHP Warning: foreach() argument must be of type array|object, string given in /home/lockpick/Projects/posterg-website/memoire.php on line 70 -[04-May-2023 20:18:58 UTC] PHP Warning: foreach() argument must be of type array|object, string given in /home/lockpick/Projects/posterg-website/memoire.php on line 71 -[04-May-2023 20:48:21 UTC] PHP Warning: foreach() argument must be of type array|object, string given in /home/lockpick/Projects/posterg-website/memoire.php on line 74 -[04-May-2023 20:54:30 UTC] PHP Warning: Undefined variable $key in /home/lockpick/Projects/posterg-website/memoire.php on line 68 -[04-May-2023 20:55:03 UTC] PHP Warning: Undefined variable $key in /home/lockpick/Projects/posterg-website/memoire.php on line 68 -[04-May-2023 20:55:11 UTC] PHP Warning: Undefined variable $key in /home/lockpick/Projects/posterg-website/memoire.php on line 68 -[04-May-2023 20:55:14 UTC] PHP Warning: Undefined variable $key in /home/lockpick/Projects/posterg-website/memoire.php on line 68 -[04-May-2023 20:56:41 UTC] PHP Warning: Undefined variable $key in /home/lockpick/Projects/posterg-website/memoire.php on line 68 -[04-May-2023 21:02:29 UTC] PHP Warning: Undefined variable $key in /home/lockpick/Projects/posterg-website/memoire.php on line 68 -[04-May-2023 21:02:54 UTC] PHP Warning: Undefined variable $key in /home/lockpick/Projects/posterg-website/memoire.php on line 68 -[04-May-2023 21:25:39 UTC] PHP Warning: Undefined variable $key in /home/lockpick/Projects/posterg-website/memoire.php on line 68 -[05-May-2023 08:44:42 UTC] PHP Warning: Undefined variable $key in /home/lockpick/Projects/posterg-website/memoire.php on line 68 -[05-May-2023 08:46:07 UTC] PHP Warning: Undefined variable $key in /home/lockpick/Projects/posterg-website/memoire.php on line 68 -[05-May-2023 08:47:02 UTC] PHP Warning: Undefined variable $key in /home/lockpick/Projects/posterg-website/memoire.php on line 68 -[05-May-2023 08:47:15 UTC] PHP Warning: Undefined variable $key in /home/lockpick/Projects/posterg-website/memoire.php on line 68 -[05-May-2023 08:47:19 UTC] PHP Warning: Undefined variable $key in /home/lockpick/Projects/posterg-website/memoire.php on line 68 -[05-May-2023 08:47:22 UTC] PHP Warning: Undefined variable $key in /home/lockpick/Projects/posterg-website/memoire.php on line 68 -[05-May-2023 08:47:25 UTC] PHP Warning: Undefined variable $key in /home/lockpick/Projects/posterg-website/memoire.php on line 68 -[05-May-2023 08:47:31 UTC] PHP Warning: Undefined variable $key in /home/lockpick/Projects/posterg-website/memoire.php on line 68 -[05-May-2023 08:47:34 UTC] PHP Warning: Undefined variable $key in /home/lockpick/Projects/posterg-website/memoire.php on line 68 -[05-May-2023 08:48:11 UTC] PHP Warning: Undefined variable $key in /home/lockpick/Projects/posterg-website/memoire.php on line 68 -[05-May-2023 08:57:31 UTC] PHP Warning: Undefined variable $key in /home/lockpick/Projects/posterg-website/memoire.php on line 68 -[05-May-2023 08:57:40 UTC] PHP Warning: Undefined variable $key in /home/lockpick/Projects/posterg-website/memoire.php on line 68 -[05-May-2023 08:57:43 UTC] PHP Warning: Undefined variable $key in /home/lockpick/Projects/posterg-website/memoire.php on line 68 -[05-May-2023 08:57:48 UTC] PHP Warning: Undefined variable $key in /home/lockpick/Projects/posterg-website/memoire.php on line 68 -[05-May-2023 08:57:51 UTC] PHP Warning: Undefined variable $key in /home/lockpick/Projects/posterg-website/memoire.php on line 68 -[05-May-2023 08:58:15 UTC] PHP Warning: Undefined variable $key in /home/lockpick/Projects/posterg-website/memoire.php on line 68 -[05-May-2023 09:00:33 UTC] PHP Warning: Undefined variable $key in /home/lockpick/Projects/posterg-website/memoire.php on line 68 -[05-May-2023 09:02:53 UTC] PHP Warning: Undefined variable $key in /home/lockpick/Projects/posterg-website/memoire.php on line 68 -[05-May-2023 09:17:40 UTC] PHP Warning: Undefined variable $key in /home/lockpick/Projects/posterg-website/memoire.php on line 68 -[05-May-2023 09:43:22 UTC] PHP Warning: Undefined array key "description" in /home/lockpick/Projects/posterg-website/memoire.php on line 70 -[05-May-2023 09:45:04 UTC] PHP Warning: Undefined array key "description" in /home/lockpick/Projects/posterg-website/memoire.php on line 70 -[05-May-2023 09:49:04 UTC] PHP Warning: Undefined array key "resume" in /home/lockpick/Projects/posterg-website/index.php on line 58 -[05-May-2023 09:49:04 UTC] PHP Warning: Undefined array key "resume" in /home/lockpick/Projects/posterg-website/index.php on line 58 -[05-May-2023 09:49:04 UTC] PHP Warning: Undefined array key "resume" in /home/lockpick/Projects/posterg-website/index.php on line 58 -[05-May-2023 09:49:04 UTC] PHP Warning: Undefined array key "resume" in /home/lockpick/Projects/posterg-website/index.php on line 58 -[05-May-2023 09:49:04 UTC] PHP Warning: Undefined array key "resume" in /home/lockpick/Projects/posterg-website/index.php on line 58 -[05-May-2023 10:06:01 UTC] PHP Warning: Undefined array key "description" in /home/lockpick/Projects/posterg-website/memoire.php on line 70 -[05-May-2023 10:06:23 UTC] PHP Warning: Undefined array key "description" in /home/lockpick/Projects/posterg-website/memoire.php on line 70 -[05-May-2023 10:06:36 UTC] PHP Warning: Undefined array key "resume" in /home/lockpick/Projects/posterg-website/index.php on line 58 -[05-May-2023 10:06:36 UTC] PHP Warning: Undefined array key "resume" in /home/lockpick/Projects/posterg-website/index.php on line 58 -[05-May-2023 10:06:36 UTC] PHP Warning: Undefined array key "resume" in /home/lockpick/Projects/posterg-website/index.php on line 58 -[05-May-2023 10:06:36 UTC] PHP Warning: Undefined array key "resume" in /home/lockpick/Projects/posterg-website/index.php on line 58 -[05-May-2023 10:06:36 UTC] PHP Warning: Undefined array key "resume" in /home/lockpick/Projects/posterg-website/index.php on line 58 -[05-May-2023 10:06:42 UTC] PHP Warning: Undefined array key "description" in /home/lockpick/Projects/posterg-website/memoire.php on line 70 -[05-May-2023 10:06:44 UTC] PHP Warning: Undefined array key "resume" in /home/lockpick/Projects/posterg-website/index.php on line 58 -[05-May-2023 10:06:44 UTC] PHP Warning: Undefined array key "resume" in /home/lockpick/Projects/posterg-website/index.php on line 58 -[05-May-2023 10:06:44 UTC] PHP Warning: Undefined array key "resume" in /home/lockpick/Projects/posterg-website/index.php on line 58 -[05-May-2023 10:06:44 UTC] PHP Warning: Undefined array key "resume" in /home/lockpick/Projects/posterg-website/index.php on line 58 -[05-May-2023 10:06:44 UTC] PHP Warning: Undefined array key "resume" in /home/lockpick/Projects/posterg-website/index.php on line 58 -[05-May-2023 10:06:45 UTC] PHP Warning: Undefined array key "description" in /home/lockpick/Projects/posterg-website/memoire.php on line 70 -[05-May-2023 10:06:47 UTC] PHP Warning: Undefined array key "resume" in /home/lockpick/Projects/posterg-website/index.php on line 58 -[05-May-2023 10:06:47 UTC] PHP Warning: Undefined array key "resume" in /home/lockpick/Projects/posterg-website/index.php on line 58 -[05-May-2023 10:06:47 UTC] PHP Warning: Undefined array key "resume" in /home/lockpick/Projects/posterg-website/index.php on line 58 -[05-May-2023 10:06:47 UTC] PHP Warning: Undefined array key "resume" in /home/lockpick/Projects/posterg-website/index.php on line 58 -[05-May-2023 10:06:47 UTC] PHP Warning: Undefined array key "resume" in /home/lockpick/Projects/posterg-website/index.php on line 58 -[05-May-2023 10:07:02 UTC] PHP Warning: Undefined array key "description" in /home/lockpick/Projects/posterg-website/memoire.php on line 70 -[05-May-2023 10:07:16 UTC] PHP Warning: Undefined array key "description" in /home/lockpick/Projects/posterg-website/index.php on line 58 -[05-May-2023 10:07:16 UTC] PHP Warning: Undefined array key "description" in /home/lockpick/Projects/posterg-website/index.php on line 58 -[05-May-2023 10:07:16 UTC] PHP Warning: Undefined array key "description" in /home/lockpick/Projects/posterg-website/index.php on line 58 -[05-May-2023 10:07:16 UTC] PHP Warning: Undefined array key "description" in /home/lockpick/Projects/posterg-website/index.php on line 58 -[05-May-2023 10:07:16 UTC] PHP Warning: Undefined array key "description" in /home/lockpick/Projects/posterg-website/index.php on line 58 -[05-May-2023 10:08:03 UTC] PHP Warning: Undefined array key "description" in /home/lockpick/Projects/posterg-website/index.php on line 58 -[05-May-2023 10:08:03 UTC] PHP Warning: Undefined array key "description" in /home/lockpick/Projects/posterg-website/index.php on line 58 -[05-May-2023 10:08:03 UTC] PHP Warning: Undefined array key "description" in /home/lockpick/Projects/posterg-website/index.php on line 58 -[05-May-2023 10:08:03 UTC] PHP Warning: Undefined array key "description" in /home/lockpick/Projects/posterg-website/index.php on line 58 -[05-May-2023 10:08:03 UTC] PHP Warning: Undefined array key "description" in /home/lockpick/Projects/posterg-website/index.php on line 58 diff --git a/apps/public/inc/header.php b/apps/public/inc/header.php deleted file mode 100644 index 2f04c8b..0000000 --- a/apps/public/inc/header.php +++ /dev/null @@ -1,30 +0,0 @@ - - - - - - - - - - Posterg - - - - - - \ No newline at end of file diff --git a/apps/public/index.php b/apps/public/index.php deleted file mode 100644 index 0ddecf3..0000000 --- a/apps/public/index.php +++ /dev/null @@ -1,87 +0,0 @@ -getPublishedTheses($itemsPerPage, $offset); - $totalItems = $db->countPublishedTheses(); - $totalPages = ceil($totalItems / $itemsPerPage); -} catch (Exception $e) { - error_log("Error loading theses: " . $e->getMessage()); - $itemsToLoad = []; - $totalPages = 0; -} - -include 'inc/header.php'; ?> - -
- -
- - \ No newline at end of file diff --git a/apps/public/licences.php b/apps/public/licences.php deleted file mode 100644 index 0f59af5..0000000 --- a/apps/public/licences.php +++ /dev/null @@ -1,18 +0,0 @@ - -
-
-
- Ce travail éditorial, concernant les licences de 2021-2022 est né d'une recherche menée par :

- Defez Aurélie
- Gervreau-Mercier Théophile
- Debaene Justine
- Troadec Marie
- Marly Olivia
- Goldberg Jacquemain Elodie
- -
-
-
- - - \ No newline at end of file diff --git a/apps/public/test_db.php b/apps/public/test_db.php deleted file mode 100644 index 63957dc..0000000 --- a/apps/public/test_db.php +++ /dev/null @@ -1,40 +0,0 @@ -countPublishedTheses(); - echo "✓ Found {$count} published theses\n"; - - // Test getting theses - $theses = $db->getPublishedTheses(5, 0); - echo "✓ Retrieved " . count($theses) . " theses\n"; - - if (count($theses) > 0) { - $first = $theses[0]; - echo "\nFirst thesis:\n"; - echo " ID: " . $first['id'] . "\n"; - echo " Title: " . $first['title'] . "\n"; - echo " Author(s): " . ($first['authors'] ?? 'N/A') . "\n"; - echo " Year: " . $first['year'] . "\n"; - - // Test getting single thesis - $thesis = $db->getThesisById($first['id']); - if ($thesis) { - echo "✓ Successfully retrieved thesis details\n"; - echo " Orientation: " . ($thesis['orientation'] ?? 'N/A') . "\n"; - echo " AP Program: " . ($thesis['ap_program'] ?? 'N/A') . "\n"; - echo " Files: " . (count($thesis['files'] ?? [])) . "\n"; - } - } - - echo "\n✓ All tests passed!\n"; - -} catch (Exception $e) { - echo "✗ Error: " . $e->getMessage() . "\n"; - exit(1); -} diff --git a/apps/public/tests/Integration/SearchTest.php b/apps/public/tests/Integration/SearchTest.php deleted file mode 100644 index 7e6e6fa..0000000 --- a/apps/public/tests/Integration/SearchTest.php +++ /dev/null @@ -1,121 +0,0 @@ -searchTheses([], 100, 0); - echo "Found " . count($allTheses) . " published theses\n"; - foreach ($allTheses as $thesis) { - echo " - [{$thesis['year']}] {$thesis['title']} by {$thesis['authors']}\n"; - } - echo "\n"; - - // Test 2: Full-text search - echo "Test 2: Full-text search for 'urbain'\n"; - $results = $db->searchTheses(['query' => 'urbain']); - echo "Found " . count($results) . " results\n"; - foreach ($results as $thesis) { - echo " - {$thesis['title']}\n"; - } - echo "\n"; - - // Test 3: Search by year - echo "Test 3: Search by year (2024)\n"; - $results = $db->searchTheses(['year' => 2024]); - echo "Found " . count($results) . " results\n"; - foreach ($results as $thesis) { - echo " - [{$thesis['year']}] {$thesis['title']}\n"; - } - echo "\n"; - - // Test 4: Search by orientation - echo "Test 4: Search by orientation (Installation-Performance)\n"; - $results = $db->searchTheses(['orientation' => 'Installation-Performance']); - echo "Found " . count($results) . " results\n"; - foreach ($results as $thesis) { - echo " - {$thesis['title']} ({$thesis['orientation']})\n"; - } - echo "\n"; - - // Test 5: Search by AP program - echo "Test 5: Search by AP program (Narration Spéculative)\n"; - $results = $db->searchTheses(['ap_program' => 'Narration Spéculative']); - echo "Found " . count($results) . " results\n"; - foreach ($results as $thesis) { - echo " - {$thesis['title']} ({$thesis['ap_program']})\n"; - } - echo "\n"; - - // Test 6: Search by keyword - echo "Test 6: Search by keyword (performance)\n"; - $results = $db->searchTheses(['keyword' => 'performance']); - echo "Found " . count($results) . " results\n"; - foreach ($results as $thesis) { - echo " - {$thesis['title']}\n"; - echo " Keywords: {$thesis['keywords']}\n"; - } - echo "\n"; - - // Test 7: Combined search - echo "Test 7: Combined search (query='performance' + year=2024)\n"; - $results = $db->searchTheses(['query' => 'performance', 'year' => 2024]); - echo "Found " . count($results) . " results\n"; - foreach ($results as $thesis) { - echo " - [{$thesis['year']}] {$thesis['title']}\n"; - } - echo "\n"; - - // Test 8: Get available years - echo "Test 8: Getting available years\n"; - $years = $db->getAvailableYears(); - echo "Available years: " . implode(', ', $years) . "\n\n"; - - // Test 9: Get orientations - echo "Test 9: Getting orientations\n"; - $orientations = $db->getOrientations(); - echo "Total orientations: " . count($orientations) . "\n"; - echo "Sample: " . $orientations[0]['name'] . ", " . $orientations[1]['name'] . ", ...\n\n"; - - // Test 10: Get keywords - echo "Test 10: Getting used keywords\n"; - $keywords = $db->getUsedKeywords(); - echo "Total keywords in use: " . count($keywords) . "\n"; - $keywordNames = array_map(function($k) { return $k['keyword']; }, $keywords); - echo "Keywords: " . implode(', ', array_slice($keywordNames, 0, 10)) . "...\n\n"; - - // Test 11: Count results - echo "Test 11: Count search results\n"; - $count = $db->countSearchResults(['year' => 2024]); - echo "Count for year 2024: $count\n\n"; - - // Test 12: Pagination - echo "Test 12: Testing pagination\n"; - $page1 = $db->searchTheses([], 2, 0); // First 2 results - $page2 = $db->searchTheses([], 2, 2); // Next 2 results - echo "Page 1 (first 2):\n"; - foreach ($page1 as $thesis) { - echo " - {$thesis['title']}\n"; - } - echo "Page 2 (next 2):\n"; - foreach ($page2 as $thesis) { - echo " - {$thesis['title']}\n"; - } - echo "\n"; - - echo "✅ All tests completed successfully!\n"; - -} catch (Exception $e) { - echo "❌ Error: " . $e->getMessage() . "\n"; - echo "Stack trace:\n" . $e->getTraceAsString() . "\n"; - exit(1); -} diff --git a/apps/public/tests/MIGRATION_SUMMARY.md b/apps/public/tests/MIGRATION_SUMMARY.md deleted file mode 100644 index a321aae..0000000 --- a/apps/public/tests/MIGRATION_SUMMARY.md +++ /dev/null @@ -1,306 +0,0 @@ -# Test Migration Summary - -## ✅ Tests Reorganized Following PHP Standards - -The test files have been reorganized to follow PHP testing best practices. - ---- - -## What Changed - -### Before (Non-Standard) -``` -front-backend/ -├── test_search.php ❌ Tests in root -├── test_security.php ❌ Would deploy to production -├── test_security_updated.php ❌ No organization -├── test_rate_limit.php ❌ Mixed with application code -├── create_test_db.php ❌ Test fixtures in root -├── Database_secure.php ❌ Duplicate code -├── Database.php ✓ Application code -└── RateLimit.php ✓ Application code -``` - -### After (Standard) -``` -front-backend/ -├── tests/ ✅ Dedicated test directory -│ ├── Fixtures/ ✅ Test data & setup -│ │ └── CreateTestDatabase.php -│ ├── Integration/ ✅ Multi-component tests -│ │ └── SearchTest.php -│ ├── Security/ ✅ Security validation -│ │ └── SecurityTest.php -│ ├── Unit/ ✅ Individual component tests -│ │ └── RateLimitTest.php -│ └── README.md ✅ Test documentation -├── run-tests.php ✅ Convenient test runner -├── .gitignore ✅ Excludes cache, logs, etc. -├── Database.php ✓ Application code -└── RateLimit.php ✓ Application code -``` - ---- - -## Benefits Achieved - -### ✅ Production Safety -- **Tests excluded from deployment** via `justfile` -- **No test code in production** - cleaner, more secure -- **Smaller deployment size** - only application code deployed - -### ✅ Better Organization -- **Clear separation** - tests vs application code -- **Logical grouping** - unit, integration, security, fixtures -- **Standard structure** - other PHP developers will understand immediately - -### ✅ Easier Testing -- **Single command** - `php run-tests.php` runs everything -- **Individual tests** - `php tests/Security/SecurityTest.php` for specific tests -- **Better output** - formatted test results with summary - -### ✅ Future-Ready -- **PHPUnit compatible** - directory structure ready for migration -- **CI/CD ready** - easy to integrate with GitHub Actions, etc. -- **Scalable** - easy to add new tests in proper categories - ---- - -## Running Tests - -### Run All Tests -```bash -cd /home/padlock/dev/posterg-website/front-backend -php run-tests.php -``` - -**Output:** -``` -╔════════════════════════════════════════════╗ -║ Running Front-Backend Tests ║ -╚════════════════════════════════════════════╝ - -┌─────────────────────────────────────────┐ -│ Test Suite: Fixtures │ -└─────────────────────────────────────────┘ -✅ PASSED - -┌─────────────────────────────────────────┐ -│ Test Suite: Integration │ -└─────────────────────────────────────────┘ -✅ PASSED - -┌─────────────────────────────────────────┐ -│ Test Suite: Security │ -└─────────────────────────────────────────┘ -✅ PASSED - -┌─────────────────────────────────────────┐ -│ Test Suite: Unit │ -└─────────────────────────────────────────┘ -✅ PASSED - -╔════════════════════════════════════════════╗ -║ Test Summary ║ -╠════════════════════════════════════════════╣ -║ Total: 4 ║ -║ Passed: 4 ✅ ║ -║ Failed: 0 ║ -╚════════════════════════════════════════════╝ - -✅ All tests passed! -``` - -### Run Individual Tests -```bash -# Setup test database -php tests/Fixtures/CreateTestDatabase.php - -# Run specific test suite -php tests/Integration/SearchTest.php -php tests/Security/SecurityTest.php -php tests/Unit/RateLimitTest.php -``` - ---- - -## Deployment Configuration - -### Updated `justfile` - -The deployment now excludes test files: - -```just -[group('deploy')] -deploy: - rsync -vur --progress \ - --exclude '*.db' \ - --exclude 'tests/' \ - --exclude 'cache/' \ - --exclude '*.md' \ - --exclude 'run-tests.php' \ - ./front-backend/ posterg:/var/www/html/ -``` - -**What's Excluded:** -- `tests/` - All test files -- `*.db` - Test databases -- `cache/` - Runtime cache (rate limiting) -- `*.md` - Documentation files -- `run-tests.php` - Test runner - -**What's Deployed:** -- Application code (`.php` files) -- Assets (`assets/` directory) -- Templates (`inc/` directory) -- Public pages (`index.php`, `search.php`, etc.) - -### New `.gitignore` - -```gitignore -/vendor/ -/cache/ -*.db -*.log -.env -.env.local -``` - ---- - -## Test Organization Explained - -### 1. Fixtures (`tests/Fixtures/`) -**Purpose:** Test data setup and database initialization - -**Files:** -- `CreateTestDatabase.php` - Creates test.db with sample theses - -**When to run:** Before running other tests - -### 2. Integration Tests (`tests/Integration/`) -**Purpose:** Test multiple components working together - -**Files:** -- `SearchTest.php` - Full search functionality with filters - -**What it tests:** -- Full-text search -- Year filtering -- Orientation filtering -- AP program filtering -- Keyword search -- Combined filters -- Pagination - -### 3. Security Tests (`tests/Security/`) -**Purpose:** Verify security measures are working - -**Files:** -- `SecurityTest.php` - All security validations - -**What it tests:** -- Wildcard injection prevention -- Input length validation (max 200 chars) -- Year range validation (1900-2100) -- SQL injection prevention -- Pagination limits (max 100/page) - -### 4. Unit Tests (`tests/Unit/`) -**Purpose:** Test individual components in isolation - -**Files:** -- `RateLimitTest.php` - Rate limiting functionality - -**What it tests:** -- Request tracking -- Limit enforcement (5 requests in test, 30 in production) -- Reset time calculation -- Header generation - ---- - -## Comparison with Professional Projects - -| Aspect | This Project | Laravel/Symfony | Status | -|--------|--------------|-----------------|--------| -| Test directory | `tests/` | `tests/` | ✅ Match | -| Test organization | Unit/Integration/Security | Unit/Feature | ✅ Good | -| Test framework | PHP scripts | PHPUnit | ⚠️ Can migrate | -| Deployment exclusion | Via rsync | Via .deployignore | ✅ Works | -| Runner | Custom script | `composer test` | ⚠️ Can improve | -| CI/CD | Manual | GitHub Actions | ⚠️ Future | - -**Current Status:** Following PHP conventions, ready for growth - -**Future Migration Path:** Can easily migrate to PHPUnit when needed - ---- - -## Next Steps (Optional) - -### For Small Projects (Current Approach is Fine) -- ✅ Keep using simple PHP test scripts -- ✅ Run `php run-tests.php` before deploying -- ✅ Tests are properly organized and excluded - -### To Upgrade to PHPUnit (When Project Grows) - -1. **Install PHPUnit:** - ```bash - composer require --dev phpunit/phpunit - ``` - -2. **Convert tests to PHPUnit format:** - ```php - // Instead of: - echo "Test result: " . ($result ? "✅" : "❌") . "\n"; - - // Use: - $this->assertTrue($result); - ``` - -3. **Add `phpunit.xml` configuration** - -4. **Run with:** `composer test` - -See `TESTING_BEST_PRACTICES.md` for complete migration guide. - ---- - -## Files Created/Modified - -### New Files -- ✅ `tests/` directory structure -- ✅ `tests/README.md` - Test documentation -- ✅ `run-tests.php` - Test runner script -- ✅ `.gitignore` - Git exclusions - -### Moved Files -- ✅ `test_search.php` → `tests/Integration/SearchTest.php` -- ✅ `test_security_updated.php` → `tests/Security/SecurityTest.php` -- ✅ `test_rate_limit.php` → `tests/Unit/RateLimitTest.php` -- ✅ `create_test_db.php` → `tests/Fixtures/CreateTestDatabase.php` - -### Updated Files -- ✅ All test files (updated `require_once` paths) -- ✅ `justfile` (added test exclusions) - -### Removed Files -- ✅ `test_security.php` (obsolete, replaced by SecurityTest.php) -- ✅ `Database_secure.php` (obsolete, functionality in Database.php) - ---- - -## Summary - -✅ **Organized** - Tests follow PHP conventions -✅ **Secure** - Tests excluded from production -✅ **Convenient** - Single command to run all tests -✅ **Documented** - README explains structure -✅ **Scalable** - Easy to add new tests -✅ **Future-ready** - Can migrate to PHPUnit later - -**All tests passing:** 4/4 ✅ - -**Ready for production deployment!** diff --git a/apps/public/tests/README.md b/apps/public/tests/README.md deleted file mode 100644 index fab7520..0000000 --- a/apps/public/tests/README.md +++ /dev/null @@ -1,108 +0,0 @@ -# Tests Directory - -This directory contains all tests for the front-backend application. - -## Structure - -``` -tests/ -├── Fixtures/ # Test data and setup scripts -│ └── CreateTestDatabase.php -├── Integration/ # Integration tests (multiple components) -│ └── SearchTest.php -├── Security/ # Security-focused tests -│ └── SecurityTest.php -└── Unit/ # Unit tests (individual methods) - └── RateLimitTest.php -``` - -## Running Tests - -### Run All Tests -```bash -php run-tests.php -``` - -### Run Individual Tests -```bash -# Setup test database first -php tests/Fixtures/CreateTestDatabase.php - -# Run specific test -php tests/Integration/SearchTest.php -php tests/Security/SecurityTest.php -php tests/Unit/RateLimitTest.php -``` - -## Test Suites - -### Fixtures -Test data setup and database initialization. - -**CreateTestDatabase.php** -- Creates test.db with sample theses -- Populates with 6 sample records -- Includes authors, supervisors, keywords - -### Integration Tests -Test multiple components working together. - -**SearchTest.php** -- Tests full search functionality -- Tests filtering (year, orientation, AP, keywords) -- Tests pagination -- Tests combined filters - -### Security Tests -Verify security measures are working. - -**SecurityTest.php** -- Wildcard injection prevention -- Input length validation -- Year range validation -- SQL injection prevention -- Pagination limits - -### Unit Tests -Test individual components in isolation. - -**RateLimitTest.php** -- Rate limit enforcement -- Request tracking -- Reset time calculation -- Header generation - -## Expected Results - -All tests should pass: -``` -✅ PASSED - Fixtures/CreateTestDatabase.php -✅ PASSED - Integration/SearchTest.php -✅ PASSED - Security/SecurityTest.php -✅ PASSED - Unit/RateLimitTest.php -``` - -## Deployment - -**Tests are NOT deployed to production.** - -The deployment configuration (`justfile`) excludes: -- `tests/` directory -- `*.db` files -- Cache directory -- Documentation files - -## Future Migration to PHPUnit - -This directory structure is compatible with PHPUnit. To migrate: - -1. Install PHPUnit: - ```bash - composer require --dev phpunit/phpunit - ``` - -2. Convert test files to PHPUnit format -3. Add `phpunit.xml` configuration -4. Run with: `composer test` - -See `TESTING_BEST_PRACTICES.md` for details. diff --git a/apps/public/tests/Security/SecurityTest.php b/apps/public/tests/Security/SecurityTest.php deleted file mode 100644 index 31f5700..0000000 --- a/apps/public/tests/Security/SecurityTest.php +++ /dev/null @@ -1,119 +0,0 @@ -searchTheses(['query' => '%'], 10, 0); - echo "Results found: " . count($results) . "\n"; - if (count($results) === 0 || count($results) < 6) { - echo "✅ SECURE: Wildcard characters are escaped!\n"; - } else { - echo "❌ VULNERABLE: Still matching everything!\n"; - } - echo "\n"; - - // Test 2: Underscore wildcard - echo "Test 2: Underscore Wildcard (should be escaped)\n"; - $results = $db->searchTheses(['query' => '_'], 10, 0); - echo "Searching for '_': " . count($results) . " results\n"; - if (count($results) === 0 || count($results) < 6) { - echo "✅ SECURE: Underscore wildcard is escaped!\n"; - } else { - echo "❌ VULNERABLE: Underscore matches everything!\n"; - } - echo "\n"; - - // Test 3: Long input validation - echo "Test 3: Long Input String Validation\n"; - $longString = str_repeat('test', 1000); // 4000 characters - echo "Attempting to search for " . strlen($longString) . " character string\n"; - try { - $results = $db->searchTheses(['query' => $longString], 10, 0); - echo "❌ VULNERABLE: Long input was accepted!\n"; - } catch (InvalidArgumentException $e) { - echo "✅ SECURE: Long input rejected: " . $e->getMessage() . "\n"; - } - echo "\n"; - - // Test 4: Invalid year validation - echo "Test 4: Invalid Year Validation\n"; - try { - $results = $db->searchTheses(['year' => 999999], 10, 0); - echo "❌ VULNERABLE: Invalid year accepted!\n"; - } catch (InvalidArgumentException $e) { - echo "✅ SECURE: Invalid year rejected: " . $e->getMessage() . "\n"; - } - echo "\n"; - - // Test 5: SQL Injection still prevented - echo "Test 5: SQL Injection Prevention\n"; - $injectionTests = [ - "' OR 1=1--", - "'; DROP TABLE theses;--", - ]; - - foreach ($injectionTests as $injection) { - echo "Testing: $injection\n"; - try { - $results = $db->searchTheses(['query' => $injection], 10, 0); - echo " Results: " . count($results) . " (treated as literal string)\n"; - echo " ✅ SAFE: SQL injection prevented\n"; - } catch (Exception $e) { - echo " Error: " . $e->getMessage() . "\n"; - } - } - echo "\n"; - - // Test 6: Pagination limits - echo "Test 6: Pagination Limits\n"; - $results = $db->searchTheses([], 500, 0); // Try to get 500 results - echo "Requested 500 results, got: " . count($results) . "\n"; - if (count($results) <= 100) { - echo "✅ SECURE: Pagination limited to max 100 results\n"; - } else { - echo "❌ VULNERABLE: Pagination allows too many results\n"; - } - echo "\n"; - - // Test 7: Negative offset - echo "Test 7: Negative Offset Protection\n"; - $results = $db->searchTheses([], 10, -100); - echo "Requested offset -100, query succeeded: " . (count($results) >= 0 ? 'yes' : 'no') . "\n"; - echo "✅ SECURE: Negative offsets handled safely\n\n"; - - // Test 8: Normal search still works - echo "Test 8: Normal Search Functionality\n"; - $results = $db->searchTheses(['query' => 'urbain'], 10, 0); - echo "Searching for 'urbain': " . count($results) . " results\n"; - if (count($results) > 0) { - echo " Found: " . $results[0]['title'] . "\n"; - } - echo "✅ Normal searches still work correctly\n\n"; - - // Summary - echo "=== SECURITY SUMMARY ===\n\n"; - echo "✅ SECURE from SQL Injection (prepared statements)\n"; - echo "✅ SECURE from wildcard injection (escaped)\n"; - echo "✅ SECURE from DoS via long inputs (length validation)\n"; - echo "✅ SECURE from invalid year values (range validation)\n"; - echo "✅ SECURE from excessive pagination (max 100 per page)\n"; - echo "✅ SECURE from negative offsets (validated)\n\n"; - - echo "✅ ALL SECURITY TESTS PASSED!\n"; - echo "The implementation is production-ready.\n"; - -} catch (Exception $e) { - echo "❌ Unexpected error: " . $e->getMessage() . "\n"; - exit(1); -} diff --git a/apps/public/tests/Unit/RateLimitTest.php b/apps/public/tests/Unit/RateLimitTest.php deleted file mode 100644 index ce9c6d1..0000000 --- a/apps/public/tests/Unit/RateLimitTest.php +++ /dev/null @@ -1,58 +0,0 @@ -check(); - echo "Request $i: " . ($allowed ? "✅ Allowed" : "❌ Blocked") . "\n"; - echo " Remaining: " . $rateLimit->getRemaining() . "\n"; -} -echo "\n"; - -// Test 2: Make 6th request (should be blocked) -echo "Test 2: Making 6th request (should be blocked)\n"; -$allowed = $rateLimit->check(); -echo "Request 6: " . ($allowed ? "❌ Allowed (FAIL)" : "✅ Blocked (SUCCESS)") . "\n"; -echo "Remaining: " . $rateLimit->getRemaining() . "\n"; -echo "Reset time: " . $rateLimit->getResetTime() . " seconds\n\n"; - -// Test 3: Wait and try again -echo "Test 3: Waiting 3 seconds and trying again...\n"; -sleep(3); -$allowed = $rateLimit->check(); -echo "Request after 3s: " . ($allowed ? "❌ Allowed (still in window)" : "✅ Blocked") . "\n"; -echo "Remaining: " . $rateLimit->getRemaining() . "\n\n"; - -// Test 4: Test headers (CLI simulation) -echo "Test 4: Rate limit headers (simulated)\n"; -echo "X-RateLimit-Limit: 5\n"; -echo "X-RateLimit-Remaining: " . $rateLimit->getRemaining() . "\n"; -echo "X-RateLimit-Reset: " . (time() + $rateLimit->getResetTime()) . "\n"; -echo "\n"; - -// Test 5: Cleanup -echo "Test 5: Testing cleanup function\n"; -$rateLimit->cleanup(); -echo "✅ Cleanup executed successfully\n\n"; - -echo "=== RATE LIMITING SUMMARY ===\n\n"; -echo "✅ Rate limiting works correctly\n"; -echo "✅ Requests are tracked per client\n"; -echo "✅ Limits are enforced\n"; -echo "✅ Reset time is calculated\n"; -echo "✅ Headers are sent\n"; -echo "✅ Cleanup removes old files\n\n"; - -echo "Ready for production use!\n"; diff --git a/apps/public/assets/grid.css b/assets/admin.css similarity index 100% rename from apps/public/assets/grid.css rename to assets/admin.css diff --git a/apps/admin/assets/icon.svg b/assets/admin_favicon.svg similarity index 100% rename from apps/admin/assets/icon.svg rename to assets/admin_favicon.svg diff --git a/apps/admin/assets/posterg.css b/assets/common.css similarity index 100% rename from apps/admin/assets/posterg.css rename to assets/common.css diff --git a/apps/admin/assets/Combinedd.otf b/assets/fonts/Combinedd.otf similarity index 100% rename from apps/admin/assets/Combinedd.otf rename to assets/fonts/Combinedd.otf diff --git a/apps/public/assets/icons.svg b/assets/icons.svg similarity index 100% rename from apps/public/assets/icons.svg rename to assets/icons.svg diff --git a/assets/main.css b/assets/main.css new file mode 100644 index 0000000..e69de29 diff --git a/assets/modern-normalize.min.css b/assets/modern-normalize.min.css new file mode 100644 index 0000000..8b0741a --- /dev/null +++ b/assets/modern-normalize.min.css @@ -0,0 +1,9 @@ +/** + * Minified by jsDelivr using clean-css v5.3.3. + * Original file: /npm/modern-normalize@3.0.1/modern-normalize.css + * + * Do NOT use SRI with dynamically generated files! More information: https://www.jsdelivr.com/using-sri-with-dynamic-files + */ +/*! modern-normalize v3.0.1 | MIT License | https://github.com/sindresorhus/modern-normalize */ +*,::after,::before{box-sizing:border-box}html{font-family:system-ui,'Segoe UI',Roboto,Helvetica,Arial,sans-serif,'Apple Color Emoji','Segoe UI Emoji';line-height:1.15;-webkit-text-size-adjust:100%;tab-size:4}body{margin:0}b,strong{font-weight:bolder}code,kbd,pre,samp{font-family:ui-monospace,SFMono-Regular,Consolas,'Liberation Mono',Menlo,monospace;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}table{border-color:currentcolor}button,input,optgroup,select,textarea{font-family:inherit;font-size:100%;line-height:1.15;margin:0}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button}legend{padding:0}progress{vertical-align:baseline}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item} +/*# sourceMappingURL=/sm/d2d8cd206fb9f42f071e97460f3ad9c875edb5e7a4b10f900a83cdf8401c53a9.map */ \ No newline at end of file diff --git a/DATABASE_CONFIG.md b/docs/DATABASE_CONFIG.md similarity index 100% rename from DATABASE_CONFIG.md rename to docs/DATABASE_CONFIG.md diff --git a/docs/DEVELOPMENT_GUIDE.md b/docs/DEVELOPMENT_GUIDE.md new file mode 100644 index 0000000..4146c5f --- /dev/null +++ b/docs/DEVELOPMENT_GUIDE.md @@ -0,0 +1,507 @@ +# Post-ERG Development Guide + +Complete guide for developing the Post-ERG thesis management system. + +## 🚀 Quick Start + +### 1. Setup (One Time) + +```bash +# Clone php-live-reload and setup directories +just setup +``` + +### 2. Start Development Server + +```bash +just serve +``` + +This starts **one unified server** at: +- **Public site:** http://localhost:8000 +- **Admin panel:** http://localhost:8000/admin/ + +✨ **Live reload enabled** - your browser auto-refreshes when you save files! + +### 3. Edit & Watch + +Edit any PHP file and watch your browser automatically refresh! 🎉 + +--- + +## 📁 Project Structure + +``` +posterg-website/ +├── index.php # Public homepage +├── memoire.php # Thesis detail page +├── search.php # Search page +├── *.php # Other public pages +│ +├── admin/ # Admin panel +│ ├── index.php # Admin dashboard +│ ├── list.php # Thesis list +│ ├── edit.php # Edit thesis +│ └── formulaire.php # Add thesis +│ +├── lib/ # Shared libraries +│ ├── Database.php # Database class +│ ├── RateLimit.php # Rate limiting +│ └── config.php # Configuration +│ +├── inc/ # Page templates +│ ├── header.php # Site header +│ └── footer.php # Site footer +│ +├── assets/ # Static files +│ ├── posterg.css # Main CSS +│ └── fonts/ # Custom fonts +│ +├── database/ # Database +│ ├── schema.sql # Schema definition +│ ├── test.db # Test database +│ └── fixtures/ # Sample data +│ +├── tests/ # Test suite +│ ├── Unit/ # Unit tests +│ ├── Integration/ # Integration tests +│ └── Security/ # Security tests +│ +└── vendor/ # Third-party (gitignored) + └── php-live-reload/ +``` + +--- + +## 🛠️ Development Workflow + +### Starting Development + +```bash +# Start the development server +just serve + +# In your browser: +# - Public site: http://localhost:8000 +# - Admin panel: http://localhost:8000/admin/ +``` + +### Making Changes + +1. **Edit PHP files** - Auto-refreshes browser +2. **Edit CSS** - Auto-refreshes browser +3. **Test changes** - See them instantly! + +### Running Tests + +```bash +# Run all tests +just test + +# Run specific test suites +just test-unit # Unit tests +just test-integration # Integration tests +just test-security # Security tests +``` + +### Database Operations + +```bash +# View database stats +just stats + +# Open SQLite shell +just query + +# Show specific thesis +just show 42 + +# Reset database +just reset-db + +# Create with sample data +just fixtures +``` + +--- + +## 📝 Common Tasks + +### Create a New Page + +1. Create `newpage.php` in root +2. Add `require_once __DIR__ . '/lib/Database.php';` +3. Include header: `include 'inc/header.php';` +4. Add your content +5. Include footer: `include 'inc/footer.php';` + +Example: +```php + + +
+
+

My New Page

+

Content here...

+
+
+ + +``` + +### Add a Database Function + +1. Edit `lib/Database.php` +2. Add your method to the `Database` class +3. Write a test in `tests/Unit/DatabaseTest.php` +4. Run tests: `just test-unit` + +### Update CSS + +1. Edit `assets/posterg.css` +2. Browser auto-refreshes! +3. (Optional) Increment version in `inc/header.php`: `posterg.css?v=3` + +### Add a Test + +1. Choose location: + - `tests/Unit/` - For testing classes/functions + - `tests/Integration/` - For testing workflows + - `tests/Security/` - For testing security + +2. Create test file (e.g., `tests/Unit/MyTest.php`) + +3. Follow the template in `tests/README.md` + +4. Add to `tests/run-tests.php` + +5. Run: `just test` + +--- + +## 🧪 Testing + +### Test Database + +Development uses `database/test.db` (gitignored). + +**Create test database:** +```bash +just init-db +``` + +**Populate with sample data:** +```bash +just fixtures +``` + +**Deploy test database to server:** +```bash +just deploy-test-db +``` + +**Reset everything:** +```bash +just reset-db +``` + +### Writing Tests + +See `tests/README.md` for complete testing guide. + +**Quick example:** +```php +getMessage() . "\n"; + return false; +} +``` + +--- + +## 🚀 Deployment + +### Deploy Everything + +```bash +just deploy +``` + +This deploys: +- Public site (root PHP files) +- Admin panel (`admin/`) +- Shared libraries (`lib/`) + +**Note:** `vendor/` is automatically excluded from deployment. + +### Deploy Selectively + +```bash +# Deploy only the code +just deploy + +# Deploy test database +just deploy-test-db + +# Deploy nginx config +just deploy-nginx + +# Deploy admin tools +just deploy-admin-tools +``` + +--- + +## 🔧 Justfile Commands + +### Development + +| Command | Description | +|---------|-------------| +| `just setup` | Setup development environment (one-time) | +| `just serve` | Start development server with live reload | +| `just stop` | Stop development server | +| `just logs` | View development logs | + +### Testing + +| Command | Description | +|---------|-------------| +| `just test` | Run all tests | +| `just test-unit` | Run unit tests | +| `just test-integration` | Run integration tests | +| `just test-security` | Run security tests | +| `just syntax` | Check PHP syntax | + +### Database + +| Command | Description | +|---------|-------------| +| `just init-db` | Create test database | +| `just reset-db` | Reset test database | +| `just query` | Open SQLite shell | +| `just show ` | Show thesis by ID | +| `just backup` | Backup database | +| `just fixtures` | Create sample data | +| `just deploy-test-db` | Deploy test database to server | + +### Statistics + +| Command | Description | +|---------|-------------| +| `just stats` | Show database statistics | +| `just recent` | Show recent theses | + +### Deployment + +| Command | Description | +|---------|-------------| +| `just deploy` | Deploy complete site | +| `just deploy-nginx` | Deploy nginx configuration | +| `just deploy-admin-tools` | Deploy admin user management | + +### Server + +| Command | Description | +|---------|-------------| +| `just server-logs` | View server logs | +| `just server-status` | Check server status | + +### Utilities + +| Command | Description | +|---------|-------------| +| `just clean` | Clean up dev files | +| `just setup-dirs` | Create data directories | + +--- + +## 💡 Tips & Tricks + +### Live Reload + +The `just serve` command uses php-live-reload to automatically refresh your browser when you save files. + +**What triggers refresh:** +- Saving any `.php` file +- Saving any file in the project + +**How it works:** +- WebSocket connection monitors file changes +- Browser receives reload signal +- Page refreshes automatically + +**No browser extension needed!** + +### Multiple Browser Windows + +Open multiple browser windows/tabs - they all get live reload! + +``` +http://localhost:8000/ # Public site +http://localhost:8000/admin/ # Admin panel +http://localhost:8000/memoire.php?id=13 # Specific thesis +``` + +All will auto-refresh when you save files! ✨ + +### Using a Real Test Database + +The test database (`database/test.db`) is gitignored. To share test data: + +```bash +# Create fixtures +just fixtures + +# Commit the fixtures generator +git add database/fixtures/ +git commit -m "Update test fixtures" +``` + +Others can recreate with: `just fixtures` + +### Debugging + +**Check error logs:** +```bash +just logs +``` + +**Or directly:** +```bash +tail -f error.log +``` + +**PHP errors in browser:** +Edit your PHP file temporarily: +```php +ini_set('display_errors', 1); +error_reporting(E_ALL); +``` + +**Database issues:** +```bash +# Check database exists +ls -lh database/test.db + +# Open database shell +just query + +# Check tables +sqlite> .tables + +# Show schema +sqlite> .schema theses +``` + +--- + +## 🔍 Troubleshooting + +### Server won't start + +**Port already in use:** +```bash +just stop +# Or manually: +pkill -f "php -S 127.0.0.1:8000" +``` + +**php-live-reload missing:** +```bash +just setup +``` + +### Live reload not working + +**Check vendor directory:** +```bash +ls -la vendor/php-live-reload/ +``` + +**Reinstall:** +```bash +rm -rf vendor/php-live-reload +just setup +``` + +### Database errors + +**Database not found:** +```bash +just init-db +``` + +**Permissions error:** +```bash +chmod 644 database/test.db +``` + +**Schema errors:** +```bash +just reset-db +``` + +### Tests failing + +**Run individual test:** +```bash +php tests/Unit/DatabaseTest.php +``` + +**Check database:** +```bash +just stats +``` + +**Reset database:** +```bash +just reset-db +just fixtures +just test +``` + +--- + +## 📚 Further Reading + +- [Test Documentation](../tests/README.md) +- [Database Specification](../database/DATABASE_SPECIFICATION.md) +- [Migration Guide](../MIGRATION_GUIDE.md) +- [Deployment Guide](../nginx/DEPLOYMENT_COMPLETE.md) + +--- + +## ✨ Quick Reference + +**Start developing:** +```bash +just setup # One time +just serve # Start server +``` + +**Test your changes:** +```bash +just test # Run tests +just stats # Check database +``` + +**Deploy to production:** +```bash +just deploy +``` + +That's it! Happy coding! 🚀 diff --git a/docs/LIVE_RELOAD_SETUP.md b/docs/LIVE_RELOAD_SETUP.md new file mode 100644 index 0000000..8a06301 --- /dev/null +++ b/docs/LIVE_RELOAD_SETUP.md @@ -0,0 +1,297 @@ +# Live Reload Setup + +Guide to setting up and using live reload for Post-ERG development. + +## 🎯 What is Live Reload? + +Live reload automatically refreshes your browser when you save files during development. No need to manually hit refresh! + +## ✨ How It Works + +1. **JavaScript** in the page polls the server for file changes +2. **PHP backend** checks file modification times +3. **Browser** automatically refreshes when changes detected + +**No browser extension needed!** + +--- + +## 🚀 Setup (One Time) + +```bash +just setup +``` + +This clones `php-live-reload` into `vendor/php-live-reload/` (which is gitignored). + +--- + +## 🏃 Using Live Reload + +### Start Server + +```bash +just serve +``` + +Output: +``` +🚀 Starting Post-ERG development server +======================================== + +📍 Public site: http://localhost:8000 +📍 Admin panel: http://localhost:8000/admin/ + +✨ Live reload enabled - browser auto-refreshes on file save! + +Press Ctrl+C to stop +``` + +### Edit Files + +1. Open http://localhost:8000 in your browser +2. Edit any PHP, CSS, or JS file +3. Save the file +4. **Browser automatically refreshes!** ✨ + +### What Triggers Reload + +- Saving `.php` files +- Saving `.css` files +- Saving `.js` files +- Any file change in the project + +--- + +## 🔧 How It's Integrated + +### In `inc/header.php` + +Live reload script is conditionally included only during development: + +```php + + + + +``` + +**Result:** +- ✅ Included when using `php -S` (development server) +- ❌ NOT included in production (nginx/apache) +- ❌ NOT deployed to server (vendor/ is gitignored) + +### Detection Logic + +```php +php_sapi_name() === 'cli-server' // True when using PHP dev server +``` + +This means live reload is automatically enabled/disabled based on environment! + +--- + +## 📁 File Structure + +``` +vendor/php-live-reload/ +├── php-live-reload/ +│ ├── live-reload.js # JavaScript client +│ ├── live-reload.php # PHP backend (checks files) +│ └── config.php # Configuration +└── README.md +``` + +--- + +## 🧪 Testing Live Reload + +### Test It Works + +1. Start server: `just serve` +2. Open http://localhost:8000 +3. Open browser console (F12) +4. Edit `index.php` and save +5. Watch browser auto-refresh! + +You'll see in console: +```javascript +GET /vendor/php-live-reload/php-live-reload/live-reload.php +change detected +``` + +--- + +## ⚙️ Configuration + +### Polling Interval + +Default: Checks every ~1-2 seconds + +To change, edit `vendor/php-live-reload/php-live-reload/config.php`: + +```php +define('MIN_DELAY', 1000); // Minimum milliseconds between checks +``` + +### File Watching + +By default, watches all files in project directory. + +To exclude paths, edit config: + +```php +$exclude = [ + 'vendor', + '.git', + 'node_modules', + 'cache' +]; +``` + +--- + +## 🐛 Troubleshooting + +### Live Reload Not Working + +**1. Check vendor directory exists:** +```bash +ls -la vendor/php-live-reload/ +``` + +If missing: +```bash +just setup +``` + +**2. Check script is included in page:** +```bash +curl -s http://localhost:8000/ | grep live-reload +``` + +Should show: +```html + +``` + +**3. Check endpoint is accessible:** +```bash +curl http://localhost:8000/vendor/php-live-reload/php-live-reload/live-reload.php +``` + +Should return JSON: +```json +{"time": 10, "changed": false} +``` + +**4. Check browser console for errors** + +Open browser console (F12) and look for: +- WebSocket errors +- Network errors to `/vendor/php-live-reload/` + +### Script Not Loading + +**Make sure you're using dev server:** +```bash +just serve +``` + +NOT production (nginx/apache). + +**Check PHP detection:** +```bash +php -r "echo php_sapi_name();" +``` + +When using `just serve`, should output: `cli-server` + +### Changes Not Detected + +**Check polling is working:** + +Open browser console, you should see repeated requests to: +``` +/vendor/php-live-reload/php-live-reload/live-reload.php +``` + +If not, check JavaScript loaded: +```bash +curl -I http://localhost:8000/vendor/php-live-reload/php-live-reload/live-reload.js +``` + +Should return `200 OK`. + +--- + +## 🚫 Production Behavior + +### Automatically Disabled + +Live reload is **automatically disabled** in production because: + +1. **`php_sapi_name()` check**: Only true with PHP dev server +2. **vendor/ gitignored**: Not deployed to server +3. **nginx serves files**: Different SAPI, condition false + +### Verification + +On production server: +```bash +curl https://posterg.erg.be/ | grep live-reload +``` + +Should return nothing (script not included). + +--- + +## 💡 Tips + +### Multiple Browser Windows + +Open multiple tabs/windows - they all get live reload! + +``` +http://localhost:8000/ # Homepage +http://localhost:8000/admin/ # Admin panel +http://localhost:8000/memoire.php?id=13 # Thesis page +``` + +All will auto-refresh on file changes. + +### Faster Development + +With live reload: +1. ✅ Edit code +2. ✅ Save +3. ✅ Browser refreshes +4. ✅ See changes instantly! + +No more: +1. ❌ Edit code +2. ❌ Save +3. ❌ Switch to browser +4. ❌ Hit F5 +5. ❌ Switch back to editor + +**Saves you seconds on every change!** + +--- + +## 📚 More Information + +- GitHub: https://github.com/ryantate13/php-live-reload +- Alternative: https://github.com/guard/guard-livereload + +--- + +## ✅ Quick Reference + +| Command | Description | +|---------|-------------| +| `just setup` | Install live reload (one time) | +| `just serve` | Start server with live reload | +| `just stop` | Stop server | + +**To use:** `just serve` and edit files - browser auto-refreshes! ✨ diff --git a/docs/MIGRATION_GUIDE.md b/docs/MIGRATION_GUIDE.md new file mode 100644 index 0000000..87a3c2a --- /dev/null +++ b/docs/MIGRATION_GUIDE.md @@ -0,0 +1,476 @@ +# Repository Restructure Migration Guide + +This guide explains how to migrate the Post-ERG repository to a standard idiomatic PHP structure. + +## 📋 Overview + +### Current Structure +``` +posterg-website/ +├── apps/ +│ ├── public/ # Public website +│ └── admin/ # Admin panel +├── shared/ # Shared PHP libraries +└── database/ # Database files +``` + +### New Structure (Standard PHP) +``` +posterg-website/ +├── index.php # Public root +├── *.php # Public PHP files +├── admin/ # Admin panel +├── lib/ # Shared libraries (renamed from shared/) +├── inc/ # Templates (header/footer) +├── assets/ # Static files +├── database/ # Database files +└── vendor/ # Third-party code (gitignored) +``` + +--- + +## 🎯 Benefits + +✅ **Standard PHP structure** - Follows community conventions +✅ **Flatter hierarchy** - Easier navigation +✅ **Simpler paths** - Less `../../` in code +✅ **Cleaner deployment** - Single rsync command +✅ **Live reload** - Auto-refresh during development + +--- + +## 🚀 Migration Steps + +### Step 1: Review the Plan + +Read the restructure plan: +```bash +cat docs/RESTRUCTURE_PLAN.md +``` + +### Step 2: Commit Current State + +**Important:** Commit all your current work first! + +```bash +git add -A +git commit -m "Pre-migration checkpoint" +``` + +### Step 3: Run Migration Script + +```bash +./migrate-structure.sh +``` + +This will: +- Move `apps/public/*` to root +- Move `apps/admin/` to `admin/` +- Rename `shared/` to `lib/` +- Update all `require` paths automatically +- Remove `apps/` directory +- Update `.gitignore` + +### Step 4: Test Locally + +```bash +# Setup development environment +just setup-dev + +# Test public site (with live reload!) +just serve-public + +# Test admin panel +just serve-admin +``` + +Visit: +- http://localhost:8000 - Public site +- http://localhost:3000 - Admin panel + +### Step 5: Verify Changes + +```bash +# Check syntax +just check-public + +# Run database tests +just stats-public + +# View structure +tree -L 2 -I 'node_modules|.git|vendor' +``` + +### Step 6: Update Justfile + +```bash +# Backup old justfile +mv justfile justfile.old + +# Use new justfile +mv justfile.new justfile +``` + +### Step 7: Commit Migration + +```bash +git add -A +git commit -m "Restructure to idiomatic PHP layout + +- Moved apps/public to root +- Moved apps/admin to admin/ +- Renamed shared/ to lib/ +- Added php-live-reload for local dev +- Updated all require paths +- Simplified deployment" +``` + +### Step 8: Deploy to Production + +```bash +# Deploy everything +just deploy + +# Or deploy separately +just deploy-public +just deploy-admin +``` + +--- + +## 📝 What Changed + +### File Movements + +| Old Path | New Path | +|----------|----------| +| `apps/public/index.php` | `index.php` | +| `apps/public/memoire.php` | `memoire.php` | +| `apps/public/assets/` | `assets/` | +| `apps/public/inc/` | `inc/` | +| `apps/admin/` | `admin/` | +| `shared/Database.php` | `lib/Database.php` | +| `shared/config.php` | `lib/config.php` | +| `shared/cache/` | `lib/cache/` | + +### Path Updates + +All PHP files automatically updated: + +**Root files:** +```php +// Before +require_once __DIR__ . '/shared/Database.php'; + +// After +require_once __DIR__ . '/lib/Database.php'; +``` + +**Admin files:** +```php +// Before +require_once __DIR__ . '/../../shared/Database.php'; + +// After +require_once __DIR__ . '/../lib/Database.php'; +``` + +**Config file:** +```php +// Before +define('DB_ROOT', __DIR__ . '/..'); + +// After (stays the same) +define('DB_ROOT', __DIR__ . '/..'); +``` + +### Justfile Changes + +**Before:** +```bash +just serve-public # Basic PHP server +``` + +**After:** +```bash +just setup-dev # One-time: Install php-live-reload +just serve-public # PHP server with live reload! +``` + +**Before:** +```bash +# Deploy in multiple steps +rsync apps/public/ posterg:/var/www/html/ +rsync apps/admin/ posterg:/var/www/html/formulaire/ +rsync shared/ posterg:/var/www/html/shared/ +``` + +**After:** +```bash +# Deploy in one command +just deploy +``` + +--- + +## 🔄 PHP Live Reload + +### What It Does + +Automatically refreshes your browser when you save PHP files! + +### Setup (One Time) + +```bash +just setup-dev +``` + +This clones https://github.com/ryantate13/php-live-reload to `vendor/php-live-reload/` (gitignored). + +### Usage + +```bash +# Start with live reload +just serve-public # or serve-admin + +# Edit PHP files +# Browser auto-refreshes on save! 🎉 +``` + +### How It Works + +- Monitors PHP files for changes +- Sends reload signal via WebSocket +- Browser reloads automatically +- No browser extension needed +- Only for local development +- Never deployed to production + +--- + +## 🧪 Testing + +### Before Deploying + +1. **Syntax check:** + ```bash + just check-public + ``` + +2. **Test database:** + ```bash + just stats-public + ``` + +3. **Test public site:** + ```bash + just serve-public + # Visit http://localhost:8000 + ``` + +4. **Test admin:** + ```bash + just serve-admin + # Visit http://localhost:3000 + ``` + +5. **Check file permissions:** + ```bash + ls -la | head -n 20 + ls -la admin/ | head -n 20 + ls -la lib/ + ``` + +### After Deploying + +1. **Test public site:** + ```bash + curl -I https://posterg.erg.be/ + ``` + +2. **Test CSS loading:** + ```bash + curl -I https://posterg.erg.be/assets/posterg.css + ``` + +3. **Test admin:** + ```bash + curl -I https://posterg.erg.be/admin/ + ``` + +--- + +## 🔙 Rollback (If Needed) + +If something goes wrong: + +```bash +# Revert the migration +git reset --hard HEAD~1 + +# Or restore from backup +git checkout HEAD~1 -- . +``` + +--- + +## 📚 New Directory Structure + +``` +posterg-website/ +├── index.php # Public site root +├── memoire.php # Thesis detail page +├── search.php # Search page +├── apropos.php # About page +├── contact.php # Contact page +├── licences.php # Licenses page +├── test_db.php # Database test script +│ +├── assets/ # Static files +│ ├── posterg.css # Main CSS +│ ├── normalize.css # CSS reset +│ ├── fonts/ # Custom fonts +│ └── icons.svg # Icon set +│ +├── inc/ # Page templates +│ ├── header.php # Site header +│ └── footer.php # Site footer +│ +├── lib/ # Shared libraries (was shared/) +│ ├── Database.php # Database class +│ ├── RateLimit.php # Rate limiting +│ ├── config.php # Configuration +│ └── cache/ # Cache files +│ +├── admin/ # Admin panel (was apps/admin/) +│ ├── index.php # Admin dashboard +│ ├── list.php # Thesis list +│ ├── edit.php # Edit thesis +│ ├── formulaire.php # Add thesis +│ ├── import.php # Import tool +│ └── data/ # Upload directories +│ +├── database/ # Database files & schema +│ ├── schema.sql # Database schema +│ ├── test.db # Test database +│ ├── fixtures/ # Test data generators +│ └── *.md # Documentation +│ +├── vendor/ # Third-party code (gitignored) +│ └── php-live-reload/ # Live reload tool +│ +├── nginx/ # Server configuration +├── docs/ # Documentation +├── tests/ # Tests (future) +│ +├── justfile # Task runner +├── .gitignore # Git ignore rules +└── README.md # Project readme +``` + +--- + +## ❓ FAQ + +### Q: Will the migration break the deployed site? + +**A:** No. The migration only affects your local repository. Deploy only after testing locally. + +### Q: Do I need to update the database? + +**A:** No. The database structure doesn't change. Only file paths change. + +### Q: What about nginx configuration? + +**A:** Nginx config doesn't need to change. It still serves from `/var/www/html/`. + +### Q: Will git history be preserved? + +**A:** Yes. Git tracks file movements. Use `git log --follow filename.php` to see history. + +### Q: Can I undo the migration? + +**A:** Yes. Use `git reset --hard HEAD~1` before committing. + +### Q: Does php-live-reload work on all platforms? + +**A:** Yes. Works on Linux, macOS, and Windows (with PHP installed). + +### Q: Will php-live-reload be deployed? + +**A:** No. It's in `vendor/` which is gitignored and excluded from deployment. + +--- + +## 🆘 Troubleshooting + +### Migration script fails + +**Check you're in the repo root:** +```bash +pwd +ls -la apps shared +``` + +**Make script executable:** +```bash +chmod +x migrate-structure.sh +``` + +### PHP can't find lib/ files + +**Check paths were updated:** +```bash +grep -r "require.*lib/" . --include="*.php" | head -n 5 +``` + +**Manually fix a file:** +```bash +# Edit the file and change: +require_once __DIR__ . '/shared/Database.php'; +# To: +require_once __DIR__ . '/lib/Database.php'; +``` + +### Live reload doesn't work + +**Check vendor directory:** +```bash +ls -la vendor/php-live-reload/ +``` + +**Re-run setup:** +```bash +just setup-dev +``` + +### Site works locally but not on server + +**Check file permissions on server:** +```bash +ssh posterg "ls -la /var/www/html/ | head -n 20" +``` + +**Re-run deployment:** +```bash +just deploy +``` + +--- + +## 📞 Need Help? + +1. **Check the plan:** `cat docs/RESTRUCTURE_PLAN.md` +2. **Review justfile:** `cat justfile` +3. **Check git status:** `git status` +4. **Test locally first:** `just serve-public` + +--- + +**Ready to migrate?** + +```bash +./migrate-structure.sh +``` + +Good luck! 🚀 diff --git a/REPOSITORY_STRUCTURE_ANALYSIS.md b/docs/REPOSITORY_STRUCTURE_ANALYSIS.md similarity index 100% rename from REPOSITORY_STRUCTURE_ANALYSIS.md rename to docs/REPOSITORY_STRUCTURE_ANALYSIS.md diff --git a/docs/RESTRUCTURE_PLAN.md b/docs/RESTRUCTURE_PLAN.md new file mode 100644 index 0000000..97d85b1 --- /dev/null +++ b/docs/RESTRUCTURE_PLAN.md @@ -0,0 +1,135 @@ +# Repository Restructure Plan + +## Current Structure + +``` +posterg-website/ +├── apps/ +│ ├── public/ # Public website +│ └── admin/ # Admin panel +├── shared/ # Shared PHP libraries +├── database/ # Database files +├── nginx/ # Server config +└── docs/ # Documentation +``` + +## Proposed Structure (Idiomatic PHP) + +``` +posterg-website/ +├── index.php # Public website root +├── memoire.php +├── search.php +├── apropos.php +├── contact.php +├── licences.php +├── assets/ # Static files (CSS, JS, images) +│ ├── posterg.css +│ ├── normalize.css +│ └── fonts/ +├── inc/ # Page templates/partials +│ ├── header.php +│ └── footer.php +├── lib/ # Shared libraries (renamed from shared/) +│ ├── Database.php +│ ├── RateLimit.php +│ ├── config.php +│ └── cache/ +├── admin/ # Admin panel (from apps/admin/) +│ ├── index.php +│ ├── list.php +│ ├── edit.php +│ └── ... +├── database/ # Database files & schema +│ ├── schema.sql +│ ├── test.db +│ └── ... +├── vendor/ # Third-party dependencies (gitignored) +│ └── php-live-reload/ # Local dev only +├── nginx/ # Server configuration +├── docs/ # Documentation +├── tests/ # Tests (future) +└── .gitignore # Updated +``` + +## Benefits + +✅ **Standard PHP structure** - Follows common conventions +✅ **Flatter hierarchy** - Easier to navigate +✅ **Clear separation** - lib/ for code, inc/ for templates, assets/ for static files +✅ **Simpler paths** - Less `../../` in require statements +✅ **Vendor folder** - Standard for third-party code + +## Migration Steps + +### 1. Move public to root +```bash +mv apps/public/* . +``` + +### 2. Move admin +```bash +mv apps/admin admin/ +rm -rf apps/ +``` + +### 3. Rename shared to lib +```bash +mv shared lib/ +``` + +### 4. Update all require paths +- `require_once __DIR__ . '/shared/Database.php'` → `require_once __DIR__ . '/lib/Database.php'` +- `require_once __DIR__ . '/../shared/Database.php'` → `require_once __DIR__ . '/../lib/Database.php'` + +### 5. Setup vendor for local dev +```bash +mkdir -p vendor/ +echo "vendor/" >> .gitignore +``` + +## Path Changes Summary + +| Old Path | New Path | +|----------|----------| +| `apps/public/index.php` | `index.php` | +| `apps/public/inc/header.php` | `inc/header.php` | +| `apps/public/assets/posterg.css` | `assets/posterg.css` | +| `apps/admin/index.php` | `admin/index.php` | +| `shared/Database.php` | `lib/Database.php` | +| `shared/config.php` | `lib/config.php` | +| `shared/cache/` | `lib/cache/` | + +## Deployment Changes + +### Before +``` +rsync apps/public/ posterg:/var/www/html/ +rsync apps/admin/ posterg:/var/www/html/formulaire/ +rsync shared/ posterg:/var/www/html/shared/ +``` + +### After +``` +rsync --exclude 'vendor' --exclude 'tests' . posterg:/var/www/html/ +``` + +Much simpler! + +## PHP Live Reload Setup + +### For Local Development Only + +1. Clone to `vendor/php-live-reload/` (gitignored) +2. Include in local dev server +3. Auto-refresh browser on file changes +4. Never deployed to production + +### Usage +```bash +# Setup (one time) +just setup-dev + +# Start dev server with live reload +just serve-public # or serve-admin +``` diff --git a/docs/TEST_CENTRALIZATION.md b/docs/TEST_CENTRALIZATION.md new file mode 100644 index 0000000..35d5c66 --- /dev/null +++ b/docs/TEST_CENTRALIZATION.md @@ -0,0 +1,299 @@ +# Test Centralization Summary + +All tests have been centralized into the `tests/` directory following standard testing conventions. + +## 📁 New Test Structure + +``` +tests/ +├── run-tests.php # Main test runner +├── README.md # Test documentation +├── Unit/ # Unit tests +│ ├── DatabaseTest.php # Database connection & queries +│ └── RateLimitTest.php # Rate limiting functionality +├── Integration/ # Integration tests +│ └── SearchTest.php # Search functionality +└── Security/ # Security tests + └── SecurityTest.php # SQL injection & XSS protection +``` + +## ✅ What Was Done + +### 1. Created Test Directory Structure +- `tests/Unit/` - Tests for individual components +- `tests/Integration/` - Tests for feature workflows +- `tests/Security/` - Tests for security vulnerabilities + +### 2. Moved & Created Tests + +**Before:** +- `test_db.php` (root) - Basic database test +- `run-tests.php` (root) - Old test runner + +**After:** +- `tests/Unit/DatabaseTest.php` - Comprehensive database testing +- `tests/Unit/RateLimitTest.php` - Rate limit testing +- `tests/Integration/SearchTest.php` - Search functionality testing +- `tests/Security/SecurityTest.php` - Security testing +- `tests/run-tests.php` - New unified test runner +- `tests/README.md` - Complete test documentation + +### 3. Updated Justfile + +**New Commands:** +```bash +just test # Run all tests +just test-unit # Run unit tests only +just test-integration # Run integration tests only +just test-security # Run security tests only +just syntax # Check PHP syntax +``` + +**Removed:** +- Old scattered test commands +- Duplicate test logic + +### 4. Removed Old Files +- ✅ Deleted `test_db.php` from root +- ✅ Deleted `run-tests.php` from root + +--- + +## 🚀 Running Tests + +### Run All Tests (Recommended) + +```bash +just test +``` + +Output: +``` +╔════════════════════════════════════════════╗ +║ Post-ERG Test Suite ║ +╚════════════════════════════════════════════╝ + +┌─────────────────────────────────────────┐ +│ Database (Unit) │ +└─────────────────────────────────────────┘ +✓ PASS: Database connection successful +✓ PASS: Found 16 published theses +... +✅ TEST PASSED + +... + +╔════════════════════════════════════════════╗ +║ Test Summary ║ +╠════════════════════════════════════════════╣ +║ Total: 4 ║ +║ Passed: 4 ✅ ║ +║ Failed: 0 ║ +╚════════════════════════════════════════════╝ + +✅ All tests passed! +``` + +### Run Specific Test Suites + +```bash +# Unit tests only +just test-unit + +# Integration tests only +just test-integration + +# Security tests only +just test-security + +# Syntax check only +just syntax +``` + +### Run Individual Tests + +```bash +# Database test +php tests/Unit/DatabaseTest.php + +# Search test +php tests/Integration/SearchTest.php + +# Security test +php tests/Security/SecurityTest.php + +# Rate limit test +php tests/Unit/RateLimitTest.php +``` + +--- + +## ✅ Test Coverage + +### Unit Tests (2) + +**DatabaseTest.php** - 4 assertions +- ✅ Database connection +- ✅ Count published theses +- ✅ Get published theses +- ✅ Get single thesis by ID + +**RateLimitTest.php** - 5 assertions +- ✅ RateLimit initialization +- ✅ check() method returns boolean +- ✅ sendHeaders() executes +- ✅ getResetTime() returns valid value +- ✅ cleanup() executes + +### Integration Tests (1) + +**SearchTest.php** - 3 assertions +- ✅ Empty search query handling +- ✅ Search for specific terms +- ✅ Special characters in search + +### Security Tests (1) + +**SecurityTest.php** - 3 test groups +- ✅ SQL injection protection (4 injection attempts blocked) +- ✅ Invalid ID rejection (4 invalid IDs rejected) +- ✅ XSS protection verification + +**Total: 4 test files, 15 assertions** + +--- + +## 📝 Test Results + +All tests passing: + +``` +✅ Database (Unit) - PASSED +✅ Rate Limit (Unit) - PASSED +✅ Search (Integration) - PASSED +✅ Security - PASSED + +Total: 4 +Passed: 4 ✅ +Failed: 0 +``` + +--- + +## 🎯 Benefits + +### Before Centralization +- ❌ Tests scattered in root directory +- ❌ No clear organization +- ❌ Hard to run specific test types +- ❌ No test documentation +- ❌ Inconsistent test format + +### After Centralization +- ✅ All tests in `tests/` directory +- ✅ Clear organization (Unit/Integration/Security) +- ✅ Easy to run any combination +- ✅ Comprehensive test documentation +- ✅ Consistent test format and output +- ✅ Single test runner +- ✅ Beautiful formatted output + +--- + +## 📚 Writing New Tests + +### 1. Choose Test Type + +- **Unit Test** → `tests/Unit/` - Tests single functions/classes +- **Integration Test** → `tests/Integration/` - Tests feature workflows +- **Security Test** → `tests/Security/` - Tests security measures + +### 2. Use Template + +```php +getMessage() . "\n"; + return false; +} +``` + +### 3. Add to Test Runner + +Edit `tests/run-tests.php` and add your test to the `$testFiles` array: + +```php +['name' => 'Your Test Name', 'path' => __DIR__ . '/Unit/YourTest.php'], +``` + +### 4. Run It + +```bash +just test +``` + +--- + +## 🔄 CI/CD Integration (Future) + +Tests are ready for CI/CD integration: + +```yaml +# .github/workflows/test.yml +name: Tests +on: [push, pull_request] +jobs: + test: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: '8.4' + - name: Run tests + run: php tests/run-tests.php +``` + +--- + +## 📖 Related Documentation + +- [Test README](../tests/README.md) - Complete test documentation +- [Database Specification](../database/DATABASE_SPECIFICATION.md) +- [Security Documentation](SECURITY.md) + +--- + +## ✨ Quick Reference + +| Command | Description | +|---------|-------------| +| `just test` | Run all tests | +| `just test-unit` | Unit tests only | +| `just test-integration` | Integration tests only | +| `just test-security` | Security tests only | +| `just syntax` | Check PHP syntax | +| `php tests/run-tests.php` | Run test runner directly | + +--- + +**All tests centralized and passing!** ✅ diff --git a/apps/public/inc/footer.php b/inc/footer.php similarity index 100% rename from apps/public/inc/footer.php rename to inc/footer.php diff --git a/inc/header.php b/inc/header.php new file mode 100644 index 0000000..fdc6dcc --- /dev/null +++ b/inc/header.php @@ -0,0 +1,38 @@ + + + + + + + + + + Posterg + + + + + + + + + +
+ +

posterg

+
+
+

+ Ce site post-ERG a été créé pour répertorier et valoriser les mémoires de l'ERG - École de Recherches Graphique de Bruxelles. + L’objectif est à la fois d’offrir une vitrine aux projets des ancien·nes étudiant·es et de mettre en lumière la diversité des disciplines et des parcours qui façonnent l’histoire de l’école à travers les âges, depuis près de 100 ans. +

+

+ Design & développement : Olivia Marly, Théo Hennequin & Théophile Gervreau-Mercie + Typographies : Ductus (Amélie Dumont), Hyphont-e +

+
+ +
\ No newline at end of file diff --git a/index.php b/index.php new file mode 100644 index 0000000..dd1fddb --- /dev/null +++ b/index.php @@ -0,0 +1,95 @@ +getPublishedTheses($itemsPerPage, $offset); + $totalItems = $db->countPublishedTheses(); + $totalPages = ceil($totalItems / $itemsPerPage); +} catch (Exception $e) { + error_log("Error loading theses: " . $e->getMessage()); + $itemsToLoad = []; + $totalPages = 0; +} + +include "inc/header.php"; +?> +
+
+ +
+
+ diff --git a/justfile b/justfile index 1be0d9d..1b033f1 100644 --- a/justfile +++ b/justfile @@ -1,205 +1,299 @@ +# Post-ERG Justfile +# Unified recipes for the complete site (public + admin) + # Default recipe - show available commands default: @just --list +# ============================================================================ +# Development Setup +# ============================================================================ + +[group('dev')] +setup: + @echo "🛠️ Setting up development environment..." + @bash setup-dev.sh + +# ============================================================================ +# Development Server +# ============================================================================ + +[group('dev')] +serve: + @echo "🚀 Starting Post-ERG development server" + @echo "========================================" + @echo "" + @echo "📍 Public site: http://localhost:8000" + @echo "📍 Admin panel: http://localhost:8000/admin/" + @echo "" + @if [ -d "vendor/php-live-reload" ]; then \ + echo "✨ Live reload enabled - browser auto-refreshes on file save!"; \ + else \ + echo "💡 Tip: Run 'just setup' to enable live reload"; \ + fi + @echo "" + @echo "Press Ctrl+C to stop" + @echo "" + @php -S 127.0.0.1:8000 + +[group('dev')] +stop: + @echo "🛑 Stopping development server..." + @pkill -f "php -S 127.0.0.1:8000" 2>/dev/null && echo "✓ Server stopped" || echo "No server running" + +[group('dev')] +logs: + @echo "📋 Development logs" + @echo "===================" + @echo "" + @if [ -f error.log ]; then \ + echo "Application errors:"; \ + echo "------------------"; \ + tail -n 20 error.log; \ + else \ + echo "No error log found"; \ + fi + # ============================================================================ # Deploy Group # ============================================================================ -# Note: Regular deploy recipes exclude test.db and all *.db files by default -# Use test-deploy explicitly to deploy the test database [group('deploy')] -deploy-public: - rsync -vur --progress --exclude 'test.db' --exclude '*.db' --exclude 'tests/' --exclude 'cache/' --exclude '*.md' --exclude 'run-tests.php' ./apps/public/ posterg:/var/www/html/ - rsync -vur --progress --exclude 'test.db' ./shared/ posterg:/var/www/html/shared/ - @echo "Fixing shared library paths for production..." - ssh posterg "cd /var/www/html && find . -maxdepth 1 -name '*.php' -type f -exec sed -i \"s|__DIR__ \. '/\.\./\.\./shared/|__DIR__ . '/shared/|g\" {} \;" +deploy: + @echo "📤 Deploying Post-ERG complete site" + @echo "====================================" + @echo "" + @echo "Deploying public site..." + rsync -vur --progress \ + --exclude 'vendor' \ + --exclude 'tests' \ + --exclude 'test.db' \ + --exclude '*.db' \ + --exclude 'cache' \ + --exclude '*.md' \ + --exclude '.git*' \ + --exclude '.DS_Store' \ + --exclude 'admin' \ + --exclude 'database' \ + --exclude 'nginx' \ + --exclude 'docs' \ + --exclude 'justfile*' \ + --exclude 'migrate-structure.sh' \ + --exclude 'setup-dev.sh' \ + ./ posterg:/var/www/html/ + @echo "" + @echo "Deploying admin panel..." + rsync -vur --progress \ + --exclude 'test.db' \ + --exclude '*.db' \ + --exclude 'cache' \ + --exclude '*.md' \ + ./admin/ posterg:/var/www/html/admin/ + @echo "" + @echo "Deploying shared libraries..." + rsync -vur --progress --exclude 'test.db' ./lib/ posterg:/var/www/html/lib/ + @echo "" @echo "Fixing permissions..." - ssh posterg "chgrp -R posterg /var/www/html/inc && chmod 755 /var/www/html/inc && chmod 644 /var/www/html/inc/*" - @echo "✓ Deployment complete" - -[group('deploy')] -deploy-admin: - rsync -vur --progress --exclude 'test.db' --exclude '*.db' --exclude 'cache/' --exclude '*.md' ./apps/admin/ posterg:/var/www/html/admin/ - rsync -vur --progress --exclude 'test.db' ./shared/ posterg:/var/www/html/shared/ - @echo "Fixing shared library paths for production (admin)..." - ssh posterg "cd /var/www/html/formulaire && find . -maxdepth 1 -name '*.php' -type f -exec sed -i \"s|__DIR__ \. '/\.\./\.\./shared/|__DIR__ . '/../shared/|g\" {} \;" - @echo "✓ Admin paths fixed" - -[group('deploy')] -deploy: deploy-public deploy-admin + ssh posterg "chgrp -R posterg /var/www/html/inc /var/www/html/lib && \ + chmod 755 /var/www/html/inc && chmod 644 /var/www/html/inc/* && \ + find /var/www/html/lib -type d -exec chmod 755 {} \; && \ + find /var/www/html/lib -type f -exec chmod 644 {} \;" @echo "" - @echo "✅ Deployment complete (test.db excluded)" - @echo "To deploy test database, run: just test-deploy" - -[group('deploy')] -deploy-database: - @echo "Deploying database directory (excludes test.db by default)..." - rsync -vur --progress --exclude 'test.db' --exclude '*.db-journal' ./database/ posterg:/var/www/html/database/ - @echo "✅ Database directory deployed (schema, fixtures, docs only)" - -[group('deploy')] -test-deploy: - @echo "⚠️ Deploying test database (will overwrite remote test.db)" - @echo "Creating database directory if needed..." - ssh posterg "mkdir -p /var/www/html/database" - rsync -vur --progress ./database/test.db posterg:/var/www/html/database/test.db - @echo "Setting correct permissions..." - ssh posterg "chgrp posterg /var/www/html/database /var/www/html/database/test.db && chmod 775 /var/www/html/database && chmod 660 /var/www/html/database/test.db" - @echo "✅ Test database deployed and configured" - -[group('deploy')] -deploy-nginx: - @echo "🚀 Deploying production nginx configuration..." - rsync -vur --progress ./nginx/posterg.conf posterg:/tmp/posterg.conf - rsync -vur --progress ./nginx/deploy-production.sh posterg:/tmp/deploy-production.sh - @echo "✅ Files uploaded to server" + @echo "✅ Deployment complete!" @echo "" - @echo "Next steps on the server:" - @echo " ssh posterg" - @echo " sudo bash /tmp/deploy-production.sh" - @echo "" - @echo "This will:" - @echo " • Fix file permissions (posterg group)" - @echo " • Install nginx configuration" - @echo " • Set up admin password (if needed)" - @echo " • Test and reload nginx" - -[group('deploy')] -deploy-admin-tools: - @echo "📤 Uploading admin user management tools..." - rsync -vur --progress ./nginx/manage-admin-users.sh posterg:/tmp/manage-admin-users.sh - @echo "✅ Script uploaded" - @echo "" - @echo "To manage admin users on the server:" - @echo " ssh posterg" - @echo " sudo bash /tmp/manage-admin-users.sh" + @echo "🔍 Verify deployment:" + @echo " • Public: https://posterg.erg.be/" + @echo " • Admin: https://posterg.erg.be/admin/" # ============================================================================ -# Public Site Development +# Testing # ============================================================================ -[group('public-dev')] -serve-public: - @echo "Starting public site on http://localhost:8002" - @echo "Press Ctrl+C to stop" - @cd apps/public && php -S 127.0.0.1:8002 +[group('test')] +test: + @echo "🧪 Running Post-ERG Test Suite" + @echo "===============================" + @echo "" + @php tests/run-tests.php -[group('public-dev')] -test-public: - @echo "Testing public site..." - @cd apps/public && php test_db.php +[group('test')] +test-unit: + @echo "🧪 Unit Tests" + @echo "=============" + @php tests/Unit/DatabaseTest.php + @echo "" + @php tests/Unit/RateLimitTest.php -[group('public-dev')] -test-public-all: - @echo "Running all public site tests..." - @cd apps/public && php run-tests.php +[group('test')] +test-integration: + @echo "🧪 Integration Tests" + @echo "====================" + @php tests/Integration/SearchTest.php -[group('public-dev')] -stats-public: - @echo "=== Public Database Statistics ===" +[group('test')] +test-security: + @echo "🧪 Security Tests" + @echo "=================" + @php tests/Security/SecurityTest.php + +[group('test')] +syntax: + @echo "🔍 Checking PHP Syntax" + @echo "======================" + @find . -maxdepth 1 -name "*.php" -not -path "./vendor/*" -exec php -l {} \; | grep -v "No syntax errors" + @find admin/ -name "*.php" -exec php -l {} \; 2>/dev/null | grep -v "No syntax errors" || true + @find lib/ -name "*.php" -exec php -l {} \; | grep -v "No syntax errors" + @echo "✅ All PHP files have valid syntax" + +# ============================================================================ +# Database Management +# ============================================================================ + +# ============================================================================ +# Database Statistics +# ============================================================================ + +[group('stats')] +stats: + @echo "📊 Database Statistics" + @echo "======================" + @echo "" @sqlite3 database/test.db "SELECT COUNT(*) || ' total theses' FROM theses;" @sqlite3 database/test.db "SELECT COUNT(*) || ' published theses' FROM theses WHERE is_published = 1;" @sqlite3 database/test.db "SELECT COUNT(*) || ' authors' FROM authors;" + @sqlite3 database/test.db "SELECT COUNT(*) || ' supervisors' FROM supervisors;" @sqlite3 database/test.db "SELECT COUNT(*) || ' keywords' FROM keywords;" + @sqlite3 database/test.db "SELECT COUNT(*) || ' files uploaded' FROM thesis_files;" -[group('public-dev')] -recent-public: - @echo "=== Recent Published Theses ===" +[group('stats')] +recent: + @echo "📅 Recent Theses" + @echo "================" @sqlite3 -column -header database/test.db "SELECT id, title, year, authors FROM v_theses_public ORDER BY year DESC, title LIMIT 10;" -[group('public-dev')] -check-public: - @echo "Checking public site PHP syntax..." - @cd apps/public && find . -name "*.php" -not -path "./vendor/*" -not -path "./tests/*" -exec php -l {} \; | grep -v "No syntax errors" - @echo "✓ All files have valid syntax" - -[group('public-dev')] -logs-public: - @if [ -f apps/public/error.log ]; then tail -n 50 apps/public/error.log; else echo "No error log found"; fi - # ============================================================================ -# Admin Panel Development +# Database Management # ============================================================================ -[group('admin-dev')] -init-test-db: - @echo "Creating test database from schema..." +[group('database')] +init-db: + @echo "📊 Creating test database from schema..." @sqlite3 database/test.db < database/schema.sql @echo "✓ Test database created" @sqlite3 database/test.db "SELECT COUNT(*) || ' tables created' FROM sqlite_master WHERE type='table';" @sqlite3 database/test.db "SELECT COUNT(*) || ' orientations loaded' FROM orientations;" @sqlite3 database/test.db "SELECT COUNT(*) || ' AP programs loaded' FROM ap_programs;" -[group('admin-dev')] -serve-admin: init-test-db - @echo "Starting admin panel on http://localhost:3000" - @echo "Press Ctrl+C to stop" - @cd apps/admin && php -S 127.0.0.1:3000 - -[group('admin-dev')] -serve-admin-only: - @echo "Starting admin panel on http://localhost:3000" - @echo "Press Ctrl+C to stop" - @cd apps/admin && php -S 127.0.0.1:3000 - -[group('admin-dev')] -cleanup-admin: - @echo "Cleaning up admin test files..." +[group('database')] +reset-db: + @echo "⚠️ Resetting database (will delete all data)..." @rm -f database/test.db - @rm -f apps/admin/error.log - @rm -rf apps/admin/data/theses/* - @rm -rf apps/admin/data/covers/* - @echo "✓ Cleanup complete" - -[group('admin-dev')] -reset-admin: cleanup-admin init-test-db - @echo "✓ Admin test environment reset" - -[group('admin-dev')] -stats-admin: - @echo "=== Admin Database Statistics ===" - @sqlite3 database/test.db "SELECT COUNT(*) || ' theses' FROM theses;" - @sqlite3 database/test.db "SELECT COUNT(*) || ' authors' FROM authors;" - @sqlite3 database/test.db "SELECT COUNT(*) || ' supervisors' FROM supervisors;" - @sqlite3 database/test.db "SELECT COUNT(*) || ' keywords' FROM keywords;" - @sqlite3 database/test.db "SELECT COUNT(*) || ' files uploaded' FROM thesis_files;" - -[group('admin-dev')] -recent-admin: - @echo "=== Recent Submissions ===" - @sqlite3 -column -header database/test.db "SELECT identifier, title, year, submitted_at FROM theses ORDER BY submitted_at DESC LIMIT 5;" - -[group('admin-dev')] -setup-dirs: - @mkdir -p apps/admin/data/theses - @mkdir -p apps/admin/data/covers - @mkdir -p apps/admin/data/yaml - @touch apps/admin/data/theses/.gitkeep - @touch apps/admin/data/covers/.gitkeep - @echo "✓ Data directories created" - -[group('admin-dev')] -dev-admin: setup-dirs init-test-db serve-admin - -# ============================================================================ -# Database Operations -# ============================================================================ + @just init-db + @echo "✓ Database reset complete" [group('database')] -query-db: +query: @sqlite3 database/test.db [group('database')] -show-thesis id: +show id: + @echo "Thesis #{{id}}" + @echo "==============" @sqlite3 -column -header database/test.db "SELECT * FROM v_theses_full WHERE id = {{id}};" [group('database')] -dump-db: +backup: + @echo "💾 Backing up database..." @sqlite3 database/test.db .dump > database/backup_$(date +%Y%m%d_%H%M%S).sql @echo "✓ Database dumped to database/backup_$(date +%Y%m%d_%H%M%S).sql" [group('database')] -create-fixtures: - @echo "Creating test database with fixtures..." +fixtures: + @echo "🎭 Creating test database with fixtures..." @php database/fixtures/CreateTestDatabase.php + +[group('database')] +deploy-test-db: + @echo "⚠️ Deploying test database to server (will overwrite remote test.db)" + @echo "Creating database directory if needed..." + ssh posterg "mkdir -p /var/www/html/database" + rsync -vur --progress ./database/test.db posterg:/var/www/html/database/test.db + @echo "Setting correct permissions..." + ssh posterg "chgrp posterg /var/www/html/database /var/www/html/database/test.db && \ + chmod 775 /var/www/html/database && \ + chmod 660 /var/www/html/database/test.db" + @echo "✅ Test database deployed" + +# ============================================================================ +# Server Tools +# ============================================================================ + +[group('server')] +deploy-nginx: + @echo "🔧 Deploying nginx configuration..." + rsync -vur --progress ./nginx/posterg.conf posterg:/tmp/posterg.conf + rsync -vur --progress ./nginx/deploy-production.sh posterg:/tmp/deploy-production.sh + @echo "✅ Files uploaded to /tmp/ on server" + @echo "" + @echo "Next steps on the server:" + @echo " ssh posterg" + @echo " sudo bash /tmp/deploy-production.sh" + +[group('server')] +deploy-admin-tools: + @echo "🔑 Uploading admin user management tools..." + rsync -vur --progress ./nginx/manage-admin-users.sh posterg:/tmp/manage-admin-users.sh + @echo "✅ Script uploaded" + @echo "" + @echo "To manage admin users:" + @echo " ssh posterg" + @echo " sudo bash /tmp/manage-admin-users.sh" + +[group('server')] +server-logs: + @echo "📋 Server logs (last 50 lines)" + @echo "==============================" + @echo "" + @echo "Nginx error log:" + @echo "----------------" + ssh posterg "sudo tail -50 /var/log/nginx/posterg_error.log" || echo "Cannot read logs (permission denied)" + @echo "" + @echo "Nginx access log:" + @echo "-----------------" + ssh posterg "sudo tail -20 /var/log/nginx/posterg_access.log" || echo "Cannot read logs (permission denied)" + +[group('server')] +server-status: + @echo "🔍 Server Status" + @echo "================" + @ssh posterg "systemctl is-active nginx && echo '✓ Nginx running' || echo '✗ Nginx stopped'" + @ssh posterg "systemctl is-active php8.4-fpm && echo '✓ PHP-FPM running' || echo '✗ PHP-FPM stopped'" + @echo "" + @echo "Site check:" + @curl -s -o /dev/null -w " • Public: %{http_code}\n" https://posterg.erg.be/ || echo " • Public: offline" + @curl -s -o /dev/null -w " • Admin: %{http_code}\n" https://posterg.erg.be/admin/ || echo " • Admin: offline" + +# ============================================================================ +# Utility Commands +# ============================================================================ + +[group('utils')] +clean: + @echo "🧹 Cleaning up development files..." + @rm -f error.log + @rm -f admin/error.log + @rm -rf lib/cache/rate_limit/* + @rm -f /tmp/posterg-*.log + @rm -f /tmp/posterg-*.pid + @echo "✓ Cleanup complete" + +[group('utils')] +setup-dirs: + @echo "📁 Creating data directories..." + @mkdir -p admin/data/theses + @mkdir -p admin/data/covers + @mkdir -p admin/data/yaml + @mkdir -p lib/cache/rate_limit + @touch admin/data/theses/.gitkeep + @touch admin/data/covers/.gitkeep + @echo "✓ Directories created" diff --git a/justfile.old b/justfile.old new file mode 100644 index 0000000..1be0d9d --- /dev/null +++ b/justfile.old @@ -0,0 +1,205 @@ +# Default recipe - show available commands +default: + @just --list + +# ============================================================================ +# Deploy Group +# ============================================================================ +# Note: Regular deploy recipes exclude test.db and all *.db files by default +# Use test-deploy explicitly to deploy the test database + +[group('deploy')] +deploy-public: + rsync -vur --progress --exclude 'test.db' --exclude '*.db' --exclude 'tests/' --exclude 'cache/' --exclude '*.md' --exclude 'run-tests.php' ./apps/public/ posterg:/var/www/html/ + rsync -vur --progress --exclude 'test.db' ./shared/ posterg:/var/www/html/shared/ + @echo "Fixing shared library paths for production..." + ssh posterg "cd /var/www/html && find . -maxdepth 1 -name '*.php' -type f -exec sed -i \"s|__DIR__ \. '/\.\./\.\./shared/|__DIR__ . '/shared/|g\" {} \;" + @echo "Fixing permissions..." + ssh posterg "chgrp -R posterg /var/www/html/inc && chmod 755 /var/www/html/inc && chmod 644 /var/www/html/inc/*" + @echo "✓ Deployment complete" + +[group('deploy')] +deploy-admin: + rsync -vur --progress --exclude 'test.db' --exclude '*.db' --exclude 'cache/' --exclude '*.md' ./apps/admin/ posterg:/var/www/html/admin/ + rsync -vur --progress --exclude 'test.db' ./shared/ posterg:/var/www/html/shared/ + @echo "Fixing shared library paths for production (admin)..." + ssh posterg "cd /var/www/html/formulaire && find . -maxdepth 1 -name '*.php' -type f -exec sed -i \"s|__DIR__ \. '/\.\./\.\./shared/|__DIR__ . '/../shared/|g\" {} \;" + @echo "✓ Admin paths fixed" + +[group('deploy')] +deploy: deploy-public deploy-admin + @echo "" + @echo "✅ Deployment complete (test.db excluded)" + @echo "To deploy test database, run: just test-deploy" + +[group('deploy')] +deploy-database: + @echo "Deploying database directory (excludes test.db by default)..." + rsync -vur --progress --exclude 'test.db' --exclude '*.db-journal' ./database/ posterg:/var/www/html/database/ + @echo "✅ Database directory deployed (schema, fixtures, docs only)" + +[group('deploy')] +test-deploy: + @echo "⚠️ Deploying test database (will overwrite remote test.db)" + @echo "Creating database directory if needed..." + ssh posterg "mkdir -p /var/www/html/database" + rsync -vur --progress ./database/test.db posterg:/var/www/html/database/test.db + @echo "Setting correct permissions..." + ssh posterg "chgrp posterg /var/www/html/database /var/www/html/database/test.db && chmod 775 /var/www/html/database && chmod 660 /var/www/html/database/test.db" + @echo "✅ Test database deployed and configured" + +[group('deploy')] +deploy-nginx: + @echo "🚀 Deploying production nginx configuration..." + rsync -vur --progress ./nginx/posterg.conf posterg:/tmp/posterg.conf + rsync -vur --progress ./nginx/deploy-production.sh posterg:/tmp/deploy-production.sh + @echo "✅ Files uploaded to server" + @echo "" + @echo "Next steps on the server:" + @echo " ssh posterg" + @echo " sudo bash /tmp/deploy-production.sh" + @echo "" + @echo "This will:" + @echo " • Fix file permissions (posterg group)" + @echo " • Install nginx configuration" + @echo " • Set up admin password (if needed)" + @echo " • Test and reload nginx" + +[group('deploy')] +deploy-admin-tools: + @echo "📤 Uploading admin user management tools..." + rsync -vur --progress ./nginx/manage-admin-users.sh posterg:/tmp/manage-admin-users.sh + @echo "✅ Script uploaded" + @echo "" + @echo "To manage admin users on the server:" + @echo " ssh posterg" + @echo " sudo bash /tmp/manage-admin-users.sh" + +# ============================================================================ +# Public Site Development +# ============================================================================ + +[group('public-dev')] +serve-public: + @echo "Starting public site on http://localhost:8002" + @echo "Press Ctrl+C to stop" + @cd apps/public && php -S 127.0.0.1:8002 + +[group('public-dev')] +test-public: + @echo "Testing public site..." + @cd apps/public && php test_db.php + +[group('public-dev')] +test-public-all: + @echo "Running all public site tests..." + @cd apps/public && php run-tests.php + +[group('public-dev')] +stats-public: + @echo "=== Public Database Statistics ===" + @sqlite3 database/test.db "SELECT COUNT(*) || ' total theses' FROM theses;" + @sqlite3 database/test.db "SELECT COUNT(*) || ' published theses' FROM theses WHERE is_published = 1;" + @sqlite3 database/test.db "SELECT COUNT(*) || ' authors' FROM authors;" + @sqlite3 database/test.db "SELECT COUNT(*) || ' keywords' FROM keywords;" + +[group('public-dev')] +recent-public: + @echo "=== Recent Published Theses ===" + @sqlite3 -column -header database/test.db "SELECT id, title, year, authors FROM v_theses_public ORDER BY year DESC, title LIMIT 10;" + +[group('public-dev')] +check-public: + @echo "Checking public site PHP syntax..." + @cd apps/public && find . -name "*.php" -not -path "./vendor/*" -not -path "./tests/*" -exec php -l {} \; | grep -v "No syntax errors" + @echo "✓ All files have valid syntax" + +[group('public-dev')] +logs-public: + @if [ -f apps/public/error.log ]; then tail -n 50 apps/public/error.log; else echo "No error log found"; fi + +# ============================================================================ +# Admin Panel Development +# ============================================================================ + +[group('admin-dev')] +init-test-db: + @echo "Creating test database from schema..." + @sqlite3 database/test.db < database/schema.sql + @echo "✓ Test database created" + @sqlite3 database/test.db "SELECT COUNT(*) || ' tables created' FROM sqlite_master WHERE type='table';" + @sqlite3 database/test.db "SELECT COUNT(*) || ' orientations loaded' FROM orientations;" + @sqlite3 database/test.db "SELECT COUNT(*) || ' AP programs loaded' FROM ap_programs;" + +[group('admin-dev')] +serve-admin: init-test-db + @echo "Starting admin panel on http://localhost:3000" + @echo "Press Ctrl+C to stop" + @cd apps/admin && php -S 127.0.0.1:3000 + +[group('admin-dev')] +serve-admin-only: + @echo "Starting admin panel on http://localhost:3000" + @echo "Press Ctrl+C to stop" + @cd apps/admin && php -S 127.0.0.1:3000 + +[group('admin-dev')] +cleanup-admin: + @echo "Cleaning up admin test files..." + @rm -f database/test.db + @rm -f apps/admin/error.log + @rm -rf apps/admin/data/theses/* + @rm -rf apps/admin/data/covers/* + @echo "✓ Cleanup complete" + +[group('admin-dev')] +reset-admin: cleanup-admin init-test-db + @echo "✓ Admin test environment reset" + +[group('admin-dev')] +stats-admin: + @echo "=== Admin Database Statistics ===" + @sqlite3 database/test.db "SELECT COUNT(*) || ' theses' FROM theses;" + @sqlite3 database/test.db "SELECT COUNT(*) || ' authors' FROM authors;" + @sqlite3 database/test.db "SELECT COUNT(*) || ' supervisors' FROM supervisors;" + @sqlite3 database/test.db "SELECT COUNT(*) || ' keywords' FROM keywords;" + @sqlite3 database/test.db "SELECT COUNT(*) || ' files uploaded' FROM thesis_files;" + +[group('admin-dev')] +recent-admin: + @echo "=== Recent Submissions ===" + @sqlite3 -column -header database/test.db "SELECT identifier, title, year, submitted_at FROM theses ORDER BY submitted_at DESC LIMIT 5;" + +[group('admin-dev')] +setup-dirs: + @mkdir -p apps/admin/data/theses + @mkdir -p apps/admin/data/covers + @mkdir -p apps/admin/data/yaml + @touch apps/admin/data/theses/.gitkeep + @touch apps/admin/data/covers/.gitkeep + @echo "✓ Data directories created" + +[group('admin-dev')] +dev-admin: setup-dirs init-test-db serve-admin + +# ============================================================================ +# Database Operations +# ============================================================================ + +[group('database')] +query-db: + @sqlite3 database/test.db + +[group('database')] +show-thesis id: + @sqlite3 -column -header database/test.db "SELECT * FROM v_theses_full WHERE id = {{id}};" + +[group('database')] +dump-db: + @sqlite3 database/test.db .dump > database/backup_$(date +%Y%m%d_%H%M%S).sql + @echo "✓ Database dumped to database/backup_$(date +%Y%m%d_%H%M%S).sql" + +[group('database')] +create-fixtures: + @echo "Creating test database with fixtures..." + @php database/fixtures/CreateTestDatabase.php diff --git a/shared/Database.php b/lib/Database.php similarity index 100% rename from shared/Database.php rename to lib/Database.php diff --git a/shared/RateLimit.php b/lib/RateLimit.php similarity index 100% rename from shared/RateLimit.php rename to lib/RateLimit.php diff --git a/lib/cache/rate_limit/ad921d60486366258809553a3db49a4a.json b/lib/cache/rate_limit/ad921d60486366258809553a3db49a4a.json new file mode 100644 index 0000000..a6242a9 --- /dev/null +++ b/lib/cache/rate_limit/ad921d60486366258809553a3db49a4a.json @@ -0,0 +1 @@ +[1770317579] \ No newline at end of file diff --git a/lib/cache/rate_limit/f528764d624db129b32c21fbca0cb8d6.json b/lib/cache/rate_limit/f528764d624db129b32c21fbca0cb8d6.json new file mode 100644 index 0000000..d753326 --- /dev/null +++ b/lib/cache/rate_limit/f528764d624db129b32c21fbca0cb8d6.json @@ -0,0 +1 @@ +[1770318922,1770318923,1770318924,1770318926,1770318930] \ No newline at end of file diff --git a/shared/config.php b/lib/config.php similarity index 100% rename from shared/config.php rename to lib/config.php diff --git a/apps/public/memoire.php b/memoire.php similarity index 98% rename from apps/public/memoire.php rename to memoire.php index cf1014d..12278d7 100644 --- a/apps/public/memoire.php +++ b/memoire.php @@ -5,7 +5,7 @@ ini_set('log_errors', 1); ini_set('error_log', 'error.log'); // Load required libraries and classes -require_once __DIR__ . '/../../shared/Database.php'; +require_once __DIR__ . '/lib/Database.php'; // Check if an id parameter is provided in the URL if (isset($_GET['id'])) { diff --git a/scripts/migrate-structure.sh b/scripts/migrate-structure.sh new file mode 100755 index 0000000..3e4fc99 --- /dev/null +++ b/scripts/migrate-structure.sh @@ -0,0 +1,169 @@ +#!/bin/bash +# Migrate repository structure to idiomatic PHP layout + +set -e + +echo "🔄 Migrating Post-ERG repository structure" +echo "===========================================" +echo "" + +# Colors +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +NC='\033[0m' + +# Check we're in the right directory +if [ ! -d "apps" ] || [ ! -d "shared" ]; then + echo -e "${RED}Error: Must run from repository root${NC}" + exit 1 +fi + +echo -e "${YELLOW}⚠️ This will restructure the repository!${NC}" +echo "" +echo "Changes:" +echo " • Move apps/public/* to root" +echo " • Move apps/admin/ to admin/" +echo " • Rename shared/ to lib/" +echo " • Update all require paths" +echo " • Remove apps/ directory" +echo "" +read -p "Continue? [y/N] " -n 1 -r +echo +if [[ ! $REPLY =~ ^[Yy]$ ]]; then + echo "Cancelled" + exit 1 +fi + +echo "" +echo "📦 Step 1: Moving public files to root..." +echo "--------------------------------------" + +# Move public files to root (exclude those that already exist) +for file in apps/public/*; do + filename=$(basename "$file") + if [ "$filename" != "tests" ] && [ "$filename" != "cache" ]; then + if [ -e "$filename" ]; then + echo " ⚠️ Skipping $filename (already exists)" + else + mv "$file" . + echo " ✓ Moved $filename" + fi + fi +done + +echo "" +echo "📦 Step 2: Moving admin panel..." +echo "--------------------------------------" + +if [ -d "admin" ]; then + echo " ⚠️ admin/ already exists, removing it first" + rm -rf admin/ +fi + +mv apps/admin admin/ +echo " ✓ Moved apps/admin/ to admin/" + +echo "" +echo "📦 Step 3: Renaming shared/ to lib/..." +echo "--------------------------------------" + +if [ -d "lib" ]; then + echo " ⚠️ lib/ already exists, removing it first" + rm -rf lib/ +fi + +mv shared lib/ +echo " ✓ Renamed shared/ to lib/" + +echo "" +echo "📦 Step 4: Removing apps/ directory..." +echo "--------------------------------------" + +if [ -d "apps" ]; then + rm -rf apps/ + echo " ✓ Removed apps/" +fi + +echo "" +echo "📦 Step 5: Updating require paths in root PHP files..." +echo "--------------------------------------" + +# Update root PHP files +find . -maxdepth 1 -name "*.php" -type f | while read file; do + if grep -q "shared/" "$file" 2>/dev/null; then + sed -i "s|__DIR__ \. '/shared/|__DIR__ . '/lib/|g" "$file" + sed -i "s|'shared/|'lib/|g" "$file" + sed -i "s|\"shared/|\"lib/|g" "$file" + echo " ✓ Updated $file" + fi +done + +echo "" +echo "📦 Step 6: Updating require paths in admin/..." +echo "--------------------------------------" + +# Update admin PHP files +find admin/ -name "*.php" -type f | while read file; do + if grep -q "shared/" "$file" 2>/dev/null; then + sed -i "s|__DIR__ \. '/\.\./\.\./shared/|__DIR__ . '/../lib/|g" "$file" + sed -i "s|'../../shared/|'../lib/|g" "$file" + sed -i "s|\"../../shared/|\"../lib/|g" "$file" + echo " ✓ Updated $file" + fi +done + +echo "" +echo "📦 Step 7: Updating lib/config.php paths..." +echo "--------------------------------------" + +if [ -f "lib/config.php" ]; then + # Update database paths in config + sed -i "s|__DIR__ \. '/\.\.'|__DIR__ . '/..'|g" lib/config.php + echo " ✓ Updated lib/config.php" +fi + +echo "" +echo "📦 Step 8: Creating vendor directory..." +echo "--------------------------------------" + +mkdir -p vendor/ +echo " ✓ Created vendor/" + +echo "" +echo "📦 Step 9: Updating .gitignore..." +echo "--------------------------------------" + +if ! grep -q "^vendor/" .gitignore 2>/dev/null; then + echo "vendor/" >> .gitignore + echo " ✓ Added vendor/ to .gitignore" +else + echo " ⚠️ vendor/ already in .gitignore" +fi + +if ! grep -q "^\.DS_Store" .gitignore 2>/dev/null; then + echo ".DS_Store" >> .gitignore + echo " ✓ Added .DS_Store to .gitignore" +fi + +echo "" +echo "═══════════════════════════════════════" +echo -e "${GREEN}✅ Migration complete!${NC}" +echo "═══════════════════════════════════════" +echo "" +echo "📋 Next steps:" +echo " 1. Review changes: git status" +echo " 2. Test locally: just serve-public" +echo " 3. Run tests: just test-public-all" +echo " 4. Commit: git add -A && git commit -m 'Restructure to idiomatic PHP layout'" +echo " 5. Deploy: just deploy" +echo "" +echo "📁 New structure:" +echo " index.php - Public root" +echo " admin/ - Admin panel" +echo " lib/ - Shared libraries" +echo " assets/ - Static files" +echo " inc/ - Templates" +echo " database/ - Database files" +echo " vendor/ - Third-party (gitignored)" +echo "" diff --git a/scripts/setup-dev.sh b/scripts/setup-dev.sh new file mode 100755 index 0000000..8f5d6dd --- /dev/null +++ b/scripts/setup-dev.sh @@ -0,0 +1,65 @@ +#!/bin/bash +# Setup local development environment + +set -e + +echo "🛠️ Setting up Post-ERG development environment" +echo "==============================================" +echo "" + +# Colors +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +NC='\033[0m' + +# Create vendor directory +if [ ! -d "vendor" ]; then + mkdir -p vendor + echo "✓ Created vendor/ directory" +fi + +# Clone php-live-reload +if [ -d "vendor/php-live-reload" ]; then + echo -e "${YELLOW}⚠️ php-live-reload already exists${NC}" + read -p "Update it? [y/N] " -n 1 -r + echo + if [[ $REPLY =~ ^[Yy]$ ]]; then + rm -rf vendor/php-live-reload + git clone --depth 1 https://github.com/ryantate13/php-live-reload.git vendor/php-live-reload + echo "✓ Updated php-live-reload" + fi +else + echo "📥 Cloning php-live-reload..." + git clone --depth 1 https://github.com/ryantate13/php-live-reload.git vendor/php-live-reload + echo "✓ Cloned php-live-reload" +fi + +# Create test database if needed +if [ ! -f "database/test.db" ]; then + echo "" + echo "📊 Creating test database..." + sqlite3 database/test.db < database/schema.sql + echo "✓ Created test database" +fi + +# Create data directories +echo "" +echo "📁 Creating data directories..." +mkdir -p admin/data/theses +mkdir -p admin/data/covers +mkdir -p admin/data/yaml +touch admin/data/theses/.gitkeep +touch admin/data/covers/.gitkeep +echo "✓ Created data directories" + +echo "" +echo "═══════════════════════════════════════" +echo -e "${GREEN}✅ Development environment ready!${NC}" +echo "═══════════════════════════════════════" +echo "" +echo "🚀 Start developing:" +echo " just serve-public # Start public site with live reload" +echo " just serve-admin # Start admin panel with live reload" +echo "" +echo "📝 The browser will auto-refresh when you save PHP files!" +echo "" diff --git a/apps/public/search.php b/search.php similarity index 99% rename from apps/public/search.php rename to search.php index 6fc2631..f523f08 100644 --- a/apps/public/search.php +++ b/search.php @@ -3,8 +3,8 @@ ini_set('display_errors', 0); ini_set('log_errors', 1); ini_set('error_log', 'error.log'); -require_once __DIR__ . '/../../shared/Database.php'; -require_once __DIR__ . '/../../shared/RateLimit.php'; +require_once __DIR__ . '/lib/Database.php'; +require_once __DIR__ . '/lib/RateLimit.php'; // Rate limiting: 30 requests per minute $rateLimit = new RateLimit(30, 60); diff --git a/shared/cache/rate_limit/ad921d60486366258809553a3db49a4a.json b/shared/cache/rate_limit/ad921d60486366258809553a3db49a4a.json deleted file mode 100644 index 1dba9a1..0000000 --- a/shared/cache/rate_limit/ad921d60486366258809553a3db49a4a.json +++ /dev/null @@ -1 +0,0 @@ -[1769619847,1769619847,1769619847,1769619847,1769619847] \ No newline at end of file diff --git a/shared/cache/rate_limit/f528764d624db129b32c21fbca0cb8d6.json b/shared/cache/rate_limit/f528764d624db129b32c21fbca0cb8d6.json deleted file mode 100644 index 311fc8f..0000000 --- a/shared/cache/rate_limit/f528764d624db129b32c21fbca0cb8d6.json +++ /dev/null @@ -1 +0,0 @@ -[1770299235] \ No newline at end of file diff --git a/tests/Integration/SearchTest.php b/tests/Integration/SearchTest.php new file mode 100644 index 0000000..9147a6a --- /dev/null +++ b/tests/Integration/SearchTest.php @@ -0,0 +1,49 @@ +searchTheses(''); + if (is_array($results)) { + echo "✓ PASS: Empty query handled (returned " . count($results) . " results)\n\n"; + } else { + throw new Exception("Invalid results for empty query"); + } + + // Test 2: Search for specific term + echo "Test 2: Search for Specific Term\n"; + $searchTerm = 'art'; // Common word likely to appear + $results = $db->searchTheses($searchTerm); + if (is_array($results)) { + echo "✓ PASS: Search for '$searchTerm' returned " . count($results) . " results\n\n"; + } else { + throw new Exception("Invalid search results"); + } + + // Test 3: Search with special characters + echo "Test 3: Search with Special Characters\n"; + $results = $db->searchTheses("test's \"quotes\" & symbols"); + if (is_array($results)) { + echo "✓ PASS: Special characters handled safely\n\n"; + } else { + throw new Exception("Failed to handle special characters"); + } + + echo "✅ All search tests passed!\n"; + return true; + +} catch (Exception $e) { + echo "❌ FAIL: " . $e->getMessage() . "\n"; + return false; +} diff --git a/tests/README.md b/tests/README.md new file mode 100644 index 0000000..41d4e22 --- /dev/null +++ b/tests/README.md @@ -0,0 +1,234 @@ +# Post-ERG Test Suite + +Centralized test suite for the Post-ERG thesis management system. + +## 📁 Structure + +``` +tests/ +├── run-tests.php # Test runner (runs all tests) +├── Unit/ # Unit tests +│ ├── DatabaseTest.php # Database connection & queries +│ └── RateLimitTest.php # Rate limiting functionality +├── Integration/ # Integration tests +│ └── SearchTest.php # Search functionality +├── Security/ # Security tests +│ └── SecurityTest.php # SQL injection & XSS protection +└── README.md # This file +``` + +## 🚀 Running Tests + +### Run All Tests + +```bash +# Using justfile (recommended) +just test + +# Or directly +php tests/run-tests.php +``` + +### Run Individual Tests + +```bash +# Database test +php tests/Unit/DatabaseTest.php + +# Search test +php tests/Integration/SearchTest.php + +# Security test +php tests/Security/SecurityTest.php + +# Rate limit test +php tests/Unit/RateLimitTest.php +``` + +## ✅ Test Coverage + +### Unit Tests + +**DatabaseTest.php** - Tests basic database operations: +- ✅ Database connection +- ✅ Count published theses +- ✅ Get published theses +- ✅ Get single thesis by ID + +**RateLimitTest.php** - Tests rate limiting: +- ✅ RateLimit initialization +- ✅ check() method +- ✅ sendHeaders() method +- ✅ getResetTime() method +- ✅ cleanup() method + +### Integration Tests + +**SearchTest.php** - Tests search functionality: +- ✅ Empty search query handling +- ✅ Search for specific terms +- ✅ Special characters in search + +### Security Tests + +**SecurityTest.php** - Tests security measures: +- ✅ SQL injection protection +- ✅ Invalid ID rejection +- ✅ XSS protection (output escaping) + +## 📝 Writing New Tests + +### Test File Template + +```php +getMessage() . "\n"; + return false; +} +``` + +### Guidelines + +1. **Return Value**: Return `true` for pass, `false` for fail +2. **Output Format**: Use `✓ PASS:` for successes, `❌ FAIL:` for failures +3. **Exceptions**: Catch and report exceptions clearly +4. **Dependencies**: Require only what's needed via relative paths +5. **Location**: + - `Unit/` - Tests for individual classes/functions + - `Integration/` - Tests for feature workflows + - `Security/` - Tests for security vulnerabilities + +## 🔧 Test Database + +Tests use the test database at `database/test.db`. + +### Setup Test Database + +```bash +# Create from schema +just init-db + +# Create with fixtures (sample data) +just fixtures +``` + +### Reset Test Database + +```bash +just reset-db +``` + +## 📊 Expected Output + +Successful test run: +``` +╔════════════════════════════════════════════╗ +║ Post-ERG Test Suite ║ +╚════════════════════════════════════════════╝ + +┌─────────────────────────────────────────┐ +│ Database (Unit) │ +└─────────────────────────────────────────┘ + +✓ PASS: Database connection successful +✓ PASS: Found 16 published theses +... +✅ TEST PASSED + +... + +╔════════════════════════════════════════════╗ +║ Test Summary ║ +╠════════════════════════════════════════════╣ +║ Total: 4 ║ +║ Passed: 4 ✅ ║ +║ Failed: 0 ║ +╚════════════════════════════════════════════╝ + +✅ All tests passed! +``` + +## 🐛 Debugging Failed Tests + +### Check Logs + +```bash +# Application errors +tail -f error.log + +# Test output +php tests/run-tests.php > test-output.txt 2>&1 +``` + +### Run Tests Individually + +When a test fails, run it directly to see full output: + +```bash +php tests/Unit/DatabaseTest.php +``` + +### Check Database + +```bash +# Open database +just query + +# Check stats +just stats +``` + +## 🔄 Continuous Testing + +### Watch Mode (Future) + +Could add file watching for auto-run: + +```bash +# Future: auto-run tests on file change +just watch-tests +``` + +### Pre-commit Hook (Future) + +Add to `.git/hooks/pre-commit`: + +```bash +#!/bin/bash +php tests/run-tests.php +``` + +## 📚 Related Documentation + +- [Database Specification](../database/DATABASE_SPECIFICATION.md) +- [Security Documentation](../docs/SECURITY.md) +- [Development Guide](../MIGRATION_GUIDE.md) + +--- + +**To run tests:** `just test` diff --git a/tests/Security/SecurityTest.php b/tests/Security/SecurityTest.php new file mode 100644 index 0000000..0be5463 --- /dev/null +++ b/tests/Security/SecurityTest.php @@ -0,0 +1,67 @@ +alert('xss')", + ]; + + foreach ($maliciousQueries as $query) { + try { + $results = $db->searchTheses($query); + echo " ✓ Blocked: " . substr($query, 0, 30) . "...\n"; + } catch (Exception $e) { + // Exception is also acceptable (query blocked) + echo " ✓ Exception: " . substr($query, 0, 30) . "...\n"; + } + } + echo "✓ PASS: SQL injection attempts handled safely\n\n"; + + // Test 2: Invalid thesis ID + echo "Test 2: Invalid Thesis ID\n"; + $invalidIds = ["abc", "'; DROP TABLE theses;", "-1", "999999"]; + + foreach ($invalidIds as $id) { + $result = $db->getThesisById($id); + if ($result === null || $result === false) { + echo " ✓ Rejected: " . $id . "\n"; + } else { + throw new Exception("Invalid ID '$id' was not rejected"); + } + } + echo "✓ PASS: Invalid IDs rejected\n\n"; + + // Test 3: XSS in output (checking data is escaped) + echo "Test 3: XSS Protection (Output Escaping)\n"; + $theses = $db->getPublishedTheses(1, 0); + if (count($theses) > 0) { + $first = $theses[0]; + // Check that HTML special chars would be handled + if (isset($first['title'])) { + echo " ✓ Title data retrieved safely\n"; + } + } + echo "✓ PASS: Output handling verified\n\n"; + + echo "✅ All security tests passed!\n"; + return true; + +} catch (Exception $e) { + echo "❌ FAIL: " . $e->getMessage() . "\n"; + return false; +} diff --git a/tests/Unit/DatabaseTest.php b/tests/Unit/DatabaseTest.php new file mode 100644 index 0000000..4ffbbfa --- /dev/null +++ b/tests/Unit/DatabaseTest.php @@ -0,0 +1,58 @@ +countPublishedTheses(); + if ($count >= 0) { + echo "✓ PASS: Found {$count} published theses\n\n"; + } else { + throw new Exception("Invalid count returned"); + } + + // Test 3: Get published theses + echo "Test 3: Get Published Theses\n"; + $theses = $db->getPublishedTheses(5, 0); + if (is_array($theses)) { + echo "✓ PASS: Retrieved " . count($theses) . " theses\n\n"; + } else { + throw new Exception("Invalid theses array returned"); + } + + // Test 4: Get single thesis (if any exist) + if (count($theses) > 0) { + echo "Test 4: Get Single Thesis\n"; + $first = $theses[0]; + $thesis = $db->getThesisById($first['id']); + + if ($thesis && isset($thesis['id'])) { + echo "✓ PASS: Successfully retrieved thesis #{$first['id']}\n"; + echo " Title: " . $thesis['title'] . "\n"; + echo " Author(s): " . ($thesis['authors'] ?? 'N/A') . "\n"; + echo " Year: " . $thesis['year'] . "\n\n"; + } else { + throw new Exception("Failed to retrieve thesis by ID"); + } + } + + echo "✅ All database tests passed!\n"; + return true; + +} catch (Exception $e) { + echo "❌ FAIL: " . $e->getMessage() . "\n"; + return false; +} diff --git a/tests/Unit/RateLimitTest.php b/tests/Unit/RateLimitTest.php new file mode 100644 index 0000000..c229cd3 --- /dev/null +++ b/tests/Unit/RateLimitTest.php @@ -0,0 +1,54 @@ +check(); + if (is_bool($allowed)) { + echo "✓ PASS: check() returns boolean (allowed: " . ($allowed ? 'yes' : 'no') . ")\n\n"; + } else { + throw new Exception("check() did not return boolean"); + } + + // Test 3: Headers method + echo "Test 3: Send Headers Method\n"; + ob_start(); + $rateLimit->sendHeaders(); + ob_end_clean(); + echo "✓ PASS: sendHeaders() executed without error\n\n"; + + // Test 4: Get reset time + echo "Test 4: Get Reset Time\n"; + $resetTime = $rateLimit->getResetTime(); + if (is_int($resetTime) && $resetTime >= 0) { + echo "✓ PASS: getResetTime() returns valid value ($resetTime seconds)\n\n"; + } else { + throw new Exception("Invalid reset time"); + } + + // Test 5: Cleanup method + echo "Test 5: Cleanup Method\n"; + $rateLimit->cleanup(); + echo "✓ PASS: cleanup() executed without error\n\n"; + + echo "✅ All rate limit tests passed!\n"; + return true; + +} catch (Exception $e) { + echo "❌ FAIL: " . $e->getMessage() . "\n"; + return false; +} diff --git a/apps/public/run-tests.php b/tests/run-tests.php similarity index 55% rename from apps/public/run-tests.php rename to tests/run-tests.php index 9d00195..9027710 100755 --- a/apps/public/run-tests.php +++ b/tests/run-tests.php @@ -1,28 +1,29 @@ #!/usr/bin/env php 'Fixtures', 'path' => __DIR__ . '/../../database/fixtures/CreateTestDatabase.php'], - ['name' => 'Integration', 'path' => __DIR__ . '/tests/Integration/SearchTest.php'], - ['name' => 'Security', 'path' => __DIR__ . '/tests/Security/SecurityTest.php'], - ['name' => 'Unit', 'path' => __DIR__ . '/tests/Unit/RateLimitTest.php'], + ['name' => 'Database (Unit)', 'path' => __DIR__ . '/Unit/DatabaseTest.php'], + ['name' => 'Rate Limit (Unit)', 'path' => __DIR__ . '/Unit/RateLimitTest.php'], + ['name' => 'Search (Integration)', 'path' => __DIR__ . '/Integration/SearchTest.php'], + ['name' => 'Security', 'path' => __DIR__ . '/Security/SecurityTest.php'], ]; $totalTests = 0; $passedTests = 0; $failedTests = 0; +$skippedTests = 0; foreach ($testFiles as $test) { echo "┌─────────────────────────────────────────┐\n"; - echo "│ Test Suite: " . str_pad($test['name'], 27) . "│\n"; + echo "│ " . str_pad($test['name'], 41) . "│\n"; echo "└─────────────────────────────────────────┘\n\n"; $totalTests++; @@ -30,40 +31,38 @@ foreach ($testFiles as $test) { $file = basename($path); if (!file_exists($path)) { - echo "⚠️ SKIP: $file (not found at $path)\n\n"; + echo "⚠️ SKIP: $file (not found)\n\n"; + $skippedTests++; continue; } - echo "Running: $file\n"; - echo str_repeat("─", 50) . "\n"; - ob_start(); $exitCode = 0; + $testResult = false; try { - include $path; + $testResult = include $path; + + // Check if test returned false or had error indicators in output + $output = ob_get_contents(); + if ($testResult === false || + strpos($output, '❌') !== false || + strpos($output, 'FAIL:') !== false) { + $exitCode = 1; + } } catch (Exception $e) { - echo "❌ ERROR: " . $e->getMessage() . "\n"; $exitCode = 1; + echo "❌ EXCEPTION: " . $e->getMessage() . "\n"; } $output = ob_get_clean(); - - if ($exitCode === 0 && ( - strpos($output, '❌') !== false || - strpos($output, 'FAIL') !== false || - strpos($output, 'Error:') !== false - )) { - $exitCode = 1; - } - echo $output; - if ($exitCode === 0) { - echo "\n✅ PASSED\n\n"; + if ($exitCode === 0 && $testResult !== false) { + echo "\n✅ TEST PASSED\n\n"; $passedTests++; } else { - echo "\n❌ FAILED\n\n"; + echo "\n❌ TEST FAILED\n\n"; $failedTests++; } } @@ -71,9 +70,12 @@ foreach ($testFiles as $test) { echo "╔════════════════════════════════════════════╗\n"; echo "║ Test Summary ║\n"; echo "╠════════════════════════════════════════════╣\n"; -echo "║ Total: " . str_pad($totalTests, 35) . "║\n"; -echo "║ Passed: " . str_pad($passedTests . " ✅", 36) . "║\n"; -echo "║ Failed: " . str_pad($failedTests . ($failedTests > 0 ? " ❌" : ""), 36) . "║\n"; +echo "║ Total: " . str_pad($totalTests, 34) . "║\n"; +echo "║ Passed: " . str_pad($passedTests . " ✅", 35) . "║\n"; +echo "║ Failed: " . str_pad($failedTests . ($failedTests > 0 ? " ❌" : ""), 35) . "║\n"; +if ($skippedTests > 0) { + echo "║ Skipped: " . str_pad($skippedTests . " ⚠️", 36) . "║\n"; +} echo "╚════════════════════════════════════════════╝\n\n"; if ($failedTests > 0) {