mirror of
https://codeberg.org/PostERG/xamxam.git
synced 2026-06-26 00:29:18 +02:00
Fix migrations and deploy issues + errors + linting
- scan both pending/ and applied/ dirs so remote catch-up works - fix remote 500s: run.php handles per-statement errors so VIEW rebuilds run after duplicate columns; replace mb_strimwidth with substr (no mbstring extension on server) - add missing migration: 015_license_custom.sql (column existed in schema.sql but was never migrated) - remote: fgetcsv enclosure single-char + AdminLogger permission-denied guard + deploy always migrates - fix admin-filters wrapping: restore flex-wrap, flex-basis on inputs/selects, shrink-protect buttons - fix phpstan: remove redundant ?? [] after isset guard in ThesisEditController - biome: exclude vendored min.js via includes patterns; lint whole js dir; modernise beforeunload-guard.js
This commit is contained in:
@@ -257,7 +257,9 @@ class AdminLogger
|
||||
}
|
||||
|
||||
$line = json_encode($entry, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE) . "\n";
|
||||
error_log($line, 3, $this->logFile);
|
||||
if (is_writable($this->logFile) || (!file_exists($this->logFile) && is_writable(dirname($this->logFile)))) {
|
||||
error_log($line, 3, $this->logFile);
|
||||
}
|
||||
|
||||
if ($this->db !== null) {
|
||||
$this->insertDb($resource, $action, $status, $context);
|
||||
|
||||
@@ -122,7 +122,7 @@ class ExportController
|
||||
*/
|
||||
public function createExportZip(?string $baseDir = null): string
|
||||
{
|
||||
$baseDir = $baseDir ?? 'files';
|
||||
$baseDir ??= 'files';
|
||||
$storageRoot = defined('STORAGE_ROOT') ? STORAGE_ROOT : APP_ROOT . '/storage';
|
||||
$files = $this->getAllThesisFiles();
|
||||
$manifest = $this->buildExportManifest();
|
||||
|
||||
@@ -196,7 +196,7 @@ class ThesisCreateController
|
||||
]);
|
||||
|
||||
$identifier = $this->db->getThesisIdentifier($thesisId);
|
||||
error_log("ThesisCreateController: created thesis #$thesisId ($identifier) with " . count($authorEntries) . " author(s)");
|
||||
error_log("ThesisCreateController: created thesis #$thesisId ($identifier) with " . count($authorEntries) . ' author(s)');
|
||||
|
||||
$this->db->setThesisAuthors($thesisId, $authorEntries);
|
||||
$this->db->setThesisJury($thesisId, $data['juryMembers']);
|
||||
@@ -298,7 +298,7 @@ class ThesisCreateController
|
||||
$authorRaw = $this->sanitiseString($post['auteurice'] ?? '');
|
||||
$authorNames = [];
|
||||
if ($authorRaw !== '') {
|
||||
$authorNames = array_filter(array_map('trim', explode(',', $authorRaw)), fn($n) => $n !== '');
|
||||
$authorNames = array_filter(array_map('trim', explode(',', $authorRaw)), fn ($n) => $n !== '');
|
||||
$authorNames = array_values($authorNames);
|
||||
sort($authorNames, SORT_NATURAL);
|
||||
}
|
||||
|
||||
@@ -165,46 +165,78 @@ class ThesisEditController
|
||||
// ── Basic validation (same required fields as create) ──────────────────
|
||||
$errors = [];
|
||||
$titre = trim($post['titre'] ?? '');
|
||||
if ($titre === '') $errors[] = 'Le titre est requis.';
|
||||
if ($titre === '') {
|
||||
$errors[] = 'Le titre est requis.';
|
||||
}
|
||||
$auteurice = trim($post['auteurice'] ?? '');
|
||||
if ($auteurice === '') $errors[] = "L'auteur·ice est requis.";
|
||||
if ($auteurice === '') {
|
||||
$errors[] = "L'auteur·ice est requis.";
|
||||
}
|
||||
$synopsis = trim($post['synopsis'] ?? '');
|
||||
if ($synopsis === '') $errors[] = 'Le synopsis est requis.';
|
||||
if ($synopsis === '') {
|
||||
$errors[] = 'Le synopsis est requis.';
|
||||
}
|
||||
$annee = intval($post['année'] ?? 0);
|
||||
if ($annee < 2000 || $annee > ((int)date('Y') + 1)) $errors[] = "L'année est invalide.";
|
||||
if ($annee < 2000 || $annee > ((int)date('Y') + 1)) {
|
||||
$errors[] = "L'année est invalide.";
|
||||
}
|
||||
$orientationId = intval($post['orientation'] ?? 0);
|
||||
if ($orientationId <= 0) $errors[] = "L'orientation est requise.";
|
||||
if ($orientationId <= 0) {
|
||||
$errors[] = "L'orientation est requise.";
|
||||
}
|
||||
$apProgramId = intval($post['ap'] ?? 0);
|
||||
if ($apProgramId <= 0) $errors[] = "L'atelier pluridisciplinaire est requis.";
|
||||
if ($apProgramId <= 0) {
|
||||
$errors[] = "L'atelier pluridisciplinaire est requis.";
|
||||
}
|
||||
$finalityId = intval($post['finality'] ?? 0);
|
||||
if ($finalityId <= 0) $errors[] = 'La finalité est requise.';
|
||||
if ($finalityId <= 0) {
|
||||
$errors[] = 'La finalité est requise.';
|
||||
}
|
||||
|
||||
// Languages
|
||||
$langIds = isset($post['languages']) && is_array($post['languages']) ? $post['languages'] : [];
|
||||
if (empty($langIds)) $errors[] = 'Au moins une langue est requise.';
|
||||
if (empty($langIds)) {
|
||||
$errors[] = 'Au moins une langue est requise.';
|
||||
}
|
||||
|
||||
// Formats
|
||||
$fmtIds = isset($post['formats']) && is_array($post['formats']) ? $post['formats'] : [];
|
||||
if (empty($fmtIds)) $errors[] = 'Au moins un format est requis.';
|
||||
if (empty($fmtIds)) {
|
||||
$errors[] = 'Au moins un format est requis.';
|
||||
}
|
||||
|
||||
// Licence
|
||||
$licenseId = filter_var($post['license_id'] ?? '', FILTER_VALIDATE_INT) ?: null;
|
||||
$licenseCustom = trim($post['license_custom'] ?? '');
|
||||
if (!$licenseId && $licenseCustom === '') $errors[] = 'Une licence est requise.';
|
||||
if (!$licenseId && $licenseCustom === '') {
|
||||
$errors[] = 'Une licence est requise.';
|
||||
}
|
||||
|
||||
// Jury
|
||||
$hasPromoteur = !empty(trim($post['jury_promoteur'] ?? ''));
|
||||
$hasLecteurInt = false;
|
||||
$hasLecteurExt = false;
|
||||
foreach ($post['jury_lecteur_interne'] ?? [] as $n) {
|
||||
if (trim((string)$n) !== '') { $hasLecteurInt = true; break; }
|
||||
if (trim((string)$n) !== '') {
|
||||
$hasLecteurInt = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
foreach ($post['jury_lecteur_externe'] ?? [] as $n) {
|
||||
if (trim((string)$n) !== '') { $hasLecteurExt = true; break; }
|
||||
if (trim((string)$n) !== '') {
|
||||
$hasLecteurExt = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!$hasPromoteur) {
|
||||
$errors[] = 'Un·e promoteur·ice interne est requis.';
|
||||
}
|
||||
if (!$hasLecteurInt) {
|
||||
$errors[] = 'Au moins un·e lecteur·ice interne est requis.';
|
||||
}
|
||||
if (!$hasLecteurExt) {
|
||||
$errors[] = 'Au moins un·e lecteur·ice externe est requis.';
|
||||
}
|
||||
if (!$hasPromoteur) $errors[] = 'Un·e promoteur·ice interne est requis.';
|
||||
if (!$hasLecteurInt) $errors[] = 'Au moins un·e lecteur·ice interne est requis.';
|
||||
if (!$hasLecteurExt) $errors[] = 'Au moins un·e lecteur·ice externe est requis.';
|
||||
|
||||
if (!empty($errors)) {
|
||||
throw new RuntimeException(implode(' ', $errors));
|
||||
@@ -241,7 +273,7 @@ class ThesisEditController
|
||||
$showContact = !empty($post['contact_public']);
|
||||
$authorNames = [];
|
||||
if ($authorsRaw !== '') {
|
||||
$authorNames = array_values(array_filter(array_map('trim', explode(',', $authorsRaw)), fn($n) => $n !== ''));
|
||||
$authorNames = array_values(array_filter(array_map('trim', explode(',', $authorsRaw)), fn ($n) => $n !== ''));
|
||||
sort($authorNames, SORT_NATURAL);
|
||||
}
|
||||
$authorEntries = [];
|
||||
@@ -402,7 +434,7 @@ class ThesisEditController
|
||||
$authorName = trim($post['auteurice'] ?? 'unknown');
|
||||
|
||||
// Sort the raw comma-separated string alphabetically, then slugify.
|
||||
$names = array_values(array_filter(array_map('trim', explode(',', $authorName)), fn($n) => $n !== ''));
|
||||
$names = array_values(array_filter(array_map('trim', explode(',', $authorName)), fn ($n) => $n !== ''));
|
||||
sort($names, SORT_NATURAL);
|
||||
$authorSlug = $this->generateAuthorSlug(implode(', ', $names));
|
||||
|
||||
@@ -674,7 +706,7 @@ class ThesisEditController
|
||||
|
||||
// Backwards compat: old jury_lecteurs[]
|
||||
if (isset($post['jury_lecteurs'])) {
|
||||
foreach ($post['jury_lecteurs'] ?? [] as $i => $name) {
|
||||
foreach ($post['jury_lecteurs'] as $i => $name) {
|
||||
$name = trim($name);
|
||||
if ($name !== '') {
|
||||
$members[] = [
|
||||
|
||||
@@ -1055,7 +1055,7 @@ class Database
|
||||
|
||||
// Levenshtein distance ≤ 10 % of the longer string.
|
||||
// levenshtein() is limited to 255 chars; use substrings for safety.
|
||||
$a = mb_substr($normNew, 0, 255);
|
||||
$a = mb_substr($normNew, 0, 255);
|
||||
$b = mb_substr($normExisting, 0, 255);
|
||||
$dist = levenshtein($a, $b);
|
||||
$threshold = (int)ceil($maxLen * 0.10);
|
||||
|
||||
Reference in New Issue
Block a user