mirror of
https://codeberg.org/PostERG/xamxam.git
synced 2026-06-25 16:19:19 +02:00
Add website-type TFE support: URLs stored as thesis_files rows, HTMX-toggle on Site web format
This commit is contained in:
14
TODO.md
14
TODO.md
@@ -102,6 +102,20 @@
|
|||||||
- [x] Migration `013_fix_csv_column_shift.sql`: move orientation from synopsis→orientation_id, finality from context_note→finality_id for already-imported theses
|
- [x] Migration `013_fix_csv_column_shift.sql`: move orientation from synopsis→orientation_id, finality from context_note→finality_id for already-imported theses
|
||||||
- [x] Migration `013_fix_remarks_keywords.php`: move keywords from remarks→tags+thesis_tags for already-imported theses
|
- [x] Migration `013_fix_remarks_keywords.php`: move keywords from remarks→tags+thesis_tags for already-imported theses
|
||||||
|
|
||||||
|
## Support website-type TFE (URL instead of uploaded files)
|
||||||
|
- [x] Add `file_type = 'website'` support to `thesis_files` — URL stored in `file_path`, no filesystem upload (no schema change needed)
|
||||||
|
- [x] Admin add form: "Site web (URL)" field dynamically shown via HTMX when "Site web" format checked
|
||||||
|
- [x] Admin edit form: website URL field via HTMX toggle + recognize website (🌐 icon) in existing-files list
|
||||||
|
- [x] Student partage form: website URL field via HTMX toggle + HTMX script added
|
||||||
|
- [x] TFE detail page (`tfe.php`): render `website` type as iframe with sandbox in media section
|
||||||
|
- [x] `ThesisCreateController`: handle website URL in submit → `handleWebsiteUrl()` stores as thesis_files row
|
||||||
|
- [x] `ThesisEditController`: handle website URL in save → `handleWebsiteUrl()` replaces existing website row; delete-files skips unlink for URLs
|
||||||
|
- [x] Edit page: website rows deletable via same delete_files checkbox mechanism
|
||||||
|
- [x] File size 0 for website rows — hidden in edit list to avoid showing "0.00 MB"
|
||||||
|
- [x] HTMX fragment endpoint: `/admin/actions/format-website-fragment.php` + `/partage/format-website-fragment`
|
||||||
|
- [x] `checkbox-list.php` partial: optional `hxPost`/`hxTarget` for HTMX live update
|
||||||
|
- [x] Server-side initial render: pre-populate `#website-url-section` if "Site web" already checked
|
||||||
|
|
||||||
## Standardise répertoire filter column rendering
|
## Standardise répertoire filter column rendering
|
||||||
- [x] Centralise filter column rendering into a shared `repFilterEntry()` function
|
- [x] Centralise filter column rendering into a shared `repFilterEntry()` function
|
||||||
- [x] Define `$filterColumns` config array as single source of truth for the 5 filter columns
|
- [x] Define `$filterColumns` config array as single source of truth for the 5 filter columns
|
||||||
|
|||||||
@@ -204,6 +204,11 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* ── Jury fieldset ──────────────────────────────────────────────────────── */
|
/* ── Jury fieldset ──────────────────────────────────────────────────────── */
|
||||||
|
|
||||||
|
/* ── Website-URL inline fieldset (shown/hidden via HTMX) ────────────────── */
|
||||||
|
/* The fieldset is shown/hidden via outerHTML swap — no CSS needed */
|
||||||
|
|
||||||
|
/* ── Jury fieldset (continued) ──────────────────────────────────────────── */
|
||||||
.admin-body fieldset fieldset.admin-jury-lecteurs,
|
.admin-body fieldset fieldset.admin-jury-lecteurs,
|
||||||
.student-body fieldset fieldset.admin-jury-lecteurs {
|
.student-body fieldset fieldset.admin-jury-lecteurs {
|
||||||
border: none;
|
border: none;
|
||||||
|
|||||||
66
app/public/partage/format-website-fragment.php
Normal file
66
app/public/partage/format-website-fragment.php
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* format-website-fragment.php (partage)
|
||||||
|
*
|
||||||
|
* HTMX fragment for the student share form: returns the website URL input fields
|
||||||
|
* if "Site web" is among the currently selected format checkboxes.
|
||||||
|
*
|
||||||
|
* Regular PHP include inside partage/index.php routing — no separate bootstrap.
|
||||||
|
* The parent partage/index.php already handles boot + session.
|
||||||
|
*
|
||||||
|
* Expected POST:
|
||||||
|
* - formats[]: array of selected format_type IDs
|
||||||
|
* - website_url: current website_url value (for repopulation)
|
||||||
|
* - website_label: current website_label value (for repopulation)
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Find the "Site web" format ID
|
||||||
|
$stmt = Database::getInstance()->getConnection()->prepare(
|
||||||
|
'SELECT id FROM format_types WHERE name = ? LIMIT 1'
|
||||||
|
);
|
||||||
|
$stmt->execute(['Site web']);
|
||||||
|
$websiteFormatId = $stmt->fetchColumn();
|
||||||
|
|
||||||
|
if (!$websiteFormatId) {
|
||||||
|
echo '';
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$selectedFormats = isset($_POST['formats']) && is_array($_POST['formats'])
|
||||||
|
? array_map('intval', $_POST['formats'])
|
||||||
|
: [];
|
||||||
|
|
||||||
|
if (!in_array((int)$websiteFormatId, $selectedFormats, true)) {
|
||||||
|
echo '';
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$websiteUrl = htmlspecialchars($_POST['website_url'] ?? '');
|
||||||
|
$websiteLabel = htmlspecialchars($_POST['website_label'] ?? '');
|
||||||
|
?>
|
||||||
|
<fieldset id="website-url-fieldset">
|
||||||
|
<legend>Site web</legend>
|
||||||
|
|
||||||
|
<div class="admin-form-group">
|
||||||
|
<label for="website_url">URL du site :</label>
|
||||||
|
<div class="admin-file-input">
|
||||||
|
<input type="url"
|
||||||
|
id="website_url"
|
||||||
|
name="website_url"
|
||||||
|
value="<?= $websiteUrl ?>"
|
||||||
|
placeholder="https://mon-tfe.erg.be">
|
||||||
|
<small>Si le TFE est un site web, entrez son URL ici. Il sera affiché comme un site embarqué sur la page du TFE.</small>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="admin-form-group">
|
||||||
|
<label for="website_label">Légende :</label>
|
||||||
|
<input type="text"
|
||||||
|
id="website_label"
|
||||||
|
name="website_label"
|
||||||
|
value="<?= $websiteLabel ?>"
|
||||||
|
placeholder="Description du site (optionnel)"
|
||||||
|
class="admin-file-label-input"
|
||||||
|
style="max-width:400px;">
|
||||||
|
</div>
|
||||||
|
</fieldset>
|
||||||
@@ -21,6 +21,13 @@ $parts = explode('/', $path);
|
|||||||
$slug = $parts[0] ?? '';
|
$slug = $parts[0] ?? '';
|
||||||
$action = $parts[1] ?? '';
|
$action = $parts[1] ?? '';
|
||||||
|
|
||||||
|
// Special route: /partage/format-website-fragment (HTMX fragment, no auth needed)
|
||||||
|
if ($slug === 'format-website-fragment' && $_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||||
|
App::boot();
|
||||||
|
require_once __DIR__ . '/format-website-fragment.php';
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
// Special route: /partage/recapitulatif?id=N
|
// Special route: /partage/recapitulatif?id=N
|
||||||
if ($slug === 'recapitulatif' || $slug === 'recapitulatif.php') {
|
if ($slug === 'recapitulatif' || $slug === 'recapitulatif.php') {
|
||||||
App::boot();
|
App::boot();
|
||||||
@@ -265,6 +272,7 @@ function renderShareLinkForm(string $slug, array $link): void
|
|||||||
<link rel="stylesheet" href="<?= App::assetV('/assets/css/form.css') ?>">
|
<link rel="stylesheet" href="<?= App::assetV('/assets/css/form.css') ?>">
|
||||||
<script src="<?= App::assetV('/assets/js/sortable.min.js') ?>" defer></script>
|
<script src="<?= App::assetV('/assets/js/sortable.min.js') ?>" defer></script>
|
||||||
<script src="<?= App::assetV('/assets/js/file-upload-queue.js') ?>" defer></script>
|
<script src="<?= App::assetV('/assets/js/file-upload-queue.js') ?>" defer></script>
|
||||||
|
<script src="<?= App::assetV('/assets/js/htmx.min.js') ?>" defer></script>
|
||||||
</head>
|
</head>
|
||||||
<body class="student-body">
|
<body class="student-body">
|
||||||
<main id="main-content">
|
<main id="main-content">
|
||||||
@@ -319,12 +327,6 @@ function renderShareLinkForm(string $slug, array $link): void
|
|||||||
<?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'; ?>
|
<?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>
|
</fieldset>
|
||||||
|
|
||||||
<!-- ═══════════════════ Format(s) ═══════════════════ -->
|
|
||||||
<fieldset>
|
|
||||||
<legend>Format(s)</legend>
|
|
||||||
<?php $name = 'formats'; $label = 'Format(s) du TFE :'; $options = $formatTypes; $checked = $formData['formats'] ?? []; $required = true; include APP_ROOT . '/templates/partials/form/checkbox-list.php'; ?>
|
|
||||||
</fieldset>
|
|
||||||
|
|
||||||
<!-- ═══════════════════ Mots-clés ═══════════════════ -->
|
<!-- ═══════════════════ Mots-clés ═══════════════════ -->
|
||||||
<fieldset>
|
<fieldset>
|
||||||
<legend>Mots-clés</legend>
|
<legend>Mots-clés</legend>
|
||||||
@@ -368,6 +370,19 @@ function renderShareLinkForm(string $slug, array $link): void
|
|||||||
require APP_ROOT . '/templates/partials/form/jury-fieldset.php';
|
require APP_ROOT . '/templates/partials/form/jury-fieldset.php';
|
||||||
?>
|
?>
|
||||||
|
|
||||||
|
<!-- ═══════════════════ Format(s) ═══════════════════ -->
|
||||||
|
<fieldset>
|
||||||
|
<legend>Format(s)</legend>
|
||||||
|
<?php
|
||||||
|
$name = 'formats'; $label = 'Format(s) du TFE :'; $options = $formatTypes; $checked = $formData['formats'] ?? []; $required = true;
|
||||||
|
$hxPost = '/partage/format-website-fragment';
|
||||||
|
$hxTarget = '#website-url-fieldset';
|
||||||
|
// Capture before include unsets it
|
||||||
|
$_checkedFormatsForSiteWeb = $checked;
|
||||||
|
include APP_ROOT . '/templates/partials/form/checkbox-list.php';
|
||||||
|
?>
|
||||||
|
</fieldset>
|
||||||
|
|
||||||
<!-- ═══════════════════ Fichiers ═══════════════════ -->
|
<!-- ═══════════════════ Fichiers ═══════════════════ -->
|
||||||
<?php
|
<?php
|
||||||
$helpContent = $helpFn('fieldset_files');
|
$helpContent = $helpFn('fieldset_files');
|
||||||
@@ -375,6 +390,43 @@ function renderShareLinkForm(string $slug, array $link): void
|
|||||||
include APP_ROOT . '/templates/partials/form/fieldset-files.php';
|
include APP_ROOT . '/templates/partials/form/fieldset-files.php';
|
||||||
?>
|
?>
|
||||||
|
|
||||||
|
<!-- Website URL fieldset — shown/hidden via HTMX when "Site web" checked -->
|
||||||
|
<fieldset id="website-url-fieldset" style="display:none">
|
||||||
|
<legend>Site web</legend>
|
||||||
|
|
||||||
|
<div class="admin-form-group">
|
||||||
|
<label for="website_url">URL du site :</label>
|
||||||
|
<div class="admin-file-input">
|
||||||
|
<input type="url"
|
||||||
|
id="website_url"
|
||||||
|
name="website_url"
|
||||||
|
value="<?= htmlspecialchars($formData['website_url'] ?? '') ?>"
|
||||||
|
placeholder="https://mon-tfe.erg.be">
|
||||||
|
<small>Si le TFE est un site web, entrez son URL ici. Il sera affiché comme un site embarqué sur la page du TFE.</small>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="admin-form-group">
|
||||||
|
<label for="website_label">Légende :</label>
|
||||||
|
<input type="text"
|
||||||
|
id="website_label"
|
||||||
|
name="website_label"
|
||||||
|
value="<?= htmlspecialchars($formData['website_label'] ?? '') ?>"
|
||||||
|
placeholder="Description du site (optionnel)"
|
||||||
|
class="admin-file-label-input"
|
||||||
|
style="max-width:400px;">
|
||||||
|
</div>
|
||||||
|
</fieldset>
|
||||||
|
<?php
|
||||||
|
// Server-side: show if Site web already checked (e.g. on error redirect)
|
||||||
|
$_stmt = Database::getInstance()->getConnection()->prepare('SELECT id FROM format_types WHERE name = ? LIMIT 1');
|
||||||
|
$_stmt->execute(['Site web']);
|
||||||
|
$_siteWebId = $_stmt->fetchColumn();
|
||||||
|
if ($_siteWebId && in_array((string)$_siteWebId, array_map('strval', $_checkedFormatsForSiteWeb), true)) {
|
||||||
|
echo '<script>document.getElementById("website-url-fieldset").style.display=""</script>';
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
|
||||||
<!-- ═══════════════════ Métadonnées complémentaires ═══════════════════ -->
|
<!-- ═══════════════════ Métadonnées complémentaires ═══════════════════ -->
|
||||||
<?php
|
<?php
|
||||||
$oldFn = $shareOldFn;
|
$oldFn = $shareOldFn;
|
||||||
|
|||||||
@@ -216,6 +216,9 @@ class ThesisCreateController
|
|||||||
$this->db->handleBannerUpload($thesisId, $files['banner'] ?? null);
|
$this->db->handleBannerUpload($thesisId, $files['banner'] ?? null);
|
||||||
$this->handleThesisFiles($thesisId, $data['annee'], $identifier, $files['files'] ?? null, $authorSlug, $post);
|
$this->handleThesisFiles($thesisId, $data['annee'], $identifier, $files['files'] ?? null, $authorSlug, $post);
|
||||||
|
|
||||||
|
// ── 6. Website URL — stored as thesis_files row ──────────────────────
|
||||||
|
$this->handleWebsiteUrl($thesisId, $post);
|
||||||
|
|
||||||
return $thesisId;
|
return $thesisId;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -813,4 +816,41 @@ class ThesisCreateController
|
|||||||
}
|
}
|
||||||
return $candidate;
|
return $candidate;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Store a website URL as a thesis_files row (file_type = 'website').
|
||||||
|
*
|
||||||
|
* The URL is stored in file_path; no filesystem operation is performed.
|
||||||
|
* label and sort_order from the POST are preserved.
|
||||||
|
*/
|
||||||
|
private function handleWebsiteUrl(int $thesisId, array $post): void
|
||||||
|
{
|
||||||
|
$websiteUrl = trim($post['website_url'] ?? '');
|
||||||
|
if ($websiteUrl === '') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate URL
|
||||||
|
$websiteUrl = filter_var($websiteUrl, FILTER_VALIDATE_URL);
|
||||||
|
if ($websiteUrl === false) {
|
||||||
|
error_log('ThesisCreateController: invalid website URL, skipping');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$label = trim($post['website_label'] ?? '');
|
||||||
|
$sortOrder = isset($post['website_order']) ? (int)$post['website_order'] : null;
|
||||||
|
$fileName = rtrim(preg_replace('#^https?://#i', '', $websiteUrl), '/');
|
||||||
|
|
||||||
|
$this->db->insertThesisFile(
|
||||||
|
$thesisId,
|
||||||
|
'website',
|
||||||
|
$websiteUrl,
|
||||||
|
$fileName,
|
||||||
|
0,
|
||||||
|
'text/html',
|
||||||
|
$label !== '' ? $label : null,
|
||||||
|
$sortOrder
|
||||||
|
);
|
||||||
|
error_log("ThesisCreateController: website stored → $websiteUrl");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -331,12 +331,15 @@ class ThesisEditController
|
|||||||
}
|
}
|
||||||
$filePath = $this->db->deleteThesisFile($fileId, $thesisId);
|
$filePath = $this->db->deleteThesisFile($fileId, $thesisId);
|
||||||
if ($filePath && defined('STORAGE_ROOT')) {
|
if ($filePath && defined('STORAGE_ROOT')) {
|
||||||
|
// Skip filesystem deletion for website URLs (not real files)
|
||||||
|
if (!str_starts_with($filePath, 'http://') && !str_starts_with($filePath, 'https://')) {
|
||||||
$abs = STORAGE_ROOT . '/' . $filePath;
|
$abs = STORAGE_ROOT . '/' . $filePath;
|
||||||
if (file_exists($abs)) {
|
if (file_exists($abs)) {
|
||||||
@unlink($abs);
|
@unlink($abs);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// ── Reorder existing files ────────────────────────────────────────────
|
// ── Reorder existing files ────────────────────────────────────────────
|
||||||
if (!empty($post['file_sort_order']) && is_array($post['file_sort_order'])) {
|
if (!empty($post['file_sort_order']) && is_array($post['file_sort_order'])) {
|
||||||
@@ -358,6 +361,9 @@ class ThesisEditController
|
|||||||
if (!empty($files['files']['name'][0])) {
|
if (!empty($files['files']['name'][0])) {
|
||||||
$this->handleThesisFiles($thesisId, $post, $files['files']);
|
$this->handleThesisFiles($thesisId, $post, $files['files']);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ── Website URL — add or update ──────────────────────────────────────
|
||||||
|
$this->handleWebsiteUrl($thesisId, $post);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── Private: file uploads ─────────────────────────────────────────────────
|
// ── Private: file uploads ─────────────────────────────────────────────────
|
||||||
@@ -703,4 +709,50 @@ class ThesisEditController
|
|||||||
}
|
}
|
||||||
return $info;
|
return $info;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add or update a website URL thesis_file row.
|
||||||
|
*
|
||||||
|
* If a website row already exists for this thesis, it is replaced.
|
||||||
|
* Otherwise a new row is inserted.
|
||||||
|
*/
|
||||||
|
private function handleWebsiteUrl(int $thesisId, array $post): void
|
||||||
|
{
|
||||||
|
$websiteUrl = trim($post['website_url'] ?? '');
|
||||||
|
|
||||||
|
// Remove existing website rows
|
||||||
|
$existingFiles = $this->db->getThesisFiles($thesisId);
|
||||||
|
foreach ($existingFiles as $f) {
|
||||||
|
if ($f['file_type'] === 'website') {
|
||||||
|
$this->db->deleteThesisFile((int)$f['id'], $thesisId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($websiteUrl === '') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate URL
|
||||||
|
$websiteUrl = filter_var($websiteUrl, FILTER_VALIDATE_URL);
|
||||||
|
if ($websiteUrl === false) {
|
||||||
|
error_log('ThesisEditController: invalid website URL, skipping');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$label = trim($post['website_label'] ?? '');
|
||||||
|
$sortOrder = isset($post['website_order']) ? (int)$post['website_order'] : null;
|
||||||
|
$fileName = rtrim(preg_replace('#^https?://#i', '', $websiteUrl), '/');
|
||||||
|
|
||||||
|
$this->db->insertThesisFile(
|
||||||
|
$thesisId,
|
||||||
|
'website',
|
||||||
|
$websiteUrl,
|
||||||
|
$fileName,
|
||||||
|
0,
|
||||||
|
'text/html',
|
||||||
|
$label !== '' ? $label : null,
|
||||||
|
$sortOrder
|
||||||
|
);
|
||||||
|
error_log("ThesisEditController: website stored → $websiteUrl");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,12 +36,6 @@
|
|||||||
<?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'; ?>
|
<?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>
|
</fieldset>
|
||||||
|
|
||||||
<!-- ═══════════════════ Format(s) ═══════════════════ -->
|
|
||||||
<fieldset>
|
|
||||||
<legend>Format(s)</legend>
|
|
||||||
<?php $name = 'formats'; $label = 'Format(s) du TFE :'; $options = $formatTypes; $checked = $formData['formats'] ?? []; $required = true; include APP_ROOT . '/templates/partials/form/checkbox-list.php'; ?>
|
|
||||||
</fieldset>
|
|
||||||
|
|
||||||
<!-- ═══════════════════ Mots-clés ═══════════════════ -->
|
<!-- ═══════════════════ Mots-clés ═══════════════════ -->
|
||||||
<fieldset>
|
<fieldset>
|
||||||
<legend>Mots-clés</legend>
|
<legend>Mots-clés</legend>
|
||||||
@@ -74,9 +68,59 @@
|
|||||||
require APP_ROOT . '/templates/partials/form/jury-fieldset.php';
|
require APP_ROOT . '/templates/partials/form/jury-fieldset.php';
|
||||||
?>
|
?>
|
||||||
|
|
||||||
|
<!-- ═══════════════════ Format(s) ═══════════════════ -->
|
||||||
|
<fieldset>
|
||||||
|
<legend>Format(s)</legend>
|
||||||
|
<?php
|
||||||
|
$name = 'formats'; $label = 'Format(s) du TFE :'; $options = $formatTypes; $checked = $formData['formats'] ?? []; $required = true;
|
||||||
|
$hxPost = '/partage/format-website-fragment';
|
||||||
|
$hxTarget = '#website-url-fieldset';
|
||||||
|
// Capture before include unsets it
|
||||||
|
$_checkedFormatsForSiteWeb = $checked;
|
||||||
|
include APP_ROOT . '/templates/partials/form/checkbox-list.php';
|
||||||
|
?>
|
||||||
|
</fieldset>
|
||||||
|
|
||||||
<!-- ═══════════════════ Fichiers ═══════════════════ -->
|
<!-- ═══════════════════ Fichiers ═══════════════════ -->
|
||||||
<?php include APP_ROOT . '/templates/partials/form/fieldset-files.php'; ?>
|
<?php include APP_ROOT . '/templates/partials/form/fieldset-files.php'; ?>
|
||||||
|
|
||||||
|
<!-- Website URL fieldset — shown/hidden via HTMX when "Site web" checked -->
|
||||||
|
<fieldset id="website-url-fieldset" style="display:none">
|
||||||
|
<legend>Site web</legend>
|
||||||
|
|
||||||
|
<div class="admin-form-group">
|
||||||
|
<label for="website_url">URL du site :</label>
|
||||||
|
<div class="admin-file-input">
|
||||||
|
<input type="url"
|
||||||
|
id="website_url"
|
||||||
|
name="website_url"
|
||||||
|
value="<?= htmlspecialchars($formData['website_url'] ?? '') ?>"
|
||||||
|
placeholder="https://mon-tfe.erg.be">
|
||||||
|
<small>Si le TFE est un site web, entrez son URL ici. Il sera affiché comme un site embarqué sur la page du TFE.</small>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="admin-form-group">
|
||||||
|
<label for="website_label">Légende :</label>
|
||||||
|
<input type="text"
|
||||||
|
id="website_label"
|
||||||
|
name="website_label"
|
||||||
|
value="<?= htmlspecialchars($formData['website_label'] ?? '') ?>"
|
||||||
|
placeholder="Description du site (optionnel)"
|
||||||
|
class="admin-file-label-input"
|
||||||
|
style="max-width:400px;">
|
||||||
|
</div>
|
||||||
|
</fieldset>
|
||||||
|
<?php
|
||||||
|
// Server-side: show if Site web already checked (e.g. on error redirect)
|
||||||
|
$_stmt = Database::getInstance()->getConnection()->prepare('SELECT id FROM format_types WHERE name = ? LIMIT 1');
|
||||||
|
$_stmt->execute(['Site web']);
|
||||||
|
$_siteWebId = $_stmt->fetchColumn();
|
||||||
|
if ($_siteWebId && in_array((string)$_siteWebId, array_map('strval', $_checkedFormatsForSiteWeb), true)) {
|
||||||
|
echo '<script>document.getElementById("website-url-fieldset").style.display=""</script>';
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
|
||||||
<!-- ═══════════════════ Métadonnées complémentaires ═══════════════════ -->
|
<!-- ═══════════════════ Métadonnées complémentaires ═══════════════════ -->
|
||||||
<?php
|
<?php
|
||||||
$oldFn = 'old';
|
$oldFn = 'old';
|
||||||
|
|||||||
@@ -60,16 +60,6 @@
|
|||||||
<?php $name = 'language_autre'; $label = 'Autre(s) langue(s) :'; $value = old('language_autre'); include APP_ROOT . '/templates/partials/form/text-field.php'; ?>
|
<?php $name = 'language_autre'; $label = 'Autre(s) langue(s) :'; $value = old('language_autre'); include APP_ROOT . '/templates/partials/form/text-field.php'; ?>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
|
|
||||||
<!-- ═══════════════════ Format(s) ═══════════════════ -->
|
|
||||||
<fieldset>
|
|
||||||
<legend>Format(s)</legend>
|
|
||||||
<?php
|
|
||||||
$checkedFormats = $formData['formats'] ?? $currentFormats;
|
|
||||||
$name = 'formats'; $label = 'Format(s) du TFE :'; $options = $formatTypes; $checked = $checkedFormats; $required = true;
|
|
||||||
include APP_ROOT . '/templates/partials/form/checkbox-list.php';
|
|
||||||
?>
|
|
||||||
</fieldset>
|
|
||||||
|
|
||||||
<!-- ═══════════════════ Mots-clés ═══════════════════ -->
|
<!-- ═══════════════════ Mots-clés ═══════════════════ -->
|
||||||
<fieldset>
|
<fieldset>
|
||||||
<legend>Mots-clés</legend>
|
<legend>Mots-clés</legend>
|
||||||
@@ -132,6 +122,20 @@
|
|||||||
require APP_ROOT . '/templates/partials/form/jury-fieldset.php';
|
require APP_ROOT . '/templates/partials/form/jury-fieldset.php';
|
||||||
?>
|
?>
|
||||||
|
|
||||||
|
<!-- ═══════════════════ Format(s) ═══════════════════ -->
|
||||||
|
<fieldset>
|
||||||
|
<legend>Format(s)</legend>
|
||||||
|
<?php
|
||||||
|
$checkedFormats = $formData['formats'] ?? $currentFormats ?? [];
|
||||||
|
$name = 'formats'; $label = 'Format(s) du TFE :'; $options = $formatTypes; $checked = $checkedFormats; $required = true;
|
||||||
|
$hxPost = '/partage/format-website-fragment';
|
||||||
|
$hxTarget = '#website-url-fieldset';
|
||||||
|
// Capture before include unsets it
|
||||||
|
$_checkedFormatsForSiteWeb = $checked;
|
||||||
|
include APP_ROOT . '/templates/partials/form/checkbox-list.php';
|
||||||
|
?>
|
||||||
|
</fieldset>
|
||||||
|
|
||||||
<!-- ═══════════════════ Fichiers ═══════════════════ -->
|
<!-- ═══════════════════ Fichiers ═══════════════════ -->
|
||||||
<fieldset>
|
<fieldset>
|
||||||
<legend>Fichiers</legend>
|
<legend>Fichiers</legend>
|
||||||
@@ -173,20 +177,25 @@
|
|||||||
$fType === 'video' || in_array($fExt, ['mp4','webm','mov','ogv']) => '🎬',
|
$fType === 'video' || in_array($fExt, ['mp4','webm','mov','ogv']) => '🎬',
|
||||||
$fType === 'audio' || in_array($fExt, ['mp3','ogg','wav','flac','aac','m4a']) => '🔊',
|
$fType === 'audio' || in_array($fExt, ['mp3','ogg','wav','flac','aac','m4a']) => '🔊',
|
||||||
$fType === 'caption' || $fExt === 'vtt' => '💬',
|
$fType === 'caption' || $fExt === 'vtt' => '💬',
|
||||||
|
$fType === 'website' => '🌐',
|
||||||
default => '📎',
|
default => '📎',
|
||||||
};
|
};
|
||||||
|
$isExternalUrl = str_starts_with($f['file_path'] ?? '', 'http://') || str_starts_with($f['file_path'] ?? '', 'https://');
|
||||||
|
$fLinkHref = $isExternalUrl
|
||||||
|
? htmlspecialchars($f['file_path'])
|
||||||
|
: ('/media.php?path=' . urlencode($f['file_path']));
|
||||||
?>
|
?>
|
||||||
<li class="admin-file-list-item" data-file-id="<?= (int)$f['id'] ?>">
|
<li class="admin-file-list-item" data-file-id="<?= (int)$f['id'] ?>">
|
||||||
<input type="hidden" name="file_sort_order[]" value="<?= (int)$f['id'] ?>">
|
<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-drag-handle" title="Réordonner">⠿</span>
|
||||||
<span class="admin-file-icon-col"><?= $fIcon ?></span>
|
<span class="admin-file-icon-col"><?= $fIcon ?></span>
|
||||||
<span class="admin-file-info">
|
<span class="admin-file-info">
|
||||||
<a href="/media.php?path=<?= urlencode($f['file_path']) ?>" target="_blank" rel="noopener" class="admin-file-name">
|
<a href="<?= $fLinkHref ?>" target="_blank" rel="noopener" class="admin-file-name">
|
||||||
<?= htmlspecialchars($f['file_name'] ?? basename($f['file_path'])) ?>
|
<?= htmlspecialchars($f['file_name'] ?? basename($f['file_path'])) ?>
|
||||||
</a>
|
</a>
|
||||||
<span class="admin-file-meta-row">
|
<span class="admin-file-meta-row">
|
||||||
<span class="admin-file-type-badge"><?= htmlspecialchars($fType) ?></span>
|
<span class="admin-file-type-badge"><?= htmlspecialchars($fType) ?></span>
|
||||||
<?php if (!empty($f['file_size'])): ?>
|
<?php if (!empty($f['file_size']) && $f['file_size'] > 0): ?>
|
||||||
<span class="admin-file-size"><?= number_format($f['file_size'] / 1024 / 1024, 2) ?> MB</span>
|
<span class="admin-file-size"><?= number_format($f['file_size'] / 1024 / 1024, 2) ?> MB</span>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
</span>
|
</span>
|
||||||
@@ -239,6 +248,55 @@
|
|||||||
</div>
|
</div>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
|
|
||||||
|
<!-- Website URL fieldset — shown/hidden via HTMX when "Site web" checked -->
|
||||||
|
<?php
|
||||||
|
// Extract existing website URL/label from thesis_files for initial render
|
||||||
|
$existingWebsite = null;
|
||||||
|
$existingWebsiteLabel = '';
|
||||||
|
foreach ($currentFiles as $f) {
|
||||||
|
if ($f['file_type'] === 'website') {
|
||||||
|
$existingWebsite = $f['file_path'] ?? '';
|
||||||
|
$existingWebsiteLabel = $f['display_label'] ?? '';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
<fieldset id="website-url-fieldset" style="display:none">
|
||||||
|
<legend>Site web</legend>
|
||||||
|
|
||||||
|
<div class="admin-form-group">
|
||||||
|
<label for="website_url">URL du site :</label>
|
||||||
|
<div class="admin-file-input">
|
||||||
|
<input type="url"
|
||||||
|
id="website_url"
|
||||||
|
name="website_url"
|
||||||
|
value="<?= htmlspecialchars($existingWebsite ?? '') ?>"
|
||||||
|
placeholder="https://mon-tfe.erg.be">
|
||||||
|
<small>Si le TFE est un site web, entrez son URL ici. Il sera affiché comme un site embarqué sur la page du TFE.</small>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="admin-form-group">
|
||||||
|
<label for="website_label">Légende :</label>
|
||||||
|
<input type="text"
|
||||||
|
id="website_label"
|
||||||
|
name="website_label"
|
||||||
|
value="<?= htmlspecialchars($existingWebsiteLabel) ?>"
|
||||||
|
placeholder="Description du site (optionnel)"
|
||||||
|
class="admin-file-label-input"
|
||||||
|
style="max-width:400px;">
|
||||||
|
</div>
|
||||||
|
</fieldset>
|
||||||
|
<?php
|
||||||
|
// Server-side: show if Site web already checked
|
||||||
|
$_stmt = Database::getInstance()->getConnection()->prepare('SELECT id FROM format_types WHERE name = ? LIMIT 1');
|
||||||
|
$_stmt->execute(['Site web']);
|
||||||
|
$_siteWebId = $_stmt->fetchColumn();
|
||||||
|
if ($_siteWebId && in_array((string)$_siteWebId, array_map('strval', $_checkedFormatsForSiteWeb), true)) {
|
||||||
|
echo '<script>document.getElementById("website-url-fieldset").style.display=""</script>';
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
|
||||||
<!-- ═══════════════════ Métadonnées complémentaires ═══════════════════ -->
|
<!-- ═══════════════════ Métadonnées complémentaires ═══════════════════ -->
|
||||||
<?php
|
<?php
|
||||||
$editMetaFormData = [
|
$editMetaFormData = [
|
||||||
|
|||||||
@@ -13,14 +13,26 @@
|
|||||||
* array $options — each element must have 'id' and 'name' keys
|
* array $options — each element must have 'id' and 'name' keys
|
||||||
* array $checked — array of 'id' values that are currently checked
|
* array $checked — array of 'id' values that are currently checked
|
||||||
* bool $required — whether at least one checkbox must be checked; default false
|
* bool $required — whether at least one checkbox must be checked; default false
|
||||||
|
* string $hxPost — optional hx-post URL for HTMX live update
|
||||||
|
* string $hxTarget — optional hx-target CSS selector for HTMX swap
|
||||||
*/
|
*/
|
||||||
|
|
||||||
$checked = $checked ?? [];
|
$checked = $checked ?? [];
|
||||||
$required = $required ?? false;
|
$required = $required ?? false;
|
||||||
|
$hxPost = $hxPost ?? '';
|
||||||
|
$hxTarget = $hxTarget ?? '';
|
||||||
?>
|
?>
|
||||||
<div>
|
<div>
|
||||||
<span class="admin-row-label"><?= htmlspecialchars($label) ?><?= $required ? ' <span class="asterisk">*</span>' : '' ?></span>
|
<span class="admin-row-label"><?= htmlspecialchars($label) ?><?= $required ? ' <span class="asterisk">*</span>' : '' ?></span>
|
||||||
<fieldset class="admin-checkbox-group"<?= $required ? ' required aria-required="true"' : '' ?>>
|
<fieldset class="admin-checkbox-group"
|
||||||
|
<?= $required ? ' required aria-required="true"' : '' ?>
|
||||||
|
<?php if ($hxPost !== ''): ?>
|
||||||
|
hx-post="<?= htmlspecialchars($hxPost) ?>"
|
||||||
|
hx-target="<?= htmlspecialchars($hxTarget) ?>"
|
||||||
|
hx-trigger="change"
|
||||||
|
hx-include="this, #website-url-fieldset"
|
||||||
|
hx-swap="outerHTML"
|
||||||
|
<?php endif; ?>>
|
||||||
<legend class="sr-only"><?= htmlspecialchars($label) ?></legend>
|
<legend class="sr-only"><?= htmlspecialchars($label) ?></legend>
|
||||||
<ul>
|
<ul>
|
||||||
<?php foreach ($options as $opt): ?>
|
<?php foreach ($options as $opt): ?>
|
||||||
@@ -38,4 +50,4 @@ $required = $required ?? false;
|
|||||||
</fieldset>
|
</fieldset>
|
||||||
</div>
|
</div>
|
||||||
<?php
|
<?php
|
||||||
unset($checked);
|
unset($checked, $hxPost, $hxTarget);
|
||||||
|
|||||||
@@ -442,7 +442,8 @@
|
|||||||
$isVideo = in_array($ext, ['mp4','webm','mov','ogv'], true) || $fileType === 'video';
|
$isVideo = in_array($ext, ['mp4','webm','mov','ogv'], true) || $fileType === 'video';
|
||||||
$isAudio = in_array($ext, ['mp3','ogg','oga','wav','flac','aac','m4a'], true) || $fileType === 'audio';
|
$isAudio = in_array($ext, ['mp3','ogg','oga','wav','flac','aac','m4a'], true) || $fileType === 'audio';
|
||||||
$isPdf = ($ext === 'pdf') || $fileType === 'main';
|
$isPdf = ($ext === 'pdf') || $fileType === 'main';
|
||||||
$isOther = !($isImage || $isVideo || $isAudio || $isPdf);
|
$isWebsite = ($fileType === 'website');
|
||||||
|
$isOther = !($isImage || $isVideo || $isAudio || $isPdf || $isWebsite);
|
||||||
|
|
||||||
$_vttPath = null;
|
$_vttPath = null;
|
||||||
if ($isVideo) {
|
if ($isVideo) {
|
||||||
@@ -451,8 +452,10 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
$caption = !empty($file["display_label"]) ? $file["display_label"] : ($file["description"] ?? '');
|
$caption = !empty($file["display_label"]) ? $file["display_label"] : ($file["description"] ?? '');
|
||||||
$mediaUrl = '/media?path=' . urlencode($file["file_path"]);
|
$filePath = $file['file_path'] ?? '';
|
||||||
$fileName = htmlspecialchars($file["file_name"] ?? basename($file["file_path"]));
|
$isExternalUrl = str_starts_with($filePath, 'http://') || str_starts_with($filePath, 'https://');
|
||||||
|
$mediaUrl = $isExternalUrl ? htmlspecialchars($filePath) : ('/media?path=' . urlencode($filePath));
|
||||||
|
$fileName = htmlspecialchars($file["file_name"] ?? basename($filePath));
|
||||||
?>
|
?>
|
||||||
<figure>
|
<figure>
|
||||||
<?php if ($isPdf): ?>
|
<?php if ($isPdf): ?>
|
||||||
@@ -464,6 +467,20 @@
|
|||||||
<p class="tfe-pdf-fallback">
|
<p class="tfe-pdf-fallback">
|
||||||
<a href="<?= $mediaUrl ?>&download=1">Télécharger le PDF</a>
|
<a href="<?= $mediaUrl ?>&download=1">Télécharger le PDF</a>
|
||||||
</p>
|
</p>
|
||||||
|
<?php elseif ($isWebsite): ?>
|
||||||
|
<iframe src="<?= $mediaUrl ?>"
|
||||||
|
width="100%" height="700px"
|
||||||
|
style="border:none"
|
||||||
|
title="<?= $fileName ?>"
|
||||||
|
sandbox="allow-scripts allow-same-origin"
|
||||||
|
loading="lazy">
|
||||||
|
</iframe>
|
||||||
|
<p class="tfe-pdf-fallback">
|
||||||
|
<a href="<?= $mediaUrl ?>" target="_blank" rel="noopener">
|
||||||
|
Ouvrir le site dans un nouvel onglet
|
||||||
|
<span class="sr-only">(ouvre dans un nouvel onglet)</span>
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
<?php elseif ($isImage): ?>
|
<?php elseif ($isImage): ?>
|
||||||
<img src="<?= $mediaUrl ?>"
|
<img src="<?= $mediaUrl ?>"
|
||||||
alt="<?= htmlspecialchars($caption !== '' ? $caption : $data['title'] . ' — ' . ($data['authors'] ?? '')) ?>">
|
alt="<?= htmlspecialchars($caption !== '' ? $caption : $data['title'] . ' — ' . ($data['authors'] ?? '')) ?>">
|
||||||
|
|||||||
Reference in New Issue
Block a user