diff --git a/TODO.md b/TODO.md
index f36a2fe..48a9c7d 100644
--- a/TODO.md
+++ b/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_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
- [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
diff --git a/app/public/assets/css/form.css b/app/public/assets/css/form.css
index 5ca8d06..4b34aa3 100644
--- a/app/public/assets/css/form.css
+++ b/app/public/assets/css/form.css
@@ -204,6 +204,11 @@
}
/* ββ 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,
.student-body fieldset fieldset.admin-jury-lecteurs {
border: none;
diff --git a/app/public/partage/format-website-fragment.php b/app/public/partage/format-website-fragment.php
new file mode 100644
index 0000000..d47cb0d
--- /dev/null
+++ b/app/public/partage/format-website-fragment.php
@@ -0,0 +1,66 @@
+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'] ?? '');
+?>
+
diff --git a/app/public/partage/index.php b/app/public/partage/index.php
index 49005cf..95f20d1 100644
--- a/app/public/partage/index.php
+++ b/app/public/partage/index.php
@@ -21,6 +21,13 @@ $parts = explode('/', $path);
$slug = $parts[0] ?? '';
$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
if ($slug === 'recapitulatif' || $slug === 'recapitulatif.php') {
App::boot();
@@ -265,6 +272,7 @@ function renderShareLinkForm(string $slug, array $link): void
+
@@ -319,12 +327,6 @@ function renderShareLinkForm(string $slug, array $link): void
-
-
-
-
-
-
-
-
-
@@ -74,9 +68,59 @@
require APP_ROOT . '/templates/partials/form/jury-fieldset.php';
?>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 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 '';
+ }
+ ?>
+
-
-
-
-
-
-
@@ -132,6 +122,20 @@
require APP_ROOT . '/templates/partials/form/jury-fieldset.php';
?>
+
+
+
+
+
+
@@ -173,20 +177,25 @@
$fType === 'video' || in_array($fExt, ['mp4','webm','mov','ogv']) => 'π¬',
$fType === 'audio' || in_array($fExt, ['mp3','ogg','wav','flac','aac','m4a']) => 'π',
$fType === 'caption' || $fExt === 'vtt' => 'π¬',
+ $fType === 'website' => 'π',
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']));
?>
β Ώ
= $fIcon ?>
-
+
= htmlspecialchars($f['file_name'] ?? basename($f['file_path'])) ?>
= htmlspecialchars($fType) ?>
-
+ 0): ?>
= number_format($f['file_size'] / 1024 / 1024, 2) ?> MB
@@ -239,6 +248,55 @@
+
+
+
+
+
+
+
+
+
+
+
+
+ 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 '';
+ }
+ ?>
+
= htmlspecialchars($label) ?>= $required ? ' *' : '' ?>
-
>
+
+
+ hx-post="= htmlspecialchars($hxPost) ?>"
+ hx-target="= htmlspecialchars($hxTarget) ?>"
+ hx-trigger="change"
+ hx-include="this, #website-url-fieldset"
+ hx-swap="outerHTML"
+ >
@@ -464,6 +467,20 @@
TΓ©lΓ©charger le PDF
+
+
+
+
+ Ouvrir le site dans un nouvel onglet
+ (ouvre dans un nouvel onglet)
+
+