mirror of
https://codeberg.org/PostERG/xamxam.git
synced 2026-06-25 16:19:19 +02:00
refactor form structure per new spec + fix
- split jury into interne/externe/ULB, - remove president from student form, - add language_autre, - split duration into pages+minutes+annexes, - move licence to degrés d'ouverture with CC2r, - add license_custom, - filter PACS from student AP list, - editable généralités help block, - Libre toggle per settings Fix: - missing comma after cc4r column in schema.sql - remove duplicate form footer from partage template - remove couverture from student files fieldset; add promoteur ULB conditional disable via JS on Approfondi - promoteur ULB: remove 'si applicable', make required when visible
This commit is contained in:
@@ -23,6 +23,12 @@ $formData = $_SESSION['form_data'] ?? [];
|
||||
unset($_SESSION['form_data']);
|
||||
$autofocusField = App::consumeAutofocus();
|
||||
|
||||
// Site settings for licence / access type toggles
|
||||
$siteSettings = Database::getInstance()->getAllSettings();
|
||||
// Form help blocks
|
||||
$helpBlocks = Database::getInstance()->getAllFormHelpBlocks();
|
||||
$helpFn = fn(string $key) => $helpBlocks[$key]['content'] ?? '';
|
||||
|
||||
function withAutofocus(string $fieldName, array $attrs = []): array {
|
||||
global $autofocusField;
|
||||
if ($autofocusField === $fieldName) {
|
||||
|
||||
@@ -17,6 +17,10 @@ if ($thesisId <= 0) {
|
||||
|
||||
$autofocusField = App::consumeAutofocus();
|
||||
|
||||
// Form help blocks for editable généralités
|
||||
$helpBlocks = Database::getInstance()->getAllFormHelpBlocks();
|
||||
$helpFn = fn(string $key) => $helpBlocks[$key]['content'] ?? '';
|
||||
|
||||
try {
|
||||
$ctrl = ThesisEditController::create();
|
||||
$view = $ctrl->load($thesisId);
|
||||
|
||||
@@ -284,7 +284,6 @@ function renderShareLinkForm(string $slug, array $link): void
|
||||
<?php
|
||||
$oldFn = $shareOldFn;
|
||||
$withAutofocusFn = $shareWithAutofocusFn;
|
||||
// Inject fieldset intro note and synopsis-specific note via the partial's hook.
|
||||
ob_start();
|
||||
$helpContent = $helpFn('fieldset_synopsis');
|
||||
include APP_ROOT . '/templates/partials/form/form-help-block.php';
|
||||
@@ -294,26 +293,29 @@ function renderShareLinkForm(string $slug, array $link): void
|
||||
include APP_ROOT . '/templates/partials/form/fieldset-tfe-info.php';
|
||||
?>
|
||||
|
||||
<!-- ═══════════════════ Composition du jury ═══════════════════ -->
|
||||
<?php
|
||||
// Seed jury partial from repopulated $formData (share add-mode).
|
||||
$juryPresident = old($formData, 'jury_president');
|
||||
$juryPromoteur = old($formData, 'jury_promoteur');
|
||||
$juryPromoteurExt = !empty($formData['jury_promoteur_ext']) ? 1 : 0;
|
||||
$juryLecteurs = [];
|
||||
for ($i = 0; $i < 4; $i++) {
|
||||
$name = old($formData, "jury_lecteurs:$i");
|
||||
if ($name !== '') {
|
||||
$juryLecteurs[] = [
|
||||
'name' => $name,
|
||||
'is_external' => !empty($formData["jury_lecteurs_ext:$i"]) ? 1 : 0,
|
||||
];
|
||||
}
|
||||
}
|
||||
$helpContent = $helpFn('fieldset_jury');
|
||||
include APP_ROOT . '/templates/partials/form/form-help-block.php';
|
||||
require APP_ROOT . '/templates/partials/form/jury-fieldset.php';
|
||||
?>
|
||||
<!-- ═══════════════════ Langue(s) ═══════════════════ -->
|
||||
<fieldset>
|
||||
<legend>Langue(s)</legend>
|
||||
<?php $name = 'languages'; $label = 'Langue(s) du TFE :'; $options = $languages; $checked = $formData['languages'] ?? []; $required = true; include APP_ROOT . '/templates/partials/form/checkbox-list.php'; ?>
|
||||
<?php $name = 'language_autre'; $label = 'Autre(s) langue(s) :'; $value = old($formData, 'language_autre'); $hint = 'Si votre TFE contient une langue absente de la liste, précisez-la ici.'; include APP_ROOT . '/templates/partials/form/text-field.php'; ?>
|
||||
</fieldset>
|
||||
|
||||
<!-- ═══════════════════ Format(s) ═══════════════════ -->
|
||||
<fieldset>
|
||||
<legend>Format(s)</legend>
|
||||
<?php $name = 'formats'; $label = 'Format(s) du TFE :'; $options = $formatTypes; $checked = $formData['formats'] ?? []; include APP_ROOT . '/templates/partials/form/checkbox-list.php'; ?>
|
||||
</fieldset>
|
||||
|
||||
<!-- ═══════════════════ Mots-clés ═══════════════════ -->
|
||||
<fieldset>
|
||||
<legend>Mots-clés</legend>
|
||||
<?php
|
||||
$name = 'tag'; $label = 'Mots-clés (max 10) :'; $value = old($formData, 'tag');
|
||||
$placeholder = 'sociologie, anthropologie, ...';
|
||||
$hint = 'Séparez par des virgules. Max 10 mots-clés.';
|
||||
include APP_ROOT . '/templates/partials/form/text-field.php';
|
||||
?>
|
||||
</fieldset>
|
||||
|
||||
<!-- ═══════════════════ Cadre académique ═══════════════════ -->
|
||||
<?php
|
||||
@@ -324,6 +326,29 @@ function renderShareLinkForm(string $slug, array $link): void
|
||||
include APP_ROOT . '/templates/partials/form/fieldset-academic.php';
|
||||
?>
|
||||
|
||||
<!-- ═══════════════════ Composition du jury ═══════════════════ -->
|
||||
<?php
|
||||
$juryPromoteur = old($formData, 'jury_promoteur');
|
||||
$juryPromoteurUlb = old($formData, 'jury_promoteur_ulb_name');
|
||||
$lecteursInternes = [];
|
||||
$lecteursExternes = [];
|
||||
for ($i = 0; $i < 10; $i++) {
|
||||
$n = old($formData, "jury_lecteur_interne:$i");
|
||||
if ($n !== '') $lecteursInternes[] = ['name' => $n];
|
||||
}
|
||||
for ($i = 0; $i < 10; $i++) {
|
||||
$n = old($formData, "jury_lecteur_externe:$i");
|
||||
if ($n !== '') $lecteursExternes[] = ['name' => $n];
|
||||
}
|
||||
$juryPresident = null;
|
||||
$showPresident = false;
|
||||
$showPromoteurUlb = true;
|
||||
$promoteurUlbConditional = true;
|
||||
$helpContent = $helpFn('fieldset_jury');
|
||||
include APP_ROOT . '/templates/partials/form/form-help-block.php';
|
||||
require APP_ROOT . '/templates/partials/form/jury-fieldset.php';
|
||||
?>
|
||||
|
||||
<!-- ═══════════════════ Fichiers ═══════════════════ -->
|
||||
<?php
|
||||
$helpContent = $helpFn('fieldset_files');
|
||||
@@ -333,17 +358,22 @@ function renderShareLinkForm(string $slug, array $link): void
|
||||
|
||||
<!-- ═══════════════════ Métadonnées complémentaires ═══════════════════ -->
|
||||
<?php
|
||||
$oldFn = $shareOldFn;
|
||||
$withAutofocusFn = $shareWithAutofocusFn;
|
||||
$showDescription = false;
|
||||
$defaultAccessTypeId = 2;
|
||||
$helpContent = $helpFn('fieldset_access');
|
||||
include APP_ROOT . '/templates/partials/form/form-help-block.php';
|
||||
$oldFn = $shareOldFn;
|
||||
$withAutofocusFn = $shareWithAutofocusFn;
|
||||
include APP_ROOT . '/templates/partials/form/fieldset-metadata.php';
|
||||
?>
|
||||
|
||||
<!-- ═══════════════════ Degrés d'ouverture et licences ═══════════════════ -->
|
||||
<?php include APP_ROOT . '/templates/partials/form/fieldset-licence-explanation.php'; ?>
|
||||
<?php
|
||||
$libreEnabled = ($siteSettings['access_type_libre_enabled'] ?? '0') === '1';
|
||||
$interneEnabled = ($siteSettings['access_type_interne_enabled'] ?? '1') === '1';
|
||||
$interditEnabled = ($siteSettings['access_type_interdit_enabled'] ?? '1') === '1';
|
||||
$generalitiesHtml = $helpFn('fieldset_generalites');
|
||||
$defaultAccessTypeId = 2;
|
||||
$helpContent = $helpFn('fieldset_access');
|
||||
include APP_ROOT . '/templates/partials/form/form-help-block.php';
|
||||
include APP_ROOT . '/templates/partials/form/fieldset-licence-explanation.php';
|
||||
?>
|
||||
|
||||
<!-- ═══════════════════ E-mail de confirmation ═══════════ -->
|
||||
<fieldset>
|
||||
@@ -351,7 +381,7 @@ function renderShareLinkForm(string $slug, array $link): void
|
||||
<?php $helpContent = $helpFn('fieldset_email'); include APP_ROOT . '/templates/partials/form/form-help-block.php'; ?>
|
||||
<?php
|
||||
$name = 'confirmation_email';
|
||||
$label = 'Adresse e-mail * :';
|
||||
$label = 'Adresse e-mail :';
|
||||
$value = old($formData, 'confirmation_email');
|
||||
$type = 'email';
|
||||
$required = true;
|
||||
|
||||
@@ -183,8 +183,9 @@ class ThesisCreateController
|
||||
'synopsis' => $data['synopsis'],
|
||||
'file_size_info' => $data['durationInfo'],
|
||||
'baiu_link' => $data['lien'],
|
||||
'license_id' => $data['licenseId'],
|
||||
'access_type_id' => $data['accessTypeId'],
|
||||
'license_id' => $data['licenseId'],
|
||||
'license_custom' => $data['licenseCustom'],
|
||||
'access_type_id' => $data['accessTypeId'],
|
||||
'objet' => $data['objet'],
|
||||
]);
|
||||
|
||||
@@ -310,31 +311,65 @@ class ThesisCreateController
|
||||
$subtitle = $this->sanitiseString($post['subtitle'] ?? '');
|
||||
$synopsis = $this->validateRequired($this->sanitiseString($post['synopsis'] ?? ''), 'Synopsis');
|
||||
|
||||
$durationInfo = $this->sanitiseString($post['duration_info'] ?? '');
|
||||
|
||||
// Jury members
|
||||
$juryMembers = [];
|
||||
if (!empty(trim($post['jury_president'] ?? ''))) {
|
||||
$juryMembers[] = ['name' => trim($post['jury_president']), 'role' => 'president', 'is_external' => 0];
|
||||
$durationInfo = $this->sanitiseString($post['duration_pages'] ?? '');
|
||||
$durationMinutes = $this->sanitiseString($post['duration_minutes'] ?? '');
|
||||
if ($durationInfo !== '' && $durationMinutes !== '') {
|
||||
$durationInfo = $durationInfo . ' pages + ' . $durationMinutes . ' minutes';
|
||||
} elseif ($durationMinutes !== '') {
|
||||
$durationInfo = $durationMinutes . ' minutes';
|
||||
} elseif ($durationInfo !== '') {
|
||||
$durationInfo = $durationInfo . ' pages';
|
||||
}
|
||||
if (!empty($post['has_annexes'])) {
|
||||
$durationInfo = $durationInfo ? $durationInfo . ' + annexe(s)' : 'Annexe(s)';
|
||||
}
|
||||
|
||||
// Jury members — new structure: separate interne/externe lecteurs
|
||||
$juryMembers = [];
|
||||
if (!empty(trim($post['jury_promoteur'] ?? ''))) {
|
||||
$juryMembers[] = [
|
||||
'name' => trim($post['jury_promoteur']),
|
||||
'role' => 'promoteur',
|
||||
'is_external' => isset($post['jury_promoteur_ext']) ? 1 : 0,
|
||||
'is_ulb' => isset($post['jury_promoteur_ulb']) ? 1 : 0,
|
||||
'is_external' => 0,
|
||||
'is_ulb' => 0,
|
||||
];
|
||||
}
|
||||
foreach ($post['jury_lecteurs'] ?? [] as $i => $name) {
|
||||
if (!empty(trim($post['jury_promoteur_ulb_name'] ?? ''))) {
|
||||
$juryMembers[] = [
|
||||
'name' => trim($post['jury_promoteur_ulb_name']),
|
||||
'role' => 'promoteur',
|
||||
'is_external' => 1,
|
||||
'is_ulb' => 1,
|
||||
];
|
||||
}
|
||||
foreach ($post['jury_lecteur_interne'] ?? [] as $name) {
|
||||
$name = trim($name);
|
||||
if ($name !== '') {
|
||||
$juryMembers[] = [
|
||||
'name' => $name,
|
||||
'role' => 'lecteur',
|
||||
'is_external' => isset($post['jury_lecteurs_ext'][$i]) ? 1 : 0,
|
||||
];
|
||||
$juryMembers[] = ['name' => $name, 'role' => 'lecteur', 'is_external' => 0];
|
||||
}
|
||||
}
|
||||
foreach ($post['jury_lecteur_externe'] ?? [] as $name) {
|
||||
$name = trim($name);
|
||||
if ($name !== '') {
|
||||
$juryMembers[] = ['name' => $name, 'role' => 'lecteur', 'is_external' => 1];
|
||||
}
|
||||
}
|
||||
// Keep backwards compat with old jury_lecteurs (from old-style forms)
|
||||
if (empty($juryMembers) || isset($post['jury_lecteurs'])) {
|
||||
foreach ($post['jury_lecteurs'] ?? [] as $i => $name) {
|
||||
$name = trim($name);
|
||||
if ($name !== '') {
|
||||
$juryMembers[] = [
|
||||
'name' => $name,
|
||||
'role' => 'lecteur',
|
||||
'is_external' => isset($post['jury_lecteurs_ext'][$i]) ? 1 : 0,
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!empty(trim($post['jury_president'] ?? ''))) {
|
||||
$juryMembers[] = ['name' => trim($post['jury_president']), 'role' => 'president', 'is_external' => 0];
|
||||
}
|
||||
|
||||
// Keywords (max 10)
|
||||
$tagRaw = $this->sanitiseString($post['tag'] ?? '');
|
||||
@@ -357,6 +392,7 @@ class ThesisCreateController
|
||||
: [];
|
||||
|
||||
$licenseId = filter_var($post['license_id'] ?? '', FILTER_VALIDATE_INT) ?: null;
|
||||
$licenseCustom = trim($post['license_custom'] ?? '');
|
||||
|
||||
// Access type — must be one of the enabled types; default 2 (Interne)
|
||||
$accessTypeId = filter_var($post['access_type_id'] ?? '', FILTER_VALIDATE_INT);
|
||||
@@ -404,6 +440,7 @@ class ThesisCreateController
|
||||
'languageIds',
|
||||
'formatIds',
|
||||
'licenseId',
|
||||
'licenseCustom',
|
||||
'lien',
|
||||
'accessTypeId',
|
||||
'objet'
|
||||
|
||||
@@ -175,7 +175,7 @@ class ThesisEditController
|
||||
'finality_id' => intval($post['finality'] ?? 0),
|
||||
'synopsis' => trim($post['synopsis'] ?? ''),
|
||||
'context_note' => trim($post['context_note'] ?? ''),
|
||||
'file_size_info' => trim($post['duration_info'] ?? ''),
|
||||
'file_size_info' => $this->buildFileSizeInfo($post),
|
||||
'baiu_link' => trim($post['lien'] ?? ''),
|
||||
'license_id' => filter_var($post['license_id'] ?? '', FILTER_VALIDATE_INT) ?: null,
|
||||
'access_type_id' => filter_var($post['access_type_id'] ?? '', FILTER_VALIDATE_INT) ?: null,
|
||||
@@ -184,7 +184,8 @@ class ThesisEditController
|
||||
'jury_points' => $post['jury_points'] ?? null,
|
||||
'exemplaire_baiu' => !empty($post['exemplaire_baiu']),
|
||||
'exemplaire_erg' => !empty($post['exemplaire_erg']),
|
||||
'cc4r' => !empty($post['cc4r']),
|
||||
'cc4r' => !empty($post['cc2r']),
|
||||
'license_custom' => trim($post['license_custom'] ?? ''),
|
||||
]);
|
||||
|
||||
// ── 2. Authors (alphabetically sorted) ─────────────────────────────
|
||||
@@ -545,6 +546,43 @@ class ThesisEditController
|
||||
{
|
||||
$members = [];
|
||||
|
||||
// Promoteur interne
|
||||
if (!empty(trim($post['jury_promoteur'] ?? ''))) {
|
||||
$members[] = [
|
||||
'name' => trim($post['jury_promoteur']),
|
||||
'role' => 'promoteur',
|
||||
'is_external' => 0,
|
||||
'is_ulb' => 0,
|
||||
];
|
||||
}
|
||||
|
||||
// Promoteur ULB
|
||||
if (!empty(trim($post['jury_promoteur_ulb_name'] ?? ''))) {
|
||||
$members[] = [
|
||||
'name' => trim($post['jury_promoteur_ulb_name']),
|
||||
'role' => 'promoteur',
|
||||
'is_external' => 1,
|
||||
'is_ulb' => 1,
|
||||
];
|
||||
}
|
||||
|
||||
// Lecteurs internes
|
||||
foreach ($post['jury_lecteur_interne'] ?? [] as $name) {
|
||||
$name = trim($name);
|
||||
if ($name !== '') {
|
||||
$members[] = ['name' => $name, 'role' => 'lecteur', 'is_external' => 0];
|
||||
}
|
||||
}
|
||||
|
||||
// Lecteurs externes
|
||||
foreach ($post['jury_lecteur_externe'] ?? [] as $name) {
|
||||
$name = trim($name);
|
||||
if ($name !== '') {
|
||||
$members[] = ['name' => $name, 'role' => 'lecteur', 'is_external' => 1];
|
||||
}
|
||||
}
|
||||
|
||||
// President (optional, admin-only)
|
||||
if (!empty(trim($post['jury_president'] ?? ''))) {
|
||||
$members[] = [
|
||||
'name' => trim($post['jury_president']),
|
||||
@@ -553,26 +591,41 @@ class ThesisEditController
|
||||
];
|
||||
}
|
||||
|
||||
if (!empty(trim($post['jury_promoteur'] ?? ''))) {
|
||||
$members[] = [
|
||||
'name' => trim($post['jury_promoteur']),
|
||||
'role' => 'promoteur',
|
||||
'is_external' => isset($post['jury_promoteur_ext']) ? 1 : 0,
|
||||
'is_ulb' => isset($post['jury_promoteur_ulb']) ? 1 : 0,
|
||||
];
|
||||
}
|
||||
|
||||
foreach ($post['jury_lecteurs'] ?? [] as $i => $name) {
|
||||
$name = trim($name);
|
||||
if ($name !== '') {
|
||||
$members[] = [
|
||||
'name' => $name,
|
||||
'role' => 'lecteur',
|
||||
'is_external' => isset($post['jury_lecteurs_ext'][$i]) ? 1 : 0,
|
||||
];
|
||||
// Backwards compat: old jury_lecteurs[]
|
||||
if (isset($post['jury_lecteurs'])) {
|
||||
foreach ($post['jury_lecteurs'] ?? [] as $i => $name) {
|
||||
$name = trim($name);
|
||||
if ($name !== '') {
|
||||
$members[] = [
|
||||
'name' => $name,
|
||||
'role' => 'lecteur',
|
||||
'is_external' => isset($post['jury_lecteurs_ext'][$i]) ? 1 : 0,
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $members;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build file_size_info from separate duration fields.
|
||||
*/
|
||||
private function buildFileSizeInfo(array $post): string
|
||||
{
|
||||
$pages = trim($post['duration_pages'] ?? '');
|
||||
$minutes = trim($post['duration_minutes'] ?? '');
|
||||
$info = '';
|
||||
if ($pages !== '' && $minutes !== '') {
|
||||
$info = $pages . ' pages + ' . $minutes . ' minutes';
|
||||
} elseif ($minutes !== '') {
|
||||
$info = $minutes . ' minutes';
|
||||
} elseif ($pages !== '') {
|
||||
$info = $pages . ' pages';
|
||||
}
|
||||
if (!empty($post['has_annexes'])) {
|
||||
$info = $info ? $info . ' + annexe(s)' : 'Annexe(s)';
|
||||
}
|
||||
return $info;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1651,7 +1651,7 @@ class Database
|
||||
public function getThesisRawFields(int $thesisId): ?array
|
||||
{
|
||||
$stmt = $this->pdo->prepare(
|
||||
'SELECT license_id, access_type_id, context_note, remarks, jury_points, exemplaire_baiu, exemplaire_erg, cc4r FROM theses WHERE id = ? LIMIT 1'
|
||||
'SELECT license_id, license_custom, access_type_id, context_note, remarks, jury_points, exemplaire_baiu, exemplaire_erg, cc4r FROM theses WHERE id = ? LIMIT 1'
|
||||
);
|
||||
$stmt->execute([$thesisId]);
|
||||
$row = $stmt->fetch();
|
||||
@@ -1783,6 +1783,7 @@ class Database
|
||||
file_size_info = ?,
|
||||
baiu_link = ?,
|
||||
license_id = ?,
|
||||
license_custom = ?,
|
||||
access_type_id = ?,
|
||||
is_published = ?,
|
||||
remarks = ?,
|
||||
@@ -1805,6 +1806,7 @@ class Database
|
||||
!empty($data['file_size_info']) ? $data['file_size_info'] : null,
|
||||
!empty($data['baiu_link']) ? $data['baiu_link'] : null,
|
||||
$data['license_id'] ?? null,
|
||||
!empty($data['license_custom']) ? $data['license_custom'] : null,
|
||||
$data['access_type_id'] ?? null,
|
||||
$data['is_published'] ? 1 : 0,
|
||||
!empty($data['remarks']) ? $data['remarks'] : null,
|
||||
@@ -1847,12 +1849,12 @@ class Database
|
||||
identifier, title, subtitle, year,
|
||||
orientation_id, ap_program_id, finality_id,
|
||||
synopsis, file_size_info,
|
||||
baiu_link, license_id,
|
||||
baiu_link, license_id, license_custom,
|
||||
access_type_id,
|
||||
objet,
|
||||
is_published,
|
||||
submitted_at
|
||||
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 0, CURRENT_TIMESTAMP)
|
||||
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 0, CURRENT_TIMESTAMP)
|
||||
');
|
||||
|
||||
$validObjet = ['tfe', 'thèse', 'frart'];
|
||||
@@ -1870,6 +1872,7 @@ class Database
|
||||
!empty($data['file_size_info']) ? $data['file_size_info'] : null,
|
||||
!empty($data['baiu_link']) ? $data['baiu_link'] : null,
|
||||
$data['license_id'] ?? null,
|
||||
!empty($data['license_custom']) ? $data['license_custom'] : null,
|
||||
isset($data['access_type_id']) ? (int)$data['access_type_id'] : 2, // default: Interne
|
||||
$objet,
|
||||
]);
|
||||
|
||||
@@ -10,3 +10,4 @@
|
||||
{"timestamp":"2026-05-05T16:40:13+00:00","ip":"127.0.0.1","user_agent":"Mozilla/5.0 (X11; Linux x86_64; rv:150.0) Gecko/20100101 Firefox/150.0","resource":"system","action":"delete_all_theses","status":"success","context":{"count":13}}
|
||||
{"timestamp":"2026-05-05T16:57:57+00:00","ip":"127.0.0.1","user_agent":"Mozilla/5.0 (X11; Linux x86_64; rv:150.0) Gecko/20100101 Firefox/150.0","resource":"thesis","action":"publish","status":"success","context":{"count":15,"ids":[53,52,51,50,49,48,47,46,45,44,43,42,41,40,39]}}
|
||||
{"timestamp":"2026-05-05T16:58:02+00:00","ip":"127.0.0.1","user_agent":"Mozilla/5.0 (X11; Linux x86_64; rv:150.0) Gecko/20100101 Firefox/150.0","resource":"thesis","action":"publish","status":"success","context":{"count":25,"ids":[178,177,176,175,174,173,172,171,170,169,168,167,166,165,164,163,162,161,160,159,158,157,156,155,154]}}
|
||||
{"timestamp":"2026-05-07T16:15:27+00:00","ip":"127.0.0.1","user_agent":"Mozilla/5.0 (X11; Linux x86_64; rv:150.0) Gecko/20100101 Firefox/150.0","resource":"system","action":"files_export","status":"success","context":{"file_count":0,"byte_size":248}}
|
||||
|
||||
@@ -183,6 +183,7 @@ CREATE TABLE IF NOT EXISTS theses (
|
||||
-- Access and licensing
|
||||
access_type_id INTEGER,
|
||||
license_id INTEGER,
|
||||
license_custom TEXT, -- free-text licence (if not in predefined list)
|
||||
|
||||
-- Jury information
|
||||
jury_points DECIMAL(4,2), -- Points out of 20 (backoffice only)
|
||||
@@ -205,7 +206,7 @@ CREATE TABLE IF NOT EXISTS theses (
|
||||
exemplaire_erg BOOLEAN DEFAULT 0, -- Physical copy at ERG
|
||||
|
||||
-- CC4r acceptance (collected in student form)
|
||||
cc4r BOOLEAN DEFAULT 0
|
||||
cc4r BOOLEAN DEFAULT 0,
|
||||
|
||||
-- Timestamps
|
||||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
@@ -527,6 +528,7 @@ SELECT
|
||||
at.name as access_type,
|
||||
lt.name as license_type,
|
||||
t.license_id,
|
||||
t.license_custom,
|
||||
t.access_type_id,
|
||||
t.jury_points,
|
||||
t.submitted_at,
|
||||
|
||||
@@ -16,8 +16,30 @@
|
||||
include APP_ROOT . '/templates/partials/form/fieldset-tfe-info.php';
|
||||
?>
|
||||
|
||||
<!-- ═══════════════════ Composition du jury ═══════════════════ -->
|
||||
<?php require APP_ROOT . '/templates/partials/form/jury-fieldset.php'; ?>
|
||||
<!-- ═══════════════════ Langue(s) ═══════════════════ -->
|
||||
<fieldset>
|
||||
<legend>Langue(s)</legend>
|
||||
<?php $name = 'languages'; $label = 'Langue(s) du TFE :'; $options = $languages; $checked = $formData['languages'] ?? []; $required = true; include APP_ROOT . '/templates/partials/form/checkbox-list.php'; ?>
|
||||
<?php $name = 'language_autre'; $label = 'Autre(s) langue(s) :'; $value = old('language_autre'); $hint = 'Si votre TFE contient une langue absente de la liste, précisez-la ici.'; include APP_ROOT . '/templates/partials/form/text-field.php'; ?>
|
||||
</fieldset>
|
||||
|
||||
<!-- ═══════════════════ Format(s) ═══════════════════ -->
|
||||
<fieldset>
|
||||
<legend>Format(s)</legend>
|
||||
<?php $name = 'formats'; $label = 'Format(s) du TFE :'; $options = $formatTypes; $checked = $formData['formats'] ?? []; include APP_ROOT . '/templates/partials/form/checkbox-list.php'; ?>
|
||||
</fieldset>
|
||||
|
||||
<!-- ═══════════════════ Mots-clés ═══════════════════ -->
|
||||
<fieldset>
|
||||
<legend>Mots-clés</legend>
|
||||
<?php
|
||||
$name = 'tag'; $label = 'Mots-clés (max 10) :'; $value = old('tag');
|
||||
$placeholder = 'sociologie, anthropologie, ...';
|
||||
$hint = 'Séparez par des virgules. Max 10 mots-clés.';
|
||||
$attrs = withAutofocus('tag');
|
||||
include APP_ROOT . '/templates/partials/form/text-field.php';
|
||||
?>
|
||||
</fieldset>
|
||||
|
||||
<!-- ═══════════════════ Cadre académique ═══════════════════ -->
|
||||
<?php
|
||||
@@ -26,6 +48,19 @@
|
||||
include APP_ROOT . '/templates/partials/form/fieldset-academic.php';
|
||||
?>
|
||||
|
||||
<!-- ═══════════════════ Composition du jury ═══════════════════ -->
|
||||
<?php
|
||||
$juryPromoteur = null;
|
||||
$juryPromoteurUlb = null;
|
||||
$lecteursInternes = [];
|
||||
$lecteursExternes = [];
|
||||
$juryPresident = null;
|
||||
$showPresident = false;
|
||||
$showPromoteurUlb = true;
|
||||
$promoteurUlbConditional = false;
|
||||
require APP_ROOT . '/templates/partials/form/jury-fieldset.php';
|
||||
?>
|
||||
|
||||
<!-- ═══════════════════ Fichiers ═══════════════════ -->
|
||||
<?php include APP_ROOT . '/templates/partials/form/fieldset-files.php'; ?>
|
||||
|
||||
@@ -33,12 +68,19 @@
|
||||
<?php
|
||||
$oldFn = 'old';
|
||||
$withAutofocusFn = 'withAutofocus';
|
||||
$showDescription = false;
|
||||
include APP_ROOT . '/templates/partials/form/fieldset-metadata.php';
|
||||
?>
|
||||
|
||||
<!-- ═══════════════════ Degrés d'ouverture et licences ═══════════════════ -->
|
||||
<?php include APP_ROOT . '/templates/partials/form/fieldset-licence-explanation.php'; ?>
|
||||
<?php
|
||||
$formData = $_SESSION['form_data'] ?? [];
|
||||
$libreEnabled = ($siteSettings['access_type_libre_enabled'] ?? '0') === '1';
|
||||
$interneEnabled = ($siteSettings['access_type_interne_enabled'] ?? '1') === '1';
|
||||
$interditEnabled = ($siteSettings['access_type_interdit_enabled'] ?? '1') === '1';
|
||||
$generalitiesHtml = $helpBlocks['fieldset_generalites'] ?? '';
|
||||
$defaultAccessTypeId = 2;
|
||||
include APP_ROOT . '/templates/partials/form/fieldset-licence-explanation.php';
|
||||
?>
|
||||
|
||||
<!-- ═══════════════════ E-mail de confirmation ═══════════════ -->
|
||||
<fieldset>
|
||||
|
||||
@@ -12,9 +12,7 @@
|
||||
|
||||
<!-- ═══════════════════ Informations du TFE ═══════════════════ -->
|
||||
<?php
|
||||
// Build a merged $formData that includes contact_public for the fieldset partial.
|
||||
$formData = array_merge($formData ?? [], ['contact_public' => $currentAuthorShowContact ?? false]);
|
||||
// Build old()-compatible callable from the already-loaded $thesis data.
|
||||
$editOldFn = function (string $key, string $default = '') use ($thesis, $formData, $currentAuthorEmail) {
|
||||
if (!empty($formData[$key])) return htmlspecialchars($formData[$key]);
|
||||
$map = [
|
||||
@@ -35,30 +33,56 @@
|
||||
$oldFn = $editOldFn;
|
||||
$withAutofocusFn = $editWithAutofocusFn;
|
||||
include APP_ROOT . '/templates/partials/form/fieldset-tfe-info.php';
|
||||
// Restore $formData to original for downstream partials.
|
||||
$formData = $_SESSION['form_data'] ?? [];
|
||||
?>
|
||||
|
||||
<!-- ═══════════════════ Composition du jury ═══════════════════ -->
|
||||
<?php
|
||||
$juryPresident = null;
|
||||
$juryPromoteur = null;
|
||||
$juryPromoteurExt = 0;
|
||||
$juryPromoteurUlb = 0;
|
||||
$juryLecteurs = [];
|
||||
foreach ($jury as $jm) {
|
||||
if ($jm['role'] === 'president') {
|
||||
$juryPresident = $jm['name'];
|
||||
} elseif ($jm['role'] === 'promoteur') {
|
||||
$juryPromoteur = $jm['name'];
|
||||
$juryPromoteurExt = (int)$jm['is_external'];
|
||||
$juryPromoteurUlb = (int)($jm['is_ulb'] ?? 0);
|
||||
} elseif ($jm['role'] === 'lecteur') {
|
||||
$juryLecteurs[] = $jm;
|
||||
}
|
||||
}
|
||||
require APP_ROOT . '/templates/partials/form/jury-fieldset.php';
|
||||
?>
|
||||
<!-- Contact public checkbox (admin) -->
|
||||
<fieldset>
|
||||
<legend>Contact</legend>
|
||||
<div class="admin-form-group">
|
||||
<label class="admin-checkbox-label">
|
||||
<input type="checkbox" name="contact_public" value="1"
|
||||
<?= !empty($formData['contact_public']) || $currentAuthorShowContact ? 'checked' : '' ?>>
|
||||
Rendre le contact visible publiquement sur la fiche du TFE
|
||||
</label>
|
||||
<small>L'adresse est toujours conservée en interne comme contact de référence.</small>
|
||||
</div>
|
||||
</fieldset>
|
||||
|
||||
<!-- ═══════════════════ Langue(s) ═══════════════════ -->
|
||||
<fieldset>
|
||||
<legend>Langue(s)</legend>
|
||||
<?php
|
||||
$checkedLanguages = $formData['languages'] ?? $currentLanguages;
|
||||
$name = 'languages'; $label = 'Langue(s) du TFE :'; $options = $languages; $checked = $checkedLanguages; $required = true;
|
||||
include APP_ROOT . '/templates/partials/form/checkbox-list.php';
|
||||
?>
|
||||
<?php $name = 'language_autre'; $label = 'Autre(s) langue(s) :'; $value = old('language_autre'); include APP_ROOT . '/templates/partials/form/text-field.php'; ?>
|
||||
</fieldset>
|
||||
|
||||
<!-- ═══════════════════ Format(s) ═══════════════════ -->
|
||||
<fieldset>
|
||||
<legend>Format(s)</legend>
|
||||
<?php
|
||||
$checkedFormats = $formData['formats'] ?? $currentFormats;
|
||||
$name = 'formats'; $label = 'Format(s) du TFE :'; $options = $formatTypes; $checked = $checkedFormats;
|
||||
include APP_ROOT . '/templates/partials/form/checkbox-list.php';
|
||||
?>
|
||||
</fieldset>
|
||||
|
||||
<!-- ═══════════════════ Mots-clés ═══════════════════ -->
|
||||
<fieldset>
|
||||
<legend>Mots-clés</legend>
|
||||
<?php
|
||||
$editKwFormData = ['tag' => $thesis['keywords'] ?? ''];
|
||||
$editKwOldFn = fn(string $key, string $default = '') => isset($editKwFormData[$key]) ? htmlspecialchars((string)$editKwFormData[$key]) : $default;
|
||||
$oldFn = $editKwOldFn; $withAutofocusFn = $editWithAutofocusFn; $formData = $editKwFormData;
|
||||
$name = 'tag'; $label = 'Mots-clés (max 10) :'; $value = $editKwOldFn('tag');
|
||||
$placeholder = 'sociologie, anthropologie, ...';
|
||||
$hint = 'Séparez par des virgules. Max 10 mots-clés.';
|
||||
include APP_ROOT . '/templates/partials/form/text-field.php';
|
||||
?>
|
||||
</fieldset>
|
||||
|
||||
<!-- ═══════════════════ Cadre académique ═══════════════════ -->
|
||||
<?php
|
||||
@@ -67,14 +91,10 @@
|
||||
'orientation' => $thesis['orientation'],
|
||||
'ap' => $thesis['ap_program'],
|
||||
'finality' => $thesis['finality_type'],
|
||||
'languages' => $currentLanguages,
|
||||
'formats' => $currentFormats,
|
||||
'tag' => $thesis['keywords'] ?? '',
|
||||
];
|
||||
$editAcademicOldFn = function (string $key, string $default = '') use ($editFormData) {
|
||||
return isset($editFormData[$key]) && !is_array($editFormData[$key])
|
||||
? htmlspecialchars((string)$editFormData[$key])
|
||||
: $default;
|
||||
? htmlspecialchars((string)$editFormData[$key]) : $default;
|
||||
};
|
||||
$oldFn = $editAcademicOldFn;
|
||||
$withAutofocusFn = $editWithAutofocusFn;
|
||||
@@ -82,6 +102,36 @@
|
||||
include APP_ROOT . '/templates/partials/form/fieldset-academic.php';
|
||||
?>
|
||||
|
||||
<!-- ═══════════════════ Composition du jury ═══════════════════ -->
|
||||
<?php
|
||||
$juryPromoteur = null;
|
||||
$juryPromoteurUlb = null;
|
||||
$lecteursInternes = [];
|
||||
$lecteursExternes = [];
|
||||
$juryPresident = null;
|
||||
foreach ($jury as $jm) {
|
||||
if ($jm['role'] === 'president') {
|
||||
$juryPresident = $jm['name'];
|
||||
} elseif ($jm['role'] === 'promoteur') {
|
||||
if (($jm['is_ulb'] ?? 0) == 1) {
|
||||
$juryPromoteurUlb = $jm['name'];
|
||||
} else {
|
||||
$juryPromoteur = $jm['name'];
|
||||
}
|
||||
} elseif ($jm['role'] === 'lecteur') {
|
||||
if (($jm['is_external'] ?? 0) == 1) {
|
||||
$lecteursExternes[] = $jm;
|
||||
} else {
|
||||
$lecteursInternes[] = $jm;
|
||||
}
|
||||
}
|
||||
}
|
||||
$showPresident = true;
|
||||
$showPromoteurUlb = true;
|
||||
$promoteurUlbConditional = false;
|
||||
require APP_ROOT . '/templates/partials/form/jury-fieldset.php';
|
||||
?>
|
||||
|
||||
<!-- ═══════════════════ Fichiers ═══════════════════ -->
|
||||
<fieldset>
|
||||
<legend>Fichiers</legend>
|
||||
@@ -101,7 +151,7 @@
|
||||
<?php endif; ?>
|
||||
<input type="file" id="couverture" name="couverture" accept="image/jpeg,image/png" data-preview="fp-couverture">
|
||||
<div id="fp-couverture" class="file-preview-list" aria-live="polite"></div>
|
||||
<small><?= empty($currentCover) ? 'JPG, PNG. Max 20 MB.' : 'Laisser vide pour conserver la couverture actuelle. JPG, PNG. Max 20 MB.' ?></small>
|
||||
<small><?= empty($currentCover) ? 'JPG, PNG. Format 4:3 recommandé. Max 20 MB.' : 'Laisser vide pour conserver la couverture actuelle. JPG, PNG. Max 20 MB.' ?></small>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -127,13 +177,9 @@
|
||||
};
|
||||
?>
|
||||
<li class="admin-file-list-item" data-file-id="<?= (int)$f['id'] ?>">
|
||||
<!-- Hidden field carries sort order (updated by JS) -->
|
||||
<input type="hidden" name="file_sort_order[]" value="<?= (int)$f['id'] ?>">
|
||||
|
||||
<span class="admin-file-drag-handle" title="Réordonner">⠿</span>
|
||||
|
||||
<span class="admin-file-icon-col"><?= $fIcon ?></span>
|
||||
|
||||
<span class="admin-file-info">
|
||||
<a href="/media.php?path=<?= urlencode($f['file_path']) ?>" target="_blank" rel="noopener" class="admin-file-name">
|
||||
<?= htmlspecialchars($f['file_name'] ?? basename($f['file_path'])) ?>
|
||||
@@ -144,16 +190,13 @@
|
||||
<span class="admin-file-size"><?= number_format($f['file_size'] / 1024 / 1024, 2) ?> MB</span>
|
||||
<?php endif; ?>
|
||||
</span>
|
||||
<input type="text"
|
||||
name="file_label[<?= (int)$f['id'] ?>]"
|
||||
<input type="text" name="file_label[<?= (int)$f['id'] ?>]"
|
||||
value="<?= htmlspecialchars($f['display_label'] ?? '') ?>"
|
||||
placeholder="Légende / description (optionnel)"
|
||||
class="admin-file-label-input">
|
||||
</span>
|
||||
|
||||
<label class="admin-checkbox-label admin-file-delete">
|
||||
<input type="checkbox" name="delete_files[]" value="<?= (int)$f['id'] ?>">
|
||||
Supprimer
|
||||
<input type="checkbox" name="delete_files[]" value="<?= (int)$f['id'] ?>"> Supprimer
|
||||
</label>
|
||||
</li>
|
||||
<?php endforeach; ?>
|
||||
@@ -183,8 +226,7 @@
|
||||
<div class="admin-file-input">
|
||||
<?php if (!empty($thesis['banner_path'])): ?>
|
||||
<div class="admin-banner-preview">
|
||||
<img src="/media.php?path=<?= urlencode($thesis['banner_path']) ?>"
|
||||
alt="Bannière actuelle">
|
||||
<img src="/media.php?path=<?= urlencode($thesis['banner_path']) ?>" alt="Bannière actuelle">
|
||||
<label class="admin-checkbox-label">
|
||||
<input type="checkbox" name="remove_banner" value="1"> Supprimer la bannière
|
||||
</label>
|
||||
@@ -200,24 +242,47 @@
|
||||
<!-- ═══════════════════ Métadonnées complémentaires ═══════════════════ -->
|
||||
<?php
|
||||
$editMetaFormData = [
|
||||
'license_id' => $currentLicenseId,
|
||||
'duration_info' => $thesis['file_size_info'] ?? '',
|
||||
'lien' => $thesis['baiu_link'] ?? '',
|
||||
'access_type_id' => $currentAccessTypeId,
|
||||
'duration_pages' => $currentRaw['duration_pages'] ?? '',
|
||||
'duration_minutes' => $currentRaw['duration_minutes'] ?? '',
|
||||
'lien' => $thesis['baiu_link'] ?? '',
|
||||
];
|
||||
$editMetaOldFn = function (string $key, string $default = '') use ($editMetaFormData) {
|
||||
return isset($editMetaFormData[$key])
|
||||
? htmlspecialchars((string)$editMetaFormData[$key])
|
||||
: $default;
|
||||
return isset($editMetaFormData[$key]) ? htmlspecialchars((string)$editMetaFormData[$key]) : $default;
|
||||
};
|
||||
$oldFn = $editMetaOldFn;
|
||||
$withAutofocusFn = $editWithAutofocusFn;
|
||||
$formData = $editMetaFormData;
|
||||
$defaultAccessTypeId = $currentAccessTypeId;
|
||||
$showDescription = true;
|
||||
$oldFn = $editMetaOldFn;
|
||||
$withAutofocusFn = $editWithAutofocusFn;
|
||||
$formData = $editMetaFormData;
|
||||
include APP_ROOT . '/templates/partials/form/fieldset-metadata.php';
|
||||
?>
|
||||
|
||||
<!-- ═══════════════════ Degrés d'ouverture et licences ═══════════════════ -->
|
||||
<?php
|
||||
$formData = $_SESSION['form_data'] ?? [];
|
||||
$libreEnabled = true; // always shown in admin
|
||||
$interneEnabled = true;
|
||||
$interditEnabled = true;
|
||||
$generalitiesHtml = $helpFn('fieldset_generalites');
|
||||
$defaultAccessTypeId = $currentAccessTypeId ?? 2;
|
||||
$formData['access_type_id'] = $currentAccessTypeId;
|
||||
$formData['license_id'] = $currentLicenseId;
|
||||
$formData['license_custom'] = $currentRaw['license_custom'] ?? '';
|
||||
$formData['cc2r'] = $currentRaw['cc4r'] ?? false;
|
||||
include APP_ROOT . '/templates/partials/form/fieldset-licence-explanation.php';
|
||||
?>
|
||||
|
||||
<!-- ═══════════════════ Note contextuelle ═══════════════════ -->
|
||||
<fieldset>
|
||||
<legend>Note contextuelle</legend>
|
||||
<div>
|
||||
<label for="context_note">Note contextuelle :</label>
|
||||
<div>
|
||||
<textarea id="context_note" name="context_note"
|
||||
rows="4" maxlength="1500"><?= htmlspecialchars($currentContextNote ?? '') ?></textarea>
|
||||
<small>Visible publiquement pour les TFE Interne ou Interdit. Max 1 500 caractères.</small>
|
||||
</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
|
||||
<!-- ═══════════════════ Backoffice ═══════════════════ -->
|
||||
<fieldset>
|
||||
<legend>Backoffice</legend>
|
||||
@@ -253,28 +318,6 @@
|
||||
</label>
|
||||
<small>Case logistique : cocher si un exemplaire physique est disponible à l'ERG.</small>
|
||||
</div>
|
||||
|
||||
<div class="admin-form-group">
|
||||
<label class="admin-checkbox-label">
|
||||
<input type="checkbox" name="cc4r" value="1"
|
||||
<?= !empty($currentRaw['cc4r']) ? 'checked' : '' ?>>
|
||||
CC4r accepté
|
||||
</label>
|
||||
<small>Conditions collectives de réutilisation.</small>
|
||||
</div>
|
||||
</fieldset>
|
||||
|
||||
<!-- ═══════════════════ Note contextuelle ═══════════════════ -->
|
||||
<fieldset id="section-context-note">
|
||||
<legend>Note contextuelle</legend>
|
||||
<div>
|
||||
<label for="context_note">Note contextuelle :</label>
|
||||
<div>
|
||||
<textarea id="context_note" name="context_note"
|
||||
rows="4" maxlength="1500"><?= htmlspecialchars($currentContextNote ?? '') ?></textarea>
|
||||
<small>Visible publiquement pour les TFE Interne ou Interdit. Max 1 500 caractères.</small>
|
||||
</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
|
||||
<!-- ═══════════════════ Publication ════════════════════════ -->
|
||||
@@ -289,6 +332,5 @@
|
||||
</div>
|
||||
</fieldset>
|
||||
|
||||
|
||||
</form>
|
||||
</main>
|
||||
|
||||
@@ -19,7 +19,7 @@ $checked = $checked ?? [];
|
||||
$required = $required ?? false;
|
||||
?>
|
||||
<div>
|
||||
<span class="admin-row-label"><?= htmlspecialchars($label) ?></span>
|
||||
<span class="admin-row-label"><?= htmlspecialchars($label) ?><?= $required ? ' <span class="asterisk">*</span>' : '' ?></span>
|
||||
<fieldset class="admin-checkbox-group"<?= $required ? ' required aria-required="true"' : '' ?>>
|
||||
<legend class="sr-only"><?= htmlspecialchars($label) ?></legend>
|
||||
<ul>
|
||||
|
||||
@@ -8,9 +8,7 @@
|
||||
* array $orientations — options for the orientation select.
|
||||
* array $apPrograms — options for the AP select.
|
||||
* array $finalityTypes — options for the finality select.
|
||||
* array $languages — options for the language checkbox list.
|
||||
* array $formatTypes — options for the format checkbox list.
|
||||
* array $formData — raw form data (used for checkbox checked state).
|
||||
* array $formData — raw form data (used for selected state).
|
||||
*/
|
||||
|
||||
$oldFn = $oldFn ?? (function_exists('old') ? 'old' : fn($k, $d = '') => $d);
|
||||
@@ -46,17 +44,6 @@ $formData = $formData ?? [];
|
||||
$attrs = $withAutofocusFn('finality');
|
||||
include APP_ROOT . '/templates/partials/form/select-field.php';
|
||||
?>
|
||||
|
||||
<?php $name = 'languages'; $label = 'Langue(s) :'; $options = $languages; $checked = $formData['languages'] ?? []; $required = true; include APP_ROOT . '/templates/partials/form/checkbox-list.php'; ?>
|
||||
<?php $name = 'formats'; $label = 'Format(s) :'; $options = $formatTypes; $checked = $formData['formats'] ?? []; include APP_ROOT . '/templates/partials/form/checkbox-list.php'; ?>
|
||||
|
||||
<?php
|
||||
$name = 'tag'; $label = 'Mots-clés :'; $value = $oldFn('tag');
|
||||
$placeholder = 'sociologie, anthropologie, ...';
|
||||
$hint = 'Séparez par des virgules. Max 10 mots-clés.';
|
||||
$attrs = $withAutofocusFn('tag');
|
||||
include APP_ROOT . '/templates/partials/form/text-field.php';
|
||||
?>
|
||||
</fieldset>
|
||||
<?php
|
||||
unset($oldFn, $withAutofocusFn);
|
||||
|
||||
@@ -2,11 +2,11 @@
|
||||
/**
|
||||
* Shared partial — "Fichiers" fieldset (add / student submission mode).
|
||||
*
|
||||
* Renders upload inputs for cover image, banner image, and TFE files.
|
||||
* TFE files support multiple file types (PDF, image, audio, video, other),
|
||||
* drag-to-reorder via SortableJS, and per-file label input.
|
||||
*
|
||||
* For the edit form, the existing-files management is inline in edit.php.
|
||||
* Order per spec:
|
||||
* 1. Image de bannière (optionnel)
|
||||
* 2. Note d'intention (obligatoire)
|
||||
* 3. TFE (obligatoire)
|
||||
* 4. Annexes éventuelles (optionnel)
|
||||
*
|
||||
* Variables consumed: none beyond APP_ROOT (always available).
|
||||
*/
|
||||
@@ -14,33 +14,33 @@
|
||||
<fieldset>
|
||||
<legend>Fichiers</legend>
|
||||
|
||||
<?php
|
||||
$name = 'couverture';
|
||||
$label = 'Image de couverture :';
|
||||
$accept = 'image/jpeg,image/png';
|
||||
$hint = 'JPG, PNG. Taille max : 20 MB.';
|
||||
include APP_ROOT . '/templates/partials/form/file-field.php';
|
||||
?>
|
||||
|
||||
<?php
|
||||
$name = 'banner';
|
||||
$label = 'Image bannière (accueil) :';
|
||||
$label = 'Image de bannière (optionnel) :';
|
||||
$accept = 'image/jpeg,image/png,image/webp';
|
||||
$hint = 'JPG, PNG ou WEBP. Format paysage recommandé (4:1). Max 20 MB.';
|
||||
include APP_ROOT . '/templates/partials/form/file-field.php';
|
||||
?>
|
||||
|
||||
<?php
|
||||
$name = 'note_intention';
|
||||
$label = 'Note d\'intention :';
|
||||
$accept ='.pdf';
|
||||
$hint = 'Format PDF uniquement.';
|
||||
$required = true;
|
||||
include APP_ROOT . '/templates/partials/form/file-field.php';
|
||||
?>
|
||||
|
||||
<!-- TFE files — multi-file, sortable, with per-file labels -->
|
||||
<div class="admin-form-group admin-files-fieldgroup">
|
||||
<label>Fichiers du TFE :</label>
|
||||
<label>TFE (obligatoire) :</label>
|
||||
<div class="admin-file-input">
|
||||
<input type="file" id="tfe-files-input"
|
||||
name="files[]" multiple
|
||||
accept=".pdf,.jpg,.jpeg,.png,.gif,.webp,.mp4,.webm,.mov,.ogv,.mp3,.ogg,.oga,.wav,.flac,.aac,.m4a,.zip,.tar,.gz,.vtt"
|
||||
class="tfe-file-picker">
|
||||
<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) · autres fichiers (téléchargement uniquement).
|
||||
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 500 MB par fichier.
|
||||
Les fichiers <code>.vtt</code> sont des sous-titres et seront associés automatiquement à la vidéo précédente.
|
||||
</small>
|
||||
|
||||
@@ -51,4 +51,13 @@
|
||||
<p id="tfe-file-queue-empty" class="tfe-queue-empty">Aucun fichier sélectionné.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php
|
||||
$name = 'annexes';
|
||||
$label = 'Annexes éventuelles (optionnel) :';
|
||||
$accept = '.pdf,.zip,.tar,.gz';
|
||||
$hint = 'PDF ou archives ZIP/TAR.';
|
||||
$multiple = true;
|
||||
include APP_ROOT . '/templates/partials/form/file-field.php';
|
||||
?>
|
||||
</fieldset>
|
||||
|
||||
@@ -1,52 +1,40 @@
|
||||
<?php
|
||||
/**
|
||||
* Shared partial — "Degrés d'ouverture et licences" explanation fieldset.
|
||||
* Shared partial — "Degrés d'ouverture et licences" fieldset.
|
||||
*
|
||||
* This fieldset is informational and contains the three degrees (Libre, Interne,
|
||||
* Interdit) plus general rules. It is shown on both the admin add form and the
|
||||
* student share-link form. The admin edit form omits it.
|
||||
* Renders:
|
||||
* 1. Généralités (editable via form help blocks)
|
||||
* 2. Radio choice: Libre / Interne / Interdit (filtered by enabled flags)
|
||||
* 3. Licence dropdown + custom licence text input
|
||||
* 4. CC2r checkbox
|
||||
*
|
||||
* Variables consumed:
|
||||
* array $formData — raw form data (used for checkbox repopulation).
|
||||
* array $formData — raw form data for repopulation
|
||||
* array $licenseTypes — [{id, name}]
|
||||
* bool $libreEnabled — show Libre option (always true for admin)
|
||||
* bool $interneEnabled — show Interne option
|
||||
* bool $interditEnabled — show Interdit option
|
||||
* string $generalitiesHtml — HTML content for Généralités section (editable)
|
||||
* int $defaultAccessTypeId — default selected access type (default: 2)
|
||||
*/
|
||||
|
||||
$formData = $formData ?? [];
|
||||
$formData = $formData ?? [];
|
||||
$licenseTypes = $licenseTypes ?? [];
|
||||
$libreEnabled = $libreEnabled ?? true;
|
||||
$interneEnabled = $interneEnabled ?? true;
|
||||
$interditEnabled = $interditEnabled ?? true;
|
||||
$generalitiesHtml = $generalitiesHtml ?? '';
|
||||
$defaultAccessTypeId = $defaultAccessTypeId ?? 2;
|
||||
?>
|
||||
<fieldset class="licence-explanation">
|
||||
<legend>Degrés d'ouverture et licences</legend>
|
||||
|
||||
<div class="licence-info">
|
||||
<h3>Je veux que mon TFE soit disponible sous les conditions suivantes :</h3>
|
||||
|
||||
<div class="licence-degree">
|
||||
<h4>🔓 Libre</h4>
|
||||
<p>Mon TFE est en libre accès à tout le monde sur la plateforme des TFE ainsi que dans la bibliothèque de l'erg. Je suis conscient·e des responsabilités et obligations légales qui viennent avec une diffusion externe – et acquiesce avoir lu la documentation prévue à cet effet par l'erg, ainsi qu'avoir discuté des enjeux d'une publication avec l'équipe pédagogique. J'accepte de partager mes droits de diffusion avec l'erg, ce uniquement dans le cadre d'une diffusion sur la plateforme xamxam.</p>
|
||||
<ul>
|
||||
<li><label><input type="checkbox" name="cc4r" value="1" <?= !empty($formData['cc4r']) ? 'checked' : '' ?>> J'accepte les conditions collectives de réutilisation (CC4r) <em class="hint">(pas obligatoire)</em></label></li>
|
||||
<li><label><input type="checkbox" name="specific_license" value="1" <?= !empty($formData['specific_license']) ? 'checked' : '' ?>> Je souhaite appliquer une licence spécifique à mon travail <em class="hint">(pas obligatoire)</em></label></li>
|
||||
</ul>
|
||||
<p class="licence-note"><em>Au moins une des deux cases doit être cochée pour le degré Libre.</em></p>
|
||||
</div>
|
||||
|
||||
<div class="licence-degree">
|
||||
<h4>🔒 Interne</h4>
|
||||
<p>Mon TFE et ma note d'intention ne sont accessibles que sur place en physique ainsi que sur la plateforme xamxam par la communauté erg. Une note descriptive est disponible sur le site à toustes. J'autorise une (ré-)utilisation et diffusion dans un contexte académique et didactique au sein de l'erg.</p>
|
||||
<p class="licence-note"><em>La diffusion limitée est protégée par le cadre académique/didactique, le travail pourrait donc être diffusé en interne et être cité par d'autres étudiant·es sans implications légales pour l'auteur·ice ni pour l'école.</em></p>
|
||||
<ul>
|
||||
<li><label><input type="checkbox" name="cc4r" value="1" <?= !empty($formData['cc4r']) ? 'checked' : '' ?>> J'accepte les conditions collectives de réutilisation (CC4r) <em class="hint">(pas obligatoire)</em></label></li>
|
||||
<li><label><input type="checkbox" name="specific_license" value="1" <?= !empty($formData['specific_license']) ? 'checked' : '' ?>> Je souhaite appliquer une licence spécifique à mon travail <em class="hint">(pas obligatoire)</em></label></li>
|
||||
</ul>
|
||||
<p class="licence-note"><em>Au moins une des deux cases doit être cochée.</em></p>
|
||||
</div>
|
||||
|
||||
<div class="licence-degree">
|
||||
<h4>🚫 Interdit</h4>
|
||||
<p>Mon TFE n'est pas disponible en physique ni sur le site. Une note descriptive est disponible sur le site.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Généralités -->
|
||||
<div class="licence-generalites">
|
||||
<h3>Généralités</h3>
|
||||
<?php if ($generalitiesHtml): ?>
|
||||
<div class="form-help-editable"><?= $generalitiesHtml ?></div>
|
||||
<?php else: ?>
|
||||
<ul>
|
||||
<li>L'auteur·ice peut décider entre trois degrés de partage de son travail : <strong>libre</strong>, <strong>interne</strong>, <strong>interdit</strong>.</li>
|
||||
<li>L'auteur·ice peut, à tout moment, décider de <strong>restreindre</strong> le degré d'accès à son travail. Il ne peut néanmoins pas l'ouvrir davantage.</li>
|
||||
@@ -55,7 +43,69 @@ $formData = $formData ?? [];
|
||||
<li>Dans tous les cas, l'auteur·ice garde les droits d'auteurs, de diffusion, d'utilisation, etc. de son travail – sauf si la licence choisie restreindrait ses droits.</li>
|
||||
<li>La diffusion « xamxam » est indépendante de la diffusion à la BAIU.</li>
|
||||
</ul>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
|
||||
<!-- Degré d'ouverture -->
|
||||
<div class="licence-choice">
|
||||
<h3>J'autorise l'erg à archiver mon TFE de la manière suivante :</h3>
|
||||
<?php $selectedAccess = $formData['access_type_id'] ?? (string)$defaultAccessTypeId; ?>
|
||||
|
||||
<?php if ($libreEnabled): ?>
|
||||
<div class="licence-degree">
|
||||
<label class="admin-checkbox-label">
|
||||
<input type="radio" name="access_type_id" value="1"
|
||||
<?= $selectedAccess === '1' ? 'checked' : '' ?> required>
|
||||
<strong>🔓 Libre</strong> — Mon TFE est en libre accès à tout le monde sur la plateforme des TFE ainsi que dans la bibliothèque de l'erg.
|
||||
</label>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if ($interneEnabled): ?>
|
||||
<div class="licence-degree">
|
||||
<label class="admin-checkbox-label">
|
||||
<input type="radio" name="access_type_id" value="2"
|
||||
<?= $selectedAccess === '2' ? 'checked' : '' ?> required>
|
||||
<strong>🔒 Interne</strong> — Mon TFE n'est accessible que sur place en physique. Une note descriptive est disponible sur le site.
|
||||
</label>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if ($interditEnabled): ?>
|
||||
<div class="licence-degree">
|
||||
<label class="admin-checkbox-label">
|
||||
<input type="radio" name="access_type_id" value="3"
|
||||
<?= $selectedAccess === '3' ? 'checked' : '' ?> required>
|
||||
<strong>🚫 Interdit</strong> — Mon TFE n'est pas disponible en physique ni sur le site. Une note descriptive est disponible sur le site.
|
||||
</label>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
<p class="licence-note"><em>L'auteur·ice peut, à tout moment, décider de restreindre son propre choix. Iel ne peut par contre pas l'ouvrir.</em></p>
|
||||
</div>
|
||||
|
||||
<!-- Licence -->
|
||||
<div class="licence-license-choice">
|
||||
<h3>Licence du TFE</h3>
|
||||
<?php
|
||||
$name = 'license_id'; $label = 'Licence :'; $options = $licenseTypes;
|
||||
$selected = $formData['license_id'] ?? ''; $placeholder = '— Sélectionner —';
|
||||
include APP_ROOT . '/templates/partials/form/select-field.php';
|
||||
?>
|
||||
|
||||
<?php
|
||||
$name = 'license_custom'; $label = 'Ou précisez une autre licence :';
|
||||
$value = htmlspecialchars($formData['license_custom'] ?? '');
|
||||
$hint = 'Ex: CC BY-NC 4.0, Tous droits réservés...';
|
||||
include APP_ROOT . '/templates/partials/form/text-field.php';
|
||||
?>
|
||||
|
||||
<div class="admin-form-group">
|
||||
<label class="admin-checkbox-label">
|
||||
<input type="checkbox" name="cc2r" value="1"
|
||||
<?= !empty($formData['cc2r']) ? 'checked' : '' ?>>
|
||||
J'accepte les Conditions Collectives de Réutilisation (CC2r)
|
||||
</label>
|
||||
<small><a href="https://cc2r.net/" target="_blank" rel="noopener">En savoir plus sur la CC2r ↗</a></small>
|
||||
</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
<?php
|
||||
unset($n);
|
||||
|
||||
@@ -2,62 +2,48 @@
|
||||
/**
|
||||
* Shared partial — "Métadonnées complémentaires" fieldset.
|
||||
*
|
||||
* Now split: licence/access moved to licence-explanation fieldset.
|
||||
* This fieldset keeps: duration (pages + minutes), lien (external link).
|
||||
*
|
||||
* Variables consumed:
|
||||
* callable|null $oldFn — callable($key, $default='') for old/current values.
|
||||
* callable|null $withAutofocusFn — callable($field, $attrs=[]) to inject autofocus.
|
||||
* array $licenseTypes — options for the license select.
|
||||
* array $enabledAccessTypes — raw access-type rows (with 'id', 'name', optionally 'description').
|
||||
* array $formData — raw form data (used for selected state).
|
||||
* int $defaultAccessTypeId — default access type id; defaults to 2.
|
||||
* bool $showDescription — when true, appends description to access-type option labels.
|
||||
* array $formData — raw form data (used for repopulation).
|
||||
*/
|
||||
|
||||
$oldFn = $oldFn ?? (function_exists('old') ? 'old' : fn($k, $d = '') => $d);
|
||||
$withAutofocusFn = $withAutofocusFn ?? fn($field, $attrs = []) => $attrs;
|
||||
$formData = $formData ?? [];
|
||||
$defaultAccessTypeId = $defaultAccessTypeId ?? 2;
|
||||
$showDescription = $showDescription ?? false;
|
||||
|
||||
$accessOptions = array_map(function ($at) use ($showDescription) {
|
||||
$label = $at['name'];
|
||||
if ($showDescription && !empty($at['description'])) {
|
||||
$label .= ' — ' . $at['description'];
|
||||
}
|
||||
return ['id' => $at['id'], 'name' => $label];
|
||||
}, $enabledAccessTypes);
|
||||
|
||||
$selectedAccessType = isset($formData['access_type_id'])
|
||||
? (int) $formData['access_type_id']
|
||||
: $defaultAccessTypeId;
|
||||
$oldFn = $oldFn ?? (function_exists('old') ? 'old' : fn($k, $d = '') => $d);
|
||||
$withAutofocusFn = $withAutofocusFn ?? fn($field, $attrs = []) => $attrs;
|
||||
$formData = $formData ?? [];
|
||||
?>
|
||||
<fieldset>
|
||||
<legend>Métadonnées complémentaires</legend>
|
||||
|
||||
<?php
|
||||
$name = 'license_id'; $label = 'Licence :'; $options = $licenseTypes;
|
||||
$selected = $formData['license_id'] ?? ''; $placeholder = '— Inconnue —';
|
||||
include APP_ROOT . '/templates/partials/form/select-field.php';
|
||||
$name = 'duration_pages'; $label = 'Nombre de pages :'; $value = $oldFn('duration_pages');
|
||||
$type = 'number'; $placeholder = ''; $hint = 'Ex : 84';
|
||||
include APP_ROOT . '/templates/partials/form/text-field.php';
|
||||
?>
|
||||
|
||||
<?php
|
||||
$name = 'duration_info'; $label = 'Durée / Taille :'; $value = $oldFn('duration_info');
|
||||
$placeholder = 'Ex : 84 pages'; $hint = 'Durée (minutes) ou nombre de pages.';
|
||||
$name = 'duration_minutes'; $label = 'Durée (minutes) :'; $value = $oldFn('duration_minutes');
|
||||
$type = 'number'; $placeholder = ''; $hint = 'Ex : 32 (pour œuvres audio/vidéo)';
|
||||
include APP_ROOT . '/templates/partials/form/text-field.php';
|
||||
?>
|
||||
|
||||
<div class="admin-form-group">
|
||||
<label class="admin-checkbox-label">
|
||||
<input type="checkbox" name="has_annexes" value="1"
|
||||
<?= !empty($formData['has_annexes']) ? 'checked' : '' ?>>
|
||||
Ce TFE comporte des annexes
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<?php
|
||||
$name = 'lien'; $label = 'Lien (site / ressource) :'; $value = $oldFn('lien');
|
||||
$type = 'url'; $placeholder = 'https://...';
|
||||
$type = 'url'; $placeholder = 'https://...'; $hint = '';
|
||||
$attrs = $withAutofocusFn('lien');
|
||||
include APP_ROOT . '/templates/partials/form/text-field.php';
|
||||
?>
|
||||
|
||||
<?php
|
||||
$name = 'access_type_id'; $label = 'Visibilité / Accès :';
|
||||
$options = $accessOptions; $selected = $selectedAccessType;
|
||||
$placeholder = null; $required = true; $attrs = [];
|
||||
include APP_ROOT . '/templates/partials/form/select-field.php';
|
||||
?>
|
||||
</fieldset>
|
||||
<?php
|
||||
unset($oldFn, $withAutofocusFn, $defaultAccessTypeId, $showDescription, $accessOptions, $selectedAccessType);
|
||||
unset($oldFn, $withAutofocusFn);
|
||||
|
||||
@@ -6,14 +6,11 @@
|
||||
* callable|null $oldFn — callable($key, $default='') to retrieve old/current values;
|
||||
* defaults to the global old() function when null.
|
||||
* callable|null $withAutofocusFn — callable($field, $attrs=[]) to inject autofocus;
|
||||
* defaults to identity (returns $attrs) when null.
|
||||
* defaults to identity when null.
|
||||
* array $allowedObjet — list of allowed objet values; when count > 1 a radio group
|
||||
* is shown; when count === 1 a hidden input is emitted instead.
|
||||
* Omit or pass [] to suppress the objet selector entirely.
|
||||
* array $formData — raw form data array (used for contact_public checkbox state).
|
||||
* string $synopsisExtra — optional HTML block injected after the synopsis label
|
||||
* (use for student-facing explanations).
|
||||
* bool $isAdminMode — when true the form uses admin-style helpers; default false.
|
||||
* array $formData — raw form data array (used for checkbox states).
|
||||
* string $synopsisExtra — optional HTML block injected after the synopsis label.
|
||||
*/
|
||||
|
||||
$oldFn = $oldFn ?? (function_exists('old') ? 'old' : fn($k, $d = '') => $d);
|
||||
@@ -45,37 +42,29 @@ $synopsisExtra = $synopsisExtra ?? '';
|
||||
<?php endif; ?>
|
||||
|
||||
<?php
|
||||
$name = 'titre'; $label = 'Titre :'; $value = $oldFn('titre'); $required = true;
|
||||
$name = 'titre'; $label = 'Titre du TFE :'; $value = $oldFn('titre'); $required = true;
|
||||
$attrs = $withAutofocusFn('titre');
|
||||
include APP_ROOT . '/templates/partials/form/text-field.php';
|
||||
?>
|
||||
<?php
|
||||
$name = 'subtitle'; $label = 'Sous-titre (si applicable) :'; $value = $oldFn('subtitle'); $required = false;
|
||||
$attrs = [];
|
||||
include APP_ROOT . '/templates/partials/form/text-field.php';
|
||||
?>
|
||||
<?php
|
||||
$name = 'auteurice'; $label = 'Auteur·ice(s) :'; $value = $oldFn('auteurice'); $required = true;
|
||||
$attrs = $withAutofocusFn('auteurice', ['autocomplete' => 'name']);
|
||||
$hint = 'Séparez les auteur·ices par des virgules.';
|
||||
include APP_ROOT . '/templates/partials/form/text-field.php';
|
||||
?>
|
||||
<?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.';
|
||||
include APP_ROOT . '/templates/partials/form/text-field.php';
|
||||
?>
|
||||
|
||||
<div class="admin-form-group">
|
||||
<label class="admin-checkbox-label">
|
||||
<input type="checkbox" name="contact_public" value="1"
|
||||
<?= !empty($formData['contact_public']) ? 'checked' : '' ?>>
|
||||
Rendre mon contact visible publiquement sur la fiche du TFE
|
||||
</label>
|
||||
<small>Si coché, votre contact apparaîtra sur la page publique. L'adresse est toujours conservée en interne.</small>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label for="synopsis">Synopsis :</label>
|
||||
<label for="synopsis">Synopsis : <span class="asterisk">*</span></label>
|
||||
<?= $synopsisExtra ?>
|
||||
<textarea id="synopsis" name="synopsis" rows="7" required
|
||||
<?= ($withAutofocusFn('synopsis')['autofocus'] ?? false) ? 'autofocus' : '' ?>><?= $oldFn('synopsis') ?></textarea>
|
||||
|
||||
@@ -7,24 +7,27 @@
|
||||
* string $label — visible label text
|
||||
* string $accept — MIME types / extensions for the accept attribute (e.g. 'image/jpeg,image/png')
|
||||
* string|null $hint — optional hint shown in <small> below the input
|
||||
* bool $required — whether the field is required; default false
|
||||
* bool $multiple — whether to allow multiple file selection; default false
|
||||
* string|null $id — override the id attribute (defaults to $name)
|
||||
*/
|
||||
|
||||
$accept = $accept ?? '';
|
||||
$hint = $hint ?? null;
|
||||
$required = $required ?? false;
|
||||
$multiple = $multiple ?? false;
|
||||
$id = $id ?? $name;
|
||||
$previewId = 'fp-' . htmlspecialchars($id);
|
||||
?>
|
||||
<div>
|
||||
<label for="<?= htmlspecialchars($id) ?>"><?= htmlspecialchars($label) ?></label>
|
||||
<label for="<?= htmlspecialchars($id) ?>"><?= htmlspecialchars($label) ?><?= $required ? ' <span class="asterisk">*</span>' : '' ?></label>
|
||||
<div class="admin-file-input">
|
||||
<input type="file"
|
||||
id="<?= htmlspecialchars($id) ?>"
|
||||
name="<?= htmlspecialchars($name) ?><?= $multiple ? '[]' : '' ?>"
|
||||
<?= $accept ? 'accept="' . htmlspecialchars($accept) . '"' : '' ?>
|
||||
<?= $multiple ? 'multiple' : '' ?>
|
||||
<?= $required ? 'required' : '' ?>
|
||||
data-preview="<?= $previewId ?>">
|
||||
<div id="<?= $previewId ?>" class="file-preview-list" aria-live="polite"></div>
|
||||
<?php if ($hint): ?>
|
||||
@@ -33,4 +36,4 @@ $previewId = 'fp-' . htmlspecialchars($id);
|
||||
</div>
|
||||
</div>
|
||||
<?php
|
||||
unset($accept, $hint, $multiple, $id, $previewId);
|
||||
unset($accept, $hint, $required, $multiple, $id, $previewId);
|
||||
|
||||
@@ -3,120 +3,171 @@
|
||||
* Jury composition fieldset partial.
|
||||
*
|
||||
* Variables consumed (all optional — defaults to empty/add-mode):
|
||||
* $juryPresident string|null President name
|
||||
* $juryPromoteur string|null Promoteur name
|
||||
* $juryPromoteurUlb int 1 if promoteur is ULB, 0 otherwise
|
||||
* $juryPromoteurExt int 1 if promoteur is external, 0 otherwise
|
||||
* $juryLecteurs array Each element: ['name' => string, 'is_external' => int]
|
||||
* $juryPromoteur string|null Promoteur interne name
|
||||
* $juryPromoteurUlb string|null Promoteur ULB name
|
||||
* $lecteursInternes array [{name: string}]
|
||||
* $lecteursExternes array [{name: string}]
|
||||
* $juryPresident string|null President name (edit-only, optional)
|
||||
* $showPresident bool Show president field (default: false)
|
||||
* $showPromoteurUlb bool Show ULB promoteur field (default: true)
|
||||
* $promoteurUlbConditional bool If true, field is hidden unless finality=Approfondi
|
||||
*
|
||||
* In "add" mode (no existing jury data), callers should pass nulls/empty arrays and
|
||||
* may rely on the $formData repopulation helpers (old/wasSelected) defined in add.php.
|
||||
* When those helpers exist and no explicit values are set, the partial uses them.
|
||||
* In add-mode repopulation: if old() exists and values are null, populate from it.
|
||||
*/
|
||||
|
||||
$juryPresident = $juryPresident ?? null;
|
||||
$juryPromoteur = $juryPromoteur ?? null;
|
||||
$juryPromoteurExt = $juryPromoteurExt ?? 0;
|
||||
$juryPromoteurUlb = $juryPromoteurUlb ?? 0;
|
||||
$juryLecteurs = $juryLecteurs ?? [];
|
||||
$juryPromoteur = $juryPromoteur ?? null;
|
||||
$juryPromoteurUlb = $juryPromoteurUlb ?? null;
|
||||
$lecteursInternes = $lecteursInternes ?? [];
|
||||
$lecteursExternes = $lecteursExternes ?? [];
|
||||
$juryPresident = $juryPresident ?? null;
|
||||
$showPresident = $showPresident ?? false;
|
||||
$showPromoteurUlb = $showPromoteurUlb ?? true;
|
||||
$promoteurUlbConditional = $promoteurUlbConditional ?? false;
|
||||
|
||||
// In add-mode, repopulate from flash form data when helpers are available.
|
||||
$addMode = ($juryPresident === null && $juryPromoteur === null && empty($juryLecteurs));
|
||||
// Add-mode repopulation from flash data
|
||||
$addMode = ($juryPromoteur === null && $juryPromoteurUlb === null && empty($lecteursInternes) && empty($lecteursExternes) && $juryPresident === null);
|
||||
if ($addMode && function_exists('old')) {
|
||||
$juryPresident = old('jury_president') ?: null;
|
||||
$juryPromoteur = old('jury_promoteur') ?: null;
|
||||
$juryPromoteurExt = function_exists('wasSelected') && wasSelected('jury_promoteur_ext', '1') ? 1 : 0;
|
||||
$juryPromoteur = old('jury_promoteur') ?: null;
|
||||
$juryPromoteurUlb = old('jury_promoteur_ulb_name') ?: null;
|
||||
$juryPresident = old('jury_president') ?: null;
|
||||
for ($i = 0; $i < 10; $i++) {
|
||||
$n = old("jury_lecteur_interne:$i");
|
||||
if ($n !== '') $lecteursInternes[] = ['name' => $n];
|
||||
}
|
||||
for ($i = 0; $i < 10; $i++) {
|
||||
$n = old("jury_lecteur_externe:$i");
|
||||
if ($n !== '') $lecteursExternes[] = ['name' => $n];
|
||||
}
|
||||
}
|
||||
|
||||
$juryIdx = max(count($juryLecteurs), 1);
|
||||
?>
|
||||
<!-- Composition du jury -->
|
||||
<fieldset>
|
||||
<legend>Composition du jury</legend>
|
||||
<fieldset>
|
||||
<legend>Composition du jury</legend>
|
||||
|
||||
<!-- Président·e -->
|
||||
<div>
|
||||
<label for="jury_president">Président·e :</label>
|
||||
<input type="text" id="jury_president" name="jury_president"
|
||||
value="<?= htmlspecialchars($juryPresident ?? '') ?>"
|
||||
placeholder="Nom du/de la président·e (interne)">
|
||||
</div>
|
||||
<!-- Promoteur·ice interne -->
|
||||
<div>
|
||||
<label for="jury_promoteur">Promoteur·ice interne :</label>
|
||||
<input type="text" id="jury_promoteur" name="jury_promoteur"
|
||||
value="<?= htmlspecialchars($juryPromoteur ?? '') ?>" placeholder="Nom">
|
||||
</div>
|
||||
|
||||
<!-- Promoteur·ice -->
|
||||
<div>
|
||||
<label for="jury_promoteur">Promoteur·ice :</label>
|
||||
<div class="admin-jury-row">
|
||||
<input type="text" id="jury_promoteur" name="jury_promoteur"
|
||||
value="<?= htmlspecialchars($juryPromoteur ?? '') ?>" placeholder="Nom">
|
||||
<label class="admin-checkbox-label admin-jury-ext">
|
||||
<input type="checkbox" name="jury_promoteur_ext" value="1"
|
||||
<?= $juryPromoteurExt ? 'checked' : '' ?>
|
||||
aria-label="Promoteur·ice — externe"> Externe
|
||||
</label>
|
||||
<label class="admin-checkbox-label admin-jury-ext">
|
||||
<input type="checkbox" name="jury_promoteur_ulb" value="1"
|
||||
<?= $juryPromoteurUlb ? 'checked' : '' ?>
|
||||
aria-label="Promoteur·ice — ULB"> ULB
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Lecteur·ices (dynamic list) -->
|
||||
<fieldset class="admin-jury-lecteurs">
|
||||
<legend>Lecteur·ices</legend>
|
||||
<div id="jury-lecteurs-list" class="admin-jury-list">
|
||||
<?php if (empty($juryLecteurs)): ?>
|
||||
<div class="admin-jury-entry">
|
||||
<input type="text" name="jury_lecteurs[]" placeholder="Nom"
|
||||
id="jury_lecteur_0" aria-label="Lecteur·ice 1 — nom">
|
||||
<label class="admin-checkbox-label admin-jury-ext">
|
||||
<input type="checkbox" name="jury_lecteurs_ext[0]" value="1"
|
||||
aria-label="Lecteur·ice 1 — externe"> Externe
|
||||
</label>
|
||||
<button type="button" class="btn btn--sm btn--ghost admin-btn-remove" onclick="removeJuryRow(this)"
|
||||
aria-label="Supprimer le lecteur·ice 1"><span aria-hidden="true">✕</span></button>
|
||||
</div>
|
||||
<?php else: ?>
|
||||
<?php foreach ($juryLecteurs as $li => $lm): ?>
|
||||
<?php $lNum = $li + 1; ?>
|
||||
<div class="admin-jury-entry">
|
||||
<input type="text" name="jury_lecteurs[]"
|
||||
value="<?= htmlspecialchars($lm['name']) ?>" placeholder="Nom"
|
||||
id="jury_lecteur_<?= $li ?>" aria-label="Lecteur·ice <?= $lNum ?> — nom">
|
||||
<label class="admin-checkbox-label admin-jury-ext">
|
||||
<input type="checkbox" name="jury_lecteurs_ext[<?= $li ?>]" value="1"
|
||||
<?= $lm['is_external'] ? 'checked' : '' ?>
|
||||
aria-label="Lecteur·ice <?= $lNum ?> — externe"> Externe
|
||||
</label>
|
||||
<button type="button" class="btn btn--sm btn--ghost admin-btn-remove" onclick="removeJuryRow(this)"
|
||||
aria-label="Supprimer le lecteur·ice <?= $lNum ?>"><span aria-hidden="true">✕</span></button>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
<button type="button" class="btn btn--secondary admin-add-jury-btn"
|
||||
onclick="addJuryRow()">+ Ajouter un·e lecteur·ice</button>
|
||||
</fieldset>
|
||||
</fieldset>
|
||||
<script>
|
||||
var juryIdx = <?= $juryIdx ?>;
|
||||
function addJuryRow() {
|
||||
var list = document.getElementById('jury-lecteurs-list');
|
||||
var n = list.querySelectorAll('.admin-jury-entry').length + 1;
|
||||
var div = document.createElement('div');
|
||||
div.className = 'admin-jury-entry';
|
||||
div.innerHTML = '<input type="text" name="jury_lecteurs[]" placeholder="Nom"'
|
||||
+ ' aria-label="Lecteur\u00b7ice ' + n + ' \u2014 nom">'
|
||||
+ '<label class="admin-checkbox-label admin-jury-ext">'
|
||||
+ '<input type="checkbox" name="jury_lecteurs_ext[' + juryIdx + ']" value="1"'
|
||||
+ ' aria-label="Lecteur\u00b7ice ' + n + ' \u2014 externe"> Externe'
|
||||
+ '</label>'
|
||||
+ '<button type="button" class="btn btn--sm btn--ghost admin-btn-remove" onclick="removeJuryRow(this)"'
|
||||
+ ' aria-label="Supprimer le lecteur\u00b7ice ' + n + '"><span aria-hidden="true">\u2715</span></button>';
|
||||
list.appendChild(div);
|
||||
juryIdx++;
|
||||
<?php if ($showPromoteurUlb): ?>
|
||||
<!-- Promoteur·ice ULB -->
|
||||
<div id="jury-promoteur-ulb-row"<?= $promoteurUlbConditional ? ' style="display:none"' : '' ?>>
|
||||
<label for="jury_promoteur_ulb_name">Promoteur·ice ULB :</label>
|
||||
<input type="text" id="jury_promoteur_ulb_name" name="jury_promoteur_ulb_name"
|
||||
value="<?= htmlspecialchars($juryPromoteurUlb ?? '') ?>" placeholder="Nom">
|
||||
</div>
|
||||
<?php if ($promoteurUlbConditional): ?>
|
||||
<script>
|
||||
(function() {
|
||||
var finalitySelect = document.querySelector('select[name="finality"]');
|
||||
var ulbRow = document.getElementById('jury-promoteur-ulb-row');
|
||||
var ulbInput = ulbRow ? ulbRow.querySelector('input') : null;
|
||||
function toggleUlb() {
|
||||
if (!finalitySelect || !ulbRow) return;
|
||||
var selected = finalitySelect.options[finalitySelect.selectedIndex];
|
||||
var text = (selected && selected.text) ? selected.text.toLowerCase() : '';
|
||||
var isApprofondi = text.includes('approfondi');
|
||||
ulbRow.style.display = isApprofondi ? '' : 'none';
|
||||
if (ulbInput) {
|
||||
ulbInput.required = isApprofondi;
|
||||
ulbInput.disabled = !isApprofondi;
|
||||
if (!isApprofondi) ulbInput.value = '';
|
||||
}
|
||||
}
|
||||
function removeJuryRow(btn) {
|
||||
btn.closest('.admin-jury-entry').remove();
|
||||
if (finalitySelect) {
|
||||
finalitySelect.addEventListener('change', toggleUlb);
|
||||
toggleUlb();
|
||||
}
|
||||
</script>
|
||||
})();
|
||||
</script>
|
||||
<?php endif; ?>
|
||||
<?php endif; ?>
|
||||
|
||||
<!-- Lecteur·ice(s) interne -->
|
||||
<fieldset class="admin-jury-lecteurs">
|
||||
<legend>Lecteur·ice(s) interne</legend>
|
||||
<div id="jury-lecteurs-internes-list" class="admin-jury-list">
|
||||
<?php if (empty($lecteursInternes)): ?>
|
||||
<div class="admin-jury-entry">
|
||||
<input type="text" name="jury_lecteur_interne[]" placeholder="Nom"
|
||||
aria-label="Lecteur·ice interne 1 — nom">
|
||||
<button type="button" class="btn btn--sm btn--ghost admin-btn-remove"
|
||||
onclick="removeJuryRow(this)" aria-label="Supprimer"><span aria-hidden="true">✕</span></button>
|
||||
</div>
|
||||
<?php else: ?>
|
||||
<?php foreach ($lecteursInternes as $li => $lm): ?>
|
||||
<div class="admin-jury-entry">
|
||||
<input type="text" name="jury_lecteur_interne[]"
|
||||
value="<?= htmlspecialchars($lm['name']) ?>" placeholder="Nom"
|
||||
aria-label="Lecteur·ice interne <?= $li + 1 ?> — nom">
|
||||
<button type="button" class="btn btn--sm btn--ghost admin-btn-remove"
|
||||
onclick="removeJuryRow(this)" aria-label="Supprimer"><span aria-hidden="true">✕</span></button>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
<button type="button" class="btn btn--secondary admin-add-jury-btn"
|
||||
onclick="addJuryRow('jury-lecteurs-internes-list', 'jury_lecteur_interne[]', 'Lecteur·ice interne')">
|
||||
+ Ajouter un·e lecteur·ice interne
|
||||
</button>
|
||||
</fieldset>
|
||||
|
||||
<!-- Lecteur·ice(s) externe -->
|
||||
<fieldset class="admin-jury-lecteurs">
|
||||
<legend>Lecteur·ice(s) externe</legend>
|
||||
<div id="jury-lecteurs-externes-list" class="admin-jury-list">
|
||||
<?php if (empty($lecteursExternes)): ?>
|
||||
<div class="admin-jury-entry">
|
||||
<input type="text" name="jury_lecteur_externe[]" placeholder="Nom"
|
||||
aria-label="Lecteur·ice externe 1 — nom">
|
||||
<button type="button" class="btn btn--sm btn--ghost admin-btn-remove"
|
||||
onclick="removeJuryRow(this)" aria-label="Supprimer"><span aria-hidden="true">✕</span></button>
|
||||
</div>
|
||||
<?php else: ?>
|
||||
<?php foreach ($lecteursExternes as $li => $lm): ?>
|
||||
<div class="admin-jury-entry">
|
||||
<input type="text" name="jury_lecteur_externe[]"
|
||||
value="<?= htmlspecialchars($lm['name']) ?>" placeholder="Nom"
|
||||
aria-label="Lecteur·ice externe <?= $li + 1 ?> — nom">
|
||||
<button type="button" class="btn btn--sm btn--ghost admin-btn-remove"
|
||||
onclick="removeJuryRow(this)" aria-label="Supprimer"><span aria-hidden="true">✕</span></button>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
<button type="button" class="btn btn--secondary admin-add-jury-btn"
|
||||
onclick="addJuryRow('jury-lecteurs-externes-list', 'jury_lecteur_externe[]', 'Lecteur·ice externe')">
|
||||
+ Ajouter un·e lecteur·ice externe
|
||||
</button>
|
||||
</fieldset>
|
||||
|
||||
<?php if ($showPresident): ?>
|
||||
<!-- Président·e (admin edit only) -->
|
||||
<div>
|
||||
<label for="jury_president">Président·e :</label>
|
||||
<input type="text" id="jury_president" name="jury_president"
|
||||
value="<?= htmlspecialchars($juryPresident ?? '') ?>"
|
||||
placeholder="Nom du/de la président·e (interne)">
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</fieldset>
|
||||
|
||||
<script>
|
||||
function addJuryRow(listId, inputName, roleLabel) {
|
||||
var list = document.getElementById(listId);
|
||||
if (!list) return;
|
||||
var n = list.querySelectorAll('.admin-jury-entry').length + 1;
|
||||
var div = document.createElement('div');
|
||||
div.className = 'admin-jury-entry';
|
||||
div.innerHTML = '<input type="text" name="' + inputName + '" placeholder="Nom"'
|
||||
+ ' aria-label="' + roleLabel + ' ' + n + ' \u2014 nom">'
|
||||
+ '<button type="button" class="btn btn--sm btn--ghost admin-btn-remove"'
|
||||
+ ' onclick="removeJuryRow(this)" aria-label="Supprimer">'
|
||||
+ '<span aria-hidden="true">\u2715</span></button>';
|
||||
list.appendChild(div);
|
||||
}
|
||||
function removeJuryRow(btn) {
|
||||
btn.closest('.admin-jury-entry').remove();
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -23,7 +23,7 @@ $hint = $hint ?? null;
|
||||
$attrs = $attrs ?? [];
|
||||
?>
|
||||
<div>
|
||||
<label for="<?= htmlspecialchars($id) ?>"><?= htmlspecialchars($label) ?></label>
|
||||
<label for="<?= htmlspecialchars($id) ?>"><?= htmlspecialchars($label) ?><?= $required ? ' <span class="asterisk">*</span>' : '' ?></label>
|
||||
<?php
|
||||
$selectAttrStr = '';
|
||||
foreach ($attrs as $k => $v) {
|
||||
|
||||
@@ -33,7 +33,7 @@ foreach ($attrs as $k => $v) {
|
||||
}
|
||||
?>
|
||||
<div>
|
||||
<label for="<?= htmlspecialchars($id) ?>"><?= htmlspecialchars($label) ?></label>
|
||||
<label for="<?= htmlspecialchars($id) ?>"><?= htmlspecialchars($label) ?><?= $required ? ' <span class="asterisk">*</span>' : '' ?></label>
|
||||
<?php if ($hint): ?>
|
||||
<div>
|
||||
<input type="<?= htmlspecialchars($type) ?>"
|
||||
|
||||
Reference in New Issue
Block a user