diff --git a/.gitignore b/.gitignore
index 30efe56..66f1018 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,4 +1,4 @@
-# Config Claude Code (local, pas utile au dépôt)
+# Config Claude Code
.claude/
# macOS
diff --git a/database/scriptDatabase.sql b/database/scriptDatabase.sql
index dc12e84..93813da 100644
--- a/database/scriptDatabase.sql
+++ b/database/scriptDatabase.sql
@@ -1,16 +1,11 @@
--- On créer la base de donnée si elle n'existe déjà aps
-CREATE DATABASE IF NOT EXISTS portfolio
+CREATE DATABASE IF NOT EXISTS portfolio;
+USE portfolio;
--- On créer un utilisateur avec un mot de passe fort qui est à changer après execution dus script (voir bitwarden)
-CREATE USER 'srv_portfolio'@'%' IDENTIFIED BY 'mdp';
-
--- On met les droits au compte sur la base portfolio à l'utilisateur
+-- Mot de passe à changer après exécution du script (voir Bitwarden)
+CREATE USER IF NOT EXISTS 'srv_portfolio'@'%' IDENTIFIED BY 'mdp';
GRANT ALL ON portfolio.* TO 'srv_portfolio'@'%';
-
--- On actualise les permissions
FLUSH PRIVILEGES;
--- Création de la table user : (Généré par Beekeeper Studio : équivalent à dbGate)
CREATE TABLE IF NOT EXISTS users (
id INT UNSIGNED NOT NULL AUTO_INCREMENT,
username VARCHAR(50) NOT NULL,
@@ -18,63 +13,69 @@ CREATE TABLE IF NOT EXISTS users (
role ENUM('admin', 'user') NOT NULL DEFAULT 'user',
created_at DATETIME NULL DEFAULT NULL,
last_login DATETIME NULL DEFAULT NULL,
-
PRIMARY KEY (id),
UNIQUE INDEX idx_username (username)
);
--- Création de la table projects : (Généré par Beekeeper Studio)
+INSERT IGNORE INTO users (id, created_at, last_login, password, role, username)
+VALUES (11, '2026-06-08 11:10:21', '2026-06-08 13:10:41', '$2y$12$x01fDqrHQEYIyj7MDHa72eRyl2j/n5cNq9sv3.FIiQCaRsB50Q4iK', 'admin', 'admin');
+
CREATE TABLE IF NOT EXISTS projects (
id INT UNSIGNED NOT NULL AUTO_INCREMENT,
title VARCHAR(100) NOT NULL,
+ status VARCHAR(100) NULL,
tags VARCHAR(255) NULL,
description TEXT NOT NULL,
image_url VARCHAR(255) NOT NULL,
+ markdown LONGTEXT NULL,
+ link_url VARCHAR(255) NULL,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (id)
);
--- Création de la table projects : (Généré par Beekeeper Studio)
CREATE TABLE IF NOT EXISTS experiences (
id INT UNSIGNED NOT NULL AUTO_INCREMENT,
title VARCHAR(100) NOT NULL,
+ status VARCHAR(100) NULL,
xptype VARCHAR(100) NOT NULL,
tags VARCHAR(255) NULL,
description TEXT NOT NULL,
duration VARCHAR(100) NOT NULL,
image_url VARCHAR(255) NOT NULL,
+ markdown LONGTEXT NULL,
+ link_url VARCHAR(255) NULL,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (id)
);
--- Compte de test (PLUS A JOUR METTRE UN HASH)
--- INSERT INTO users (username, password, role)
--- VALUES ('admin', 'admin', 'admin');
-insert into `users` (`created_at`, `id`, `last_login`, `password`, `role`, `username`)
-values ('2026-06-08 11:10:21', 11, '2026-06-08 13:10:41', '$2y$12$x01fDqrHQEYIyj7MDHa72eRyl2j/n5cNq9sv3.FIiQCaRsB50Q4iK', 'admin', 'admin');
+CREATE TABLE IF NOT EXISTS schools (
+ id INT UNSIGNED NOT NULL AUTO_INCREMENT,
+ title VARCHAR(100) NOT NULL,
+ status VARCHAR(100) NULL,
+ tags VARCHAR(255) NULL,
+ description TEXT NOT NULL,
+ duration VARCHAR(100) NOT NULL,
+ image_url VARCHAR(255) NOT NULL,
+ markdown LONGTEXT NULL,
+ link_url VARCHAR(255) NULL,
+ created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
+ PRIMARY KEY (id)
+);
--- Ajout d'une collonne status pour les projets et les expériences pour pouvoir différencier les projets/expériences en cours, terminés ou à venir
-ALTER TABLE projects
-ADD COLUMN status VARCHAR(100) NULL AFTER title;
-ALTER TABLE experiences
-ADD COLUMN status VARCHAR(100) NULL AFTER title;
-
--- Table de réglages du site (clé / valeur) : utilisée pour la notification de la page d'accueil
+-- Réglages du site (clé / valeur), utilisés pour la notification de la page d'accueil
CREATE TABLE IF NOT EXISTS settings (
name VARCHAR(50) NOT NULL,
value TEXT NULL,
PRIMARY KEY (name)
);
--- Valeurs par défaut de la notification (modifiables ensuite depuis le panel Admin)
INSERT INTO settings (name, value) VALUES
('site_notice_title', 'Des bugs peuvent encore se produire.'),
('site_notice_text', 'Si le CSS ou les images s''affichent mal, pensez à vider le cache de votre navigateur (Ctrl + F5 ou Cmd + Shift + R) pour voir les dernières modifications.'),
('site_notice_enabled', '1')
ON DUPLICATE KEY UPDATE value = VALUES(value);
--- Table des compétences : une ligne = une catégorie (carte sur la page Compétences)
--- items : un item par ligne, au format "classes-icone-fontawesome|Texte" (l'icône est optionnelle : "Texte" seul fonctionne aussi)
+-- items : un item par ligne, au format "classes-icone|Texte" (icône optionnelle)
CREATE TABLE IF NOT EXISTS skills (
id INT UNSIGNED NOT NULL AUTO_INCREMENT,
title VARCHAR(100) NOT NULL,
@@ -85,7 +86,6 @@ CREATE TABLE IF NOT EXISTS skills (
PRIMARY KEY (id)
);
--- Compétences actuelles (reprises du CV)
INSERT INTO skills (title, icon, items, position) VALUES
('Systèmes d''exploitation', 'fa-solid fa-computer', 'fa-brands fa-microsoft|Windows (Serveur/XP/8/10/11)\nfa-brands fa-linux|Linux (Debian/Ubuntu)\nfa-brands fa-apple|macOS', 1),
('Réseaux', 'fa-solid fa-network-wired', 'fa-solid fa-location-dot|Adressage IP\nfa-solid fa-diagram-project|VLAN\nfa-solid fa-shield-halved|Pare-feu (iptables/ufw)\nfa-solid fa-globe|DNS (Bind9)\nfa-solid fa-server|DHCP (ISC DHCP)\nfa-solid fa-arrows-left-right|Proxy (Squid)', 2),
@@ -94,108 +94,23 @@ INSERT INTO skills (title, icon, items, position) VALUES
('Développement web', 'fa-solid fa-code', 'fa-brands fa-html5|HTML\nfa-brands fa-css3-alt|CSS\nfa-brands fa-js|JavaScript\nfa-brands fa-php|PHP', 5),
('Virtualisation & Outils', 'fa-solid fa-layer-group', 'fa-solid fa-server|Proxmox\nfa-solid fa-box|VirtualBox\nfa-brands fa-git-alt|Git (GitHub, Gitea)', 6);
--- Table des écoles / formations : affichée sous les expériences professionnelles sur la page Expériences
-CREATE TABLE IF NOT EXISTS schools (
- id INT UNSIGNED NOT NULL AUTO_INCREMENT,
- title VARCHAR(100) NOT NULL,
- status VARCHAR(100) NULL,
- tags VARCHAR(255) NULL,
- description TEXT NOT NULL,
- duration VARCHAR(100) NOT NULL,
- image_url VARCHAR(255) NOT NULL,
- created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
- PRIMARY KEY (id)
-);
-
-
--- markdown : contenu de la page détaillée (LONGTEXT = ~4 Go, taille pratiquement illimitée)
--- link_url : lien externe utilisé à la place du markdown si celui-ci est vide
--- Les deux sont facultatifs : si les deux sont vides, la carte n'est pas cliquable
-ALTER TABLE projects
-ADD COLUMN markdown LONGTEXT NULL,
-ADD COLUMN link_url VARCHAR(255) NULL;
-
-ALTER TABLE experiences
-ADD COLUMN markdown LONGTEXT NULL,
-ADD COLUMN link_url VARCHAR(255) NULL;
-
-ALTER TABLE schools
-ADD COLUMN markdown LONGTEXT NULL,
-ADD COLUMN link_url VARCHAR(255) NULL;
-
--- ============================================================
--- Juillet 2026 (4) : bandeaux de logos sur la page d'accueil
--- ("Outils que je sais manipuler" et "Ils m'ont fait confiance"),
--- gérés depuis le panel Admin
--- ============================================================
-
--- Une seule table pour les deux bandeaux, distingués par la colonne category :
--- - 'tool' => bandeau "Outils que je sais manipuler"
--- - 'partner' => bandeau "Ils m'ont fait confiance"
--- link_url est facultatif : si renseigné, le logo devient cliquable (nouvel onglet)
+-- category : 'tool' (bandeau "Outils que je sais manipuler") ou 'partner' (bandeau "Ils m'ont fait confiance")
+-- icon : classes CSS Devicon, prioritaire sur logo_url si les deux sont renseignés (au moins un des deux requis)
CREATE TABLE IF NOT EXISTS logo_banners (
id INT UNSIGNED NOT NULL AUTO_INCREMENT,
category ENUM('tool', 'partner') NOT NULL,
name VARCHAR(100) NOT NULL,
- logo_url VARCHAR(255) NOT NULL,
+ icon VARCHAR(100) NULL,
+ logo_url VARCHAR(255) NULL,
link_url VARCHAR(255) NULL,
position INT NOT NULL DEFAULT 0,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (id)
);
--- Table créée vide volontairement : les logos sont ajoutés depuis le panel Admin
--- (section Logos) pour éviter d'afficher des images devinées/potentiellement cassées
-
--- ============================================================
--- Juillet 2026 (3) : PDF en drag & drop pour les projets,
--- expériences et écoles, lu depuis un mini lecteur intégré
--- ============================================================
-
--- pdf_data : contenu binaire du PDF (LONGBLOB = ~4 Go, largement suffisant)
--- pdf_filename : nom original du fichier (affiché dans le panel admin et le lecteur)
--- Priorité d'affichage sur la page de détails : markdown > pdf > lien externe
-ALTER TABLE projects
-ADD COLUMN pdf_data LONGBLOB NULL,
-ADD COLUMN pdf_filename VARCHAR(255) NULL;
-
-ALTER TABLE experiences
-ADD COLUMN pdf_data LONGBLOB NULL,
-ADD COLUMN pdf_filename VARCHAR(255) NULL;
-
-ALTER TABLE schools
-ADD COLUMN pdf_data LONGBLOB NULL,
-ADD COLUMN pdf_filename VARCHAR(255) NULL;
-
--- ============================================================
--- Juillet 2026 (5) : icône Devicon en alternative à l'image pour
--- le bandeau de logos (surtout utile pour "Outils que je sais
--- manipuler" : Devicon est un équivalent gratuit de Font Awesome
--- dédié aux logos d'outils/langages/frameworks, cf. devicon.dev)
--- ============================================================
-
--- icon : classes CSS Devicon (ex: "devicon-docker-plain colored"), prioritaire sur logo_url
--- si les deux sont renseignés. logo_url devient facultatif : il faut au moins l'un des deux
--- (validé côté PHP), pour les outils absents de Devicon (Proxmox, GLPI, FOG...)
-ALTER TABLE logo_banners
-ADD COLUMN icon VARCHAR(100) NULL AFTER name,
-MODIFY COLUMN logo_url VARCHAR(255) NULL;
-
--- ============================================================
--- Juillet 2026 (6) : sortie des PDF des tables projects/experiences/schools
--- vers des tables associées dédiées, pour que les pages qui LISTENT ces
--- éléments (admin.php, xp.php, projects.php...) n'aient plus à charger les
--- PDF (jusqu'à 15 Mo chacun) en mémoire à chaque affichage. C'était la
--- cause du ralentissement d'admin.php et de xp.php : ces pages faisaient
--- un SELECT * qui remontait aussi tout le contenu binaire des PDF alors
--- que seul le nom de fichier est utilisé dans les listes.
---
--- ÉTAPE 1/2 (non destructive) : crée les nouvelles tables et copie les PDF
--- existants dedans. Les colonnes pdf_data / pdf_filename des tables
--- d'origine ne sont PAS touchées à cette étape : aucune perte de donnée
--- possible, elles restent en place comme copie de sécurité.
--- ============================================================
-
+-- PDF de projects/experiences/schools : tables associées plutôt qu'une colonne sur la table
+-- parente, pour que les pages qui LISTENT ces éléments n'aient jamais à charger le contenu
+-- binaire des PDF en mémoire (seul le nom de fichier y est utilisé)
CREATE TABLE IF NOT EXISTS project_pdfs (
project_id INT UNSIGNED NOT NULL,
pdf_filename VARCHAR(255) NOT NULL,
@@ -220,24 +135,29 @@ CREATE TABLE IF NOT EXISTS school_pdfs (
CONSTRAINT fk_school_pdfs_school FOREIGN KEY (school_id) REFERENCES schools(id) ON DELETE CASCADE
);
--- Copie des PDF déjà en base vers les nouvelles tables (ne copie que les lignes qui ont
--- effectivement un PDF ; ré-exécutable sans doublon grâce à la clé primaire = id du parent)
-INSERT IGNORE INTO project_pdfs (project_id, pdf_filename, pdf_data)
-SELECT id, pdf_filename, pdf_data FROM projects WHERE pdf_data IS NOT NULL;
+-- Jeu "Presse-Citron" (easter egg, code Konami) : comptes séparés des comptes admin ci-dessus.
+-- game_state (JSON) stocke bâtiments/améliorations possédés ; toute la logique du jeu vit côté JS.
+CREATE TABLE IF NOT EXISTS lemon_players (
+ id INT UNSIGNED NOT NULL AUTO_INCREMENT,
+ username VARCHAR(50) NOT NULL,
+ password VARCHAR(255) NOT NULL,
+ score DOUBLE NOT NULL DEFAULT 0,
+ lifetime_score DOUBLE NOT NULL DEFAULT 0,
+ prestige_level INT UNSIGNED NOT NULL DEFAULT 0,
+ prestige_bonus DOUBLE NOT NULL DEFAULT 0,
+ game_state LONGTEXT NULL,
+ created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
+ last_played DATETIME NULL,
+ PRIMARY KEY (id),
+ UNIQUE INDEX idx_lemon_username (username)
+);
-INSERT IGNORE INTO experience_pdfs (experience_id, pdf_filename, pdf_data)
-SELECT id, pdf_filename, pdf_data FROM experiences WHERE pdf_data IS NOT NULL;
-
-INSERT IGNORE INTO school_pdfs (school_id, pdf_filename, pdf_data)
-SELECT id, pdf_filename, pdf_data FROM schools WHERE pdf_data IS NOT NULL;
-
--- ============================================================
--- ÉTAPE 2/2 (destructive, À NE PAS EXÉCUTER MAINTENANT) : à lancer plus
--- tard, une fois que le site a été vérifié (listes, modification, suppression,
--- lecteur PDF) et qu'on est sûr à 100% que tout fonctionne avec les
--- nouvelles tables. Supprime les anciennes colonnes devenues inutiles.
--- ============================================================
-
--- ALTER TABLE projects DROP COLUMN pdf_data, DROP COLUMN pdf_filename;
--- ALTER TABLE experiences DROP COLUMN pdf_data, DROP COLUMN pdf_filename;
--- ALTER TABLE schools DROP COLUMN pdf_data, DROP COLUMN pdf_filename;
\ No newline at end of file
+-- Photo de profil des joueurs "Presse-Citron", table à part pour la même raison que les PDF
+CREATE TABLE IF NOT EXISTS lemon_avatars (
+ player_id INT UNSIGNED NOT NULL,
+ avatar_data LONGBLOB NOT NULL,
+ avatar_mime VARCHAR(50) NOT NULL,
+ updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,
+ PRIMARY KEY (player_id),
+ CONSTRAINT fk_lemon_avatars_player FOREIGN KEY (player_id) REFERENCES lemon_players(id) ON DELETE CASCADE
+);
diff --git a/elements/footer.php b/elements/footer.php
index 0713a3f..b635f8a 100644
--- a/elements/footer.php
+++ b/elements/footer.php
@@ -74,5 +74,125 @@ if (isset($_GET['success']) && $_GET['success'] === '1') {
})();
+
+
+
+