diff --git a/TODO.md b/TODO.md index 88b98a1..f5d493f 100644 --- a/TODO.md +++ b/TODO.md @@ -7,5 +7,7 @@ - [x] Fix #5: "Contact public : non" partout, non modifiable, sans impact - [x] Fix #6: Investiguer "libre → interne" impossible — aucune restriction trouvée dans le code admin - [x] Hotfix: contact_visible manquant dans le SQL de updateThesis (l'edit matchait createThesis à la place) -- [x] Fix #7: Options de licence non persistées en edit — HTMX load trigger perdait les valeurs (pas de hidden inputs pour license_id/license_custom/cc2r/want_license dans fieldset-licence-explanation.php) -- [x] Commit + jj new +- [x] Fix #7: Options de licence non persistées en edit — HTMX load trigger perdait les valeurs +- [x] Fix #3 (v2): findOrCreateAuthor avec cascade ID → nom → email, setThesisAuthors passe les IDs existants +- [x] Migration 038: corriger les identifiers theses qui ne matchent pas leur année +- [ ] Commit + jj new diff --git a/app/migrations/applied/038_fix_mismatched_identifiers.php b/app/migrations/applied/038_fix_mismatched_identifiers.php new file mode 100644 index 0000000..482c31e --- /dev/null +++ b/app/migrations/applied/038_fix_mismatched_identifiers.php @@ -0,0 +1,85 @@ +- using the next available sequence + * number for that year. Entries are ordered by id within each year so the + * numbering is deterministic and roughly chronological. + * + * Safe to re-run: only updates identifiers that are already mismatched. + */ + +defined('APP_ROOT') || define('APP_ROOT', dirname(__DIR__, 2)); +defined('STORAGE_ROOT') || define('STORAGE_ROOT', APP_ROOT . '/storage'); + +$dbPath = APP_ROOT . '/storage/xamxam.db'; +if (!file_exists($dbPath)) { + echo "ERROR: database not found at $dbPath\n"; + exit(1); +} + +$pdo = new PDO('sqlite:' . $dbPath); +$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); +$pdo->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC); +$pdo->exec('PRAGMA foreign_keys = ON'); + +// ── 1. Identify mismatched rows ────────────────────────────────────────── +$stmt = $pdo->query( + "SELECT id, year, identifier + FROM theses + WHERE deleted_at IS NULL + AND CAST(SUBSTR(identifier, 1, 4) AS INTEGER) != year" +); +$mismatched = $stmt->fetchAll(); + +if (empty($mismatched)) { + echo "No mismatched identifiers found. Nothing to fix.\n"; + exit(0); +} + +echo "Found " . count($mismatched) . " thesis(es) with mismatched identifiers.\n"; + +// Group by year, ordered by id within each year (deterministic renumbering) +$byYear = []; +foreach ($mismatched as $row) { + $year = (int)$row['year']; + $byYear[$year][] = $row; +} + +$fixed = 0; +foreach ($byYear as $year => $rows) { + // Sort by id for deterministic sequencing + usort($rows, fn($a, $b) => (int)$a['id'] <=> (int)$b['id']); + + // Find the max existing sequence for CORRECT identifiers of this year + $stmt = $pdo->prepare( + "SELECT COALESCE(MAX(CAST(SUBSTR(identifier, 6) AS INTEGER)), 0) + FROM theses + WHERE year = ? + AND deleted_at IS NULL + AND CAST(SUBSTR(identifier, 1, 4) AS INTEGER) = ?" + ); + $stmt->execute([$year, $year]); + $maxSeq = (int)$stmt->fetchColumn(); + + foreach ($rows as $row) { + $maxSeq++; + $newIdentifier = sprintf('%d-%03d', $year, $maxSeq); + + echo " Thesis {$row['id']}: {$row['identifier']} → {$newIdentifier} (year={$year})\n"; + + $pdo->prepare("UPDATE theses SET identifier = ? WHERE id = ?") + ->execute([$newIdentifier, $row['id']]); + $fixed++; + } +} + +echo "\nFixed $fixed identifier(s).\n"; +echo "Migration 038 complete.\n"; diff --git a/app/storage/logs/admin-2026-06-09.log b/app/storage/logs/admin-2026-06-09.log index 2b74a6b..76bef1a 100644 --- a/app/storage/logs/admin-2026-06-09.log +++ b/app/storage/logs/admin-2026-06-09.log @@ -5,3 +5,4 @@ {"timestamp":"2026-06-09T10:34:25+00:00","ip":"127.0.0.1","user_agent":"Mozilla/5.0 (X11; Linux x86_64; rv:151.0) Gecko/20100101 Firefox/151.0","resource":"thesis","action":"edit","status":"success","context":{"thesis_id":26,"title":"DepNum"}} {"timestamp":"2026-06-09T10:34:45+00:00","ip":"127.0.0.1","user_agent":"Mozilla/5.0 (X11; Linux x86_64; rv:151.0) Gecko/20100101 Firefox/151.0","resource":"thesis","action":"edit","status":"success","context":{"thesis_id":26,"title":"DepNum"}} {"timestamp":"2026-06-09T10:34:52+00:00","ip":"127.0.0.1","user_agent":"Mozilla/5.0 (X11; Linux x86_64; rv:151.0) Gecko/20100101 Firefox/151.0","resource":"thesis","action":"edit","status":"success","context":{"thesis_id":26,"title":"DepNum"}} +{"timestamp":"2026-06-09T10:42:57+00:00","ip":"127.0.0.1","user_agent":"Mozilla/5.0 (X11; Linux x86_64; rv:151.0) Gecko/20100101 Firefox/151.0","resource":"thesis","action":"edit","status":"success","context":{"thesis_id":26,"title":"DepNum"}} diff --git a/app/storage/logs/audit-2026-06-09.log b/app/storage/logs/audit-2026-06-09.log index aa01c36..f59506c 100644 --- a/app/storage/logs/audit-2026-06-09.log +++ b/app/storage/logs/audit-2026-06-09.log @@ -14,3 +14,5 @@ {"timestamp":"2026-06-09T10:34:45+00:00","ip":"127.0.0.1","user_agent":"Mozilla/5.0 (X11; Linux x86_64; rv:151.0) Gecko/20100101 Firefox/151.0","actor":"127.0.0.1","action":"DELETE","table":"thesis_files","record_id":6,"old_data":{"id":6,"thesis_id":26,"file_type":"website","file_path":"https://depnum.happyngreen.fr/","file_name":"depnum.happyngreen.fr","file_size":0,"mime_type":"text/html","description":null,"uploaded_at":"2026-06-09 10:34:25","sort_order":1,"display_label":null,"file_hash":null}} {"timestamp":"2026-06-09T10:34:52+00:00","ip":"127.0.0.1","user_agent":"Mozilla/5.0 (X11; Linux x86_64; rv:151.0) Gecko/20100101 Firefox/151.0","actor":"127.0.0.1","action":"UPDATE","table":"theses","record_id":26,"old_data":{"id":26,"identifier":"2024-026","title":"DepNum","subtitle":null,"year":2024,"is_doctoral":0,"objet":"tfe","orientation_id":1,"ap_program_id":3,"finality_id":3,"synopsis":"Mon mémoire de Master à l'ERG est un blog autobiographique sur ma dépendance numérique. Chaque post mêle vécu personnel, questions et recherche. J'explore les dynamiques complexes de notre dépendance collective aux technologies numériques, en croisant expérience individuelle et réflexion systémique. JLKJLKJLKJ","context_note":"blposqujdfmlkqshjd mfglkqjhzmdslkf qsdmlkfj mlqskjdf mqskdjf mlqksdjf mlqksdjf mlqksjd fmlkjqsd","remarks":null,"access_type_id":2,"license_id":3,"jury_points":17.5,"jury_note_added":0,"submitted_at":"2026-06-08 08:33:14","defense_date":null,"published_at":null,"is_published":1,"baiu_link":"https://ils.bib.uclouvain.be/global/documents/3830452","created_at":"2026-06-08 08:33:14","updated_at":"2026-06-09 10:34:45","exemplaire_baiu":0,"exemplaire_erg":0,"cc2r":1,"license_custom":null,"deleted_at":null,"contact_visible":null}} {"timestamp":"2026-06-09T10:34:52+00:00","ip":"127.0.0.1","user_agent":"Mozilla/5.0 (X11; Linux x86_64; rv:151.0) Gecko/20100101 Firefox/151.0","actor":"127.0.0.1","action":"DELETE","table":"thesis_files","record_id":7,"old_data":{"id":7,"thesis_id":26,"file_type":"website","file_path":"https://depnum.happyngreen.fr/","file_name":"depnum.happyngreen.fr","file_size":0,"mime_type":"text/html","description":null,"uploaded_at":"2026-06-09 10:34:45","sort_order":1,"display_label":null,"file_hash":null}} +{"timestamp":"2026-06-09T10:42:57+00:00","ip":"127.0.0.1","user_agent":"Mozilla/5.0 (X11; Linux x86_64; rv:151.0) Gecko/20100101 Firefox/151.0","actor":"127.0.0.1","action":"UPDATE","table":"theses","record_id":26,"old_data":{"id":26,"identifier":"2024-026","title":"DepNum","subtitle":null,"year":2024,"is_doctoral":0,"objet":"tfe","orientation_id":1,"ap_program_id":3,"finality_id":3,"synopsis":"Mon mémoire de Master à l'ERG est un blog autobiographique sur ma dépendance numérique. Chaque post mêle vécu personnel, questions et recherche. J'explore les dynamiques complexes de notre dépendance collective aux technologies numériques, en croisant expérience individuelle et réflexion systémique. JLKJLKJLKJ","context_note":"blposqujdfmlkqshjd mfglkqjhzmdslkf qsdmlkfj mlqskjdf mqskdjf mlqksdjf mlqksdjf mlqksjd fmlkjqsd","remarks":null,"access_type_id":2,"license_id":3,"jury_points":17.5,"jury_note_added":0,"submitted_at":"2026-06-08 08:33:14","defense_date":null,"published_at":null,"is_published":1,"baiu_link":"https://ils.bib.uclouvain.be/global/documents/3830452","created_at":"2026-06-08 08:33:14","updated_at":"2026-06-09 10:34:52","exemplaire_baiu":0,"exemplaire_erg":0,"cc2r":1,"license_custom":null,"deleted_at":null,"contact_visible":null}} +{"timestamp":"2026-06-09T10:42:57+00:00","ip":"127.0.0.1","user_agent":"Mozilla/5.0 (X11; Linux x86_64; rv:151.0) Gecko/20100101 Firefox/151.0","actor":"127.0.0.1","action":"DELETE","table":"thesis_files","record_id":8,"old_data":{"id":8,"thesis_id":26,"file_type":"website","file_path":"https://depnum.happyngreen.fr/","file_name":"depnum.happyngreen.fr","file_size":0,"mime_type":"text/html","description":null,"uploaded_at":"2026-06-09 10:34:52","sort_order":1,"display_label":null,"file_hash":null}}