formulaire: correctifs identifiant/année, contact, fichiers optionnels

- Identifiant: mise à jour automatique quand l'année change en back-office (updateThesis + ThesisEditController)
- Contact: hint enrichi (1 seul contact, formatage Instagram/Mastodon)
- Fichiers: TFE rendu optionnel pour Site web/Performance/Installation (note d'intention reste obligatoire)
This commit is contained in:
Pontoporeia
2026-06-08 18:05:43 +02:00
parent c4664ec2e9
commit 3d524226a1
11 changed files with 107 additions and 44 deletions

File diff suppressed because one or more lines are too long

View File

@@ -6,3 +6,7 @@
- [x] Commit + jj new
- [x] Ajouter l'affichage de la finalité sur la page publique TFE (tfe.php)
- [x] Fix "ATELIERS PLURIDISCIPLINAIRES" mid-word break in repertoire column headers
- [x] Mise à jour auto de l'identifiant quand l'année change en back-office
- [x] Améliorer les hints du champ contact dans le formulaire étudiant
- [x] Rendre le fichier TFE optionnel pour Site web / Performance / Installation (note d'intention reste obligatoire)
- [x] Augmenter les limites d'upload : vidéo/audio 8 GB, images/archives 1 GB, nginx 8 GB

View File

@@ -47,24 +47,24 @@
labelFileTypeNotAllowed: "Format non accepté",
fileValidateTypeLabelExpectedTypes:
"PDF, Images, Vidéos, Audio, VTT, Archives",
maxFileSize: "500MB",
maxFileSize: "1GB",
labelMaxFileSizeExceeded: "Fichier trop volumineux",
labelMaxFileSize: "Taille max: {filesize}",
allowMultiple: true,
// Per-extension size limits: certain types get higher caps.
perExtensionMaxSize: {
pdf: "100MB",
mp4: "2GB",
webm: "2GB",
ogv: "2GB",
mov: "2GB",
mp3: "2GB",
ogg: "2GB",
oga: "2GB",
wav: "2GB",
flac: "2GB",
aac: "2GB",
m4a: "2GB",
mp4: "8GB",
webm: "8GB",
ogv: "8GB",
mov: "8GB",
mp3: "8GB",
ogg: "8GB",
oga: "8GB",
wav: "8GB",
flac: "8GB",
aac: "8GB",
m4a: "8GB",
},
},
annexe: {
@@ -76,7 +76,7 @@
],
labelFileTypeNotAllowed: "Format non accepté",
fileValidateTypeLabelExpectedTypes: "PDF, ZIP, TAR, GZ",
maxFileSize: "500MB",
maxFileSize: "1GB",
labelMaxFileSizeExceeded: "Fichier trop volumineux",
labelMaxFileSize: "Taille max: {filesize}",
allowMultiple: true,

View File

@@ -217,6 +217,16 @@ class ThesisEditController
'cc2r' => !empty($post['cc2r']),
'license_custom' => trim($post['license_custom'] ?? ''),
];
// Regenerate identifier if year changed
$oldThesis = $this->db->getThesis($thesisId);
$oldYear = (int)($oldThesis['year'] ?? 0);
$newYear = $meta['year'];
if ($newYear !== $oldYear && $newYear >= 2000) {
$newIdentifier = $this->db->generateThesisIdentifier($newYear);
$meta['identifier'] = $newIdentifier;
error_log('[ThesisEdit] Year changed ' . $oldYear . ' → ' . $newYear . ', new identifier: ' . $newIdentifier);
}
$this->db->updateThesis($thesisId, $meta);
error_log('[ThesisEdit] Step 1 OK — thesis_id=' . $thesisId);

View File

@@ -2122,8 +2122,16 @@ class Database
$this->fetchRow('theses', $thesisId)
);
$stmt = $this->pdo->prepare('
$identifierCol = '';
$params = [];
if (array_key_exists('identifier', $data)) {
$identifierCol = 'identifier = ?,';
$params[] = $data['identifier'];
}
$stmt = $this->pdo->prepare("
UPDATE theses SET
$identifierCol
title = ?,
subtitle = ?,
year = ?,
@@ -2144,7 +2152,7 @@ class Database
cc2r = ?,
updated_at = CURRENT_TIMESTAMP
WHERE id = ?
');
");
$orientation = ($data['orientation_id'] ?? null) ? (int)$data['orientation_id'] : null;
$ap = ($data['ap_program_id'] ?? null) ? (int)$data['ap_program_id'] : null;
$finality = ($data['finality_id'] ?? null) ? (int)$data['finality_id'] : null;
@@ -2153,7 +2161,7 @@ class Database
error_log("[DB:updateThesis] thesis_id=$thesisId orientation=$orientation ap=$ap finality=$finality license=$license access=$access");
$stmt->execute([
$params = array_merge($params, [
$data['title'],
!empty($data['subtitle']) ? $data['subtitle'] : null,
(int)$data['year'],
@@ -2174,6 +2182,7 @@ class Database
!empty($data['cc2r']) ? 1 : 0,
$thesisId,
]);
$stmt->execute($params);
}
/**

View File

@@ -54,17 +54,17 @@ class FilepondHandler
public const QUEUE_SIZE_LIMITS = [
'cover' => 20 * 1024 * 1024, // 20 MB
'note_intention' => 100 * 1024 * 1024, // 100 MB
'tfe' => 500 * 1024 * 1024, // 500 MB
'video' => 500 * 1024 * 1024, // 500 MB
'audio' => 500 * 1024 * 1024, // 500 MB
'annexe' => 500 * 1024 * 1024, // 500 MB
'peertube_video' => 500 * 1024 * 1024, // 500 MB
'peertube_audio' => 500 * 1024 * 1024, // 500 MB
'tfe' => 1024 * 1024 * 1024, // 1 GB (default for non-AV, non-PDF)
'video' => 8 * 1024 * 1024 * 1024, // 8 GB
'audio' => 8 * 1024 * 1024 * 1024, // 8 GB
'annexe' => 1024 * 1024 * 1024, // 1 GB
'peertube_video' => 8 * 1024 * 1024 * 1024, // 8 GB
'peertube_audio' => 8 * 1024 * 1024 * 1024, // 8 GB
];
public const AV_EXTENSIONS = ['mp4', 'webm', 'ogv', 'mov', 'mp3', 'ogg', 'oga', 'wav', 'flac', 'aac', 'm4a'];
public const MAX_PDF_SIZE = 100 * 1024 * 1024; // 100 MB
public const MAX_AV_SIZE = 2 * 1024 * 1024 * 1024; // 2 GB
public const MAX_AV_SIZE = 8 * 1024 * 1024 * 1024; // 8 GB
// ── Log prefix for distinguishing admin vs partage ───────────────────────

View File

@@ -0,0 +1,36 @@
{"timestamp":"2026-06-08T16:11:04+00:00","ip":"unknown","user_agent":"","context":"test_context","exception":"Exception","message":"test message","trace":"#0 /home/padlock/repos/xamxam/vendor/phpunit/phpunit/src/Framework/TestCase.php(1667): ErrorHandlerTest->testLogWithContext()
#1 /home/padlock/repos/xamxam/vendor/phpunit/phpunit/src/Framework/TestCase.php(519): PHPUnit\\Framework\\TestCase->runTest()
#2 /home/padlock/repos/xamxam/vendor/phpunit/phpunit/src/Framework/TestRunner/TestRunner.php(87): PHPUnit\\Framework\\TestCase->runBare()
#3 /home/padlock/repos/xamxam/vendor/phpunit/phpunit/src/Framework/TestCase.php(365): PHPUnit\\Framework\\TestRunner->run()
#4 /home/padlock/repos/xamxam/vendor/phpunit/phpunit/src/Framework/TestSuite.php(369): PHPUnit\\Framework\\TestCase->run()
#5 /home/padlock/repos/xamxam/vendor/phpunit/phpunit/src/Framework/TestSuite.php(369): PHPUnit\\Framework\\TestSuite->run()
#6 /home/padlock/repos/xamxam/vendor/phpunit/phpunit/src/Framework/TestSuite.php(369): PHPUnit\\Framework\\TestSuite->run()
#7 /home/padlock/repos/xamxam/vendor/phpunit/phpunit/src/TextUI/TestRunner.php(64): PHPUnit\\Framework\\TestSuite->run()
#8 /home/padlock/repos/xamxam/vendor/phpunit/phpunit/src/TextUI/Application.php(211): PHPUnit\\TextUI\\TestRunner->run()
#9 /home/padlock/repos/xamxam/vendor/phpunit/phpunit/phpunit(104): PHPUnit\\TextUI\\Application->run()
#10 /home/padlock/repos/xamxam/vendor/bin/phpunit(122): include('...')
#11 {main}","extra":{"thesis_id":42,"slug":"20250101-TEST1234"}}
{"timestamp":"2026-06-08T16:11:04+00:00","ip":"unknown","user_agent":"","context":"test_null","exception":"PDOException","message":"test","trace":"#0 /home/padlock/repos/xamxam/vendor/phpunit/phpunit/src/Framework/TestCase.php(1667): ErrorHandlerTest->testLogWithNullValues()
#1 /home/padlock/repos/xamxam/vendor/phpunit/phpunit/src/Framework/TestCase.php(519): PHPUnit\\Framework\\TestCase->runTest()
#2 /home/padlock/repos/xamxam/vendor/phpunit/phpunit/src/Framework/TestRunner/TestRunner.php(87): PHPUnit\\Framework\\TestCase->runBare()
#3 /home/padlock/repos/xamxam/vendor/phpunit/phpunit/src/Framework/TestCase.php(365): PHPUnit\\Framework\\TestRunner->run()
#4 /home/padlock/repos/xamxam/vendor/phpunit/phpunit/src/Framework/TestSuite.php(369): PHPUnit\\Framework\\TestCase->run()
#5 /home/padlock/repos/xamxam/vendor/phpunit/phpunit/src/Framework/TestSuite.php(369): PHPUnit\\Framework\\TestSuite->run()
#6 /home/padlock/repos/xamxam/vendor/phpunit/phpunit/src/Framework/TestSuite.php(369): PHPUnit\\Framework\\TestSuite->run()
#7 /home/padlock/repos/xamxam/vendor/phpunit/phpunit/src/TextUI/TestRunner.php(64): PHPUnit\\Framework\\TestSuite->run()
#8 /home/padlock/repos/xamxam/vendor/phpunit/phpunit/src/TextUI/Application.php(211): PHPUnit\\TextUI\\TestRunner->run()
#9 /home/padlock/repos/xamxam/vendor/phpunit/phpunit/phpunit(104): PHPUnit\\TextUI\\Application->run()
#10 /home/padlock/repos/xamxam/vendor/bin/phpunit(122): include('...')
#11 {main}","extra":{"id":null,"name":"foo"}}
{"timestamp":"2026-06-08T16:11:04+00:00","ip":"unknown","user_agent":"","context":"test_empty","exception":"RuntimeException","message":"bare","trace":"#0 /home/padlock/repos/xamxam/vendor/phpunit/phpunit/src/Framework/TestCase.php(1667): ErrorHandlerTest->testLogWithEmptyExtra()
#1 /home/padlock/repos/xamxam/vendor/phpunit/phpunit/src/Framework/TestCase.php(519): PHPUnit\\Framework\\TestCase->runTest()
#2 /home/padlock/repos/xamxam/vendor/phpunit/phpunit/src/Framework/TestRunner/TestRunner.php(87): PHPUnit\\Framework\\TestCase->runBare()
#3 /home/padlock/repos/xamxam/vendor/phpunit/phpunit/src/Framework/TestCase.php(365): PHPUnit\\Framework\\TestRunner->run()
#4 /home/padlock/repos/xamxam/vendor/phpunit/phpunit/src/Framework/TestSuite.php(369): PHPUnit\\Framework\\TestCase->run()
#5 /home/padlock/repos/xamxam/vendor/phpunit/phpunit/src/Framework/TestSuite.php(369): PHPUnit\\Framework\\TestSuite->run()
#6 /home/padlock/repos/xamxam/vendor/phpunit/phpunit/src/Framework/TestSuite.php(369): PHPUnit\\Framework\\TestSuite->run()
#7 /home/padlock/repos/xamxam/vendor/phpunit/phpunit/src/TextUI/TestRunner.php(64): PHPUnit\\Framework\\TestSuite->run()
#8 /home/padlock/repos/xamxam/vendor/phpunit/phpunit/src/TextUI/Application.php(211): PHPUnit\\TextUI\\TestRunner->run()
#9 /home/padlock/repos/xamxam/vendor/phpunit/phpunit/phpunit(104): PHPUnit\\TextUI\\Application->run()
#10 /home/padlock/repos/xamxam/vendor/bin/phpunit(122): include('...')
#11 {main}"}

View File

@@ -36,6 +36,10 @@ $selectedFormats = isset($_POST['formats']) && is_array($_POST['formats'])
$adminMode = ($_POST['admin_mode'] ?? '0') === '1';
$editMode = ($_POST['edit_mode'] ?? '0') === '1';
// TFE file is optional when format is Site web (3), Performance (4) or Installation (6)
$noTfeFileFormats = [3, 4, 6];
$tfeFileOptional = !empty(array_intersect($selectedFormats, $noTfeFileFormats));
$websiteUrl = htmlspecialchars($_POST['website_url'] ?? '');
$websiteLabel = htmlspecialchars($_POST['website_label'] ?? '');
?>
@@ -126,20 +130,20 @@ $websiteLabel = htmlspecialchars($_POST['website_label'] ?? '');
<!-- ── 3. TFE (all files: PDF, images, video, audio, VTT, archives) ── -->
<div class="admin-form-group admin-files-fieldgroup">
<label for="tfe-files-input">TFE<?= !$adminMode ? ' <span class="asterisk">*</span>' : '' ?></label>
<label for="tfe-files-input">TFE<?= (!$adminMode && !$tfeFileOptional) ? ' <span class="asterisk">*</span>' : '' ?><?= $tfeFileOptional ? ' (optionnel pour ce format)' : '' ?></label>
<div class="admin-file-input">
<input type="file" id="tfe-files-input"
name="queue_file[tfe][]"
multiple
class="tfe-file-picker"
data-queue-type="tfe"
<?= !$adminMode ? 'required' : '' ?>
<?= (!$adminMode && !$tfeFileOptional) ? 'required' : '' ?>
data-existing-files='<?= htmlspecialchars(json_encode($existingFilesJsonForTfe ?? []), ENT_QUOTES) ?>'>
<small class="admin-file-hint">
Glissez pour réordonner.
<br>
<br>
PDF (max 100 MB) · Images (max 500 MB) · Vidéo &amp; Audio (max 2 GB) · VTT · Archives (max 500 MB).
PDF (max 100 MB) · Images (max 1 GB) · Vidéo &amp; Audio (max 8 GB) · VTT · Archives (max 1 GB).
<br>→ PDFs trop lourds ? <a href="https://www.bentopdf.com" target="_blank" rel="noopener">https://bentopdf.com/</a>
<?php if ($peerTubeEnabled): ?>
<br><br>Vidéos et audio hébergés sur <a href="<?= htmlspecialchars($peerTubeSettings['instance_url']) ?>" target="_blank" rel="noopener">PeerTube</a>.
@@ -172,7 +176,7 @@ $websiteLabel = htmlspecialchars($_POST['website_label'] ?? '');
class="tfe-file-picker"
data-queue-type="annexe"
data-existing-files='<?= htmlspecialchars(json_encode($existingFilesJsonForAnnexe ?? []), ENT_QUOTES) ?>'>
<small class="admin-file-hint">PDF ou archives ZIP/TAR. Max 500 MB. Glissez pour réordonner.</small>
<small class="admin-file-hint">PDF ou archives ZIP/TAR. Max 1 GB. Glissez pour réordonner.</small>
</div>
<?php if ($editMode): ?>
<button type="button" class="btn btn--sm btn--ghost file-browser-trigger"

View File

@@ -43,7 +43,7 @@ $adminMode = $adminMode ?? false;
class="tfe-file-picker"
data-queue-type="tfe">
<small class="admin-file-hint">
Types acceptés : PDF · JPG/PNG/GIF/WEBP · MP4/WebM/MOV (vidéo) · MP3/OGG/WAV/FLAC (audio) · ZIP/TAR (archives). Max 500 MB par fichier.
Types acceptés : PDF · JPG/PNG/GIF/WEBP · MP4/WebM/MOV (vidéo) · MP3/OGG/WAV/FLAC (audio) · ZIP/TAR (archives). Max 1 GB par fichier.
Les fichiers <code>.vtt</code> sont des sous-titres et seront associés automatiquement à la vidéo précédente.
</small>
</div>

View File

@@ -61,7 +61,7 @@ $adminMode = $adminMode ?? false;
<?php
$name = 'mail'; $label = 'Contact visible (optionnel) [mail/site/insta/etc.] :'; $value = $oldFn('mail');
$attrs = ['autocomplete' => 'email'];
$hint = 'Ce contact sera visible publiquement sur la fiche du TFE.';
$hint = 'Un seul contact. Indiquez l\'URL complète pour un site (https://…), l\'adresse mail, le nom d\'utilisateur avec @ pour Instagram (@pseudo), ou l\'adresse complète pour Mastodon (@pseudo@instance). Ce contact sera visible publiquement sur la fiche du TFE.';
include APP_ROOT . '/templates/partials/form/text-field.php';
?>

View File

@@ -49,8 +49,8 @@ server {
# server_tokens off;
# Max upload size (for thesis files — can include video)
client_max_body_size 1024M;
client_body_timeout 300s;
client_max_body_size 8192M;
client_body_timeout 600s;
# Logging
access_log /var/log/nginx/xamxam_access.log;
@@ -140,11 +140,11 @@ server {
fastcgi_pass unix:/var/run/php/php8.4-fpm.sock;
# Security parameters (must be <= client_max_body_size)
fastcgi_param PHP_VALUE "upload_max_filesize=1024M \n post_max_size=1024M";
fastcgi_param PHP_VALUE "upload_max_filesize=8192M \n post_max_size=8192M";
# Timeouts
fastcgi_read_timeout 300;
fastcgi_send_timeout 300;
fastcgi_read_timeout 600;
fastcgi_send_timeout 600;
}
# Additional security headers for admin
@@ -160,9 +160,9 @@ server {
location ~ \.php$ {
include snippets/fastcgi-php.conf;
fastcgi_pass unix:/var/run/php/php8.4-fpm.sock;
fastcgi_param PHP_VALUE "upload_max_filesize=1024M \n post_max_size=1024M";
fastcgi_read_timeout 300;
fastcgi_send_timeout 300;
fastcgi_param PHP_VALUE "upload_max_filesize=8192M \n post_max_size=8192M";
fastcgi_read_timeout 600;
fastcgi_send_timeout 600;
}
try_files $uri /index.php$is_args$args;
}
@@ -188,11 +188,11 @@ server {
fastcgi_pass unix:/var/run/php/php8.4-fpm.sock;
# Security parameters (must be <= client_max_body_size)
fastcgi_param PHP_VALUE "upload_max_filesize=1024M \n post_max_size=1024M";
fastcgi_param PHP_VALUE "upload_max_filesize=8192M \n post_max_size=8192M";
# Timeouts
fastcgi_read_timeout 300;
fastcgi_send_timeout 300;
fastcgi_read_timeout 600;
fastcgi_send_timeout 600;
}
# All other clean URLs — fall through to front controller