feat: refactor licence fragment — Libre→CC2r+licence, Interne→opt-in want_license, Interdit→none, add details/summary to license UI

This commit is contained in:
Pontoporeia
2026-05-11 14:19:44 +02:00
parent 927ee2fe2a
commit 8bf95f4e14
10 changed files with 809 additions and 109 deletions

View File

@@ -22,6 +22,7 @@
- [x] Decorelate formats from fichiers: no HTMX toggling; Site web/Vidéo/Audio always visible - [x] Decorelate formats from fichiers: no HTMX toggling; Site web/Vidéo/Audio always visible
- [x] Sticky formats fieldset inside parent container - [x] Sticky formats fieldset inside parent container
- [x] Server-side progress: PeerTubeService writes to temp file, client polls progress endpoint - [x] Server-side progress: PeerTubeService writes to temp file, client polls progress endpoint
- [x] Refactor licence fieldset: Libre→CC2r+licence, Interne→opt-in licence, Interdit→none
## HTMX Toast Feedback for Settings Checkboxes (contenus.php) ## HTMX Toast Feedback for Settings Checkboxes (contenus.php)

View File

@@ -1,20 +1,22 @@
<?php <?php
/** /**
* HTMX fragment (admin): renders the licence section with conditional required states. * HTMX fragment (admin): renders the licence section with conditional options.
* *
* POST: access_type_id, license_id, license_custom, cc2r, admin_mode * POST: access_type_id, license_id, license_custom, cc2r, want_license
* *
* Admin mode: never required. * Libre (1): CC2r checkbox + licence select/custom (at least one required)
* Interne (2): opt-in "Je souhaite appliquer une licence" → CC2r + licence select/custom
* Interdit (3): no licence options
*/ */
require_once __DIR__ . '/../../bootstrap.php'; require_once __DIR__ . '/../../bootstrap.php';
require_once __DIR__ . '/../../src/AdminAuth.php'; require_once __DIR__ . '/../../src/AdminAuth.php';
AdminAuth::requireLogin(); AdminAuth::requireLogin();
$accessTypeId = $_POST['access_type_id'] ?? '2'; $accessTypeId = $_POST['access_type_id'] ?? '';
$licenseId = $_POST['license_id'] ?? ''; $licenseId = $_POST['license_id'] ?? '';
$licenseCustom = $_POST['license_custom'] ?? ''; $licenseCustom = $_POST['license_custom'] ?? '';
$cc2r = !empty($_POST['cc2r']); $cc2r = !empty($_POST['cc2r']);
$adminMode = true; $wantLicense = !empty($_POST['want_license']);
require_once APP_ROOT . '/src/Database.php'; require_once APP_ROOT . '/src/Database.php';
$db = Database::getInstance(); $db = Database::getInstance();
@@ -22,6 +24,22 @@ $licenseTypes = $db->getAllLicenseTypes();
?> ?>
<div class="licence-license-choice"> <div class="licence-license-choice">
<?php if ($accessTypeId === '1'): ?>
<fieldset class="licence-options-fieldset">
<legend>Options de licence</legend>
<p class="licence-note">J'autorise que mon TFE soit disponible en libre accès. Je suis conscient·e des responsabilités légales.</p>
<div class="admin-form-group">
<label class="admin-checkbox-label">
<input type="checkbox" name="cc2r" value="1"
<?= $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 class="licence-or-separator"><span>et / ou</span></div>
<?php <?php
$name = 'license_id'; $label = 'Licence :'; $options = $licenseTypes; $name = 'license_id'; $label = 'Licence :'; $options = $licenseTypes;
$selected = $licenseId; $placeholder = '— Sélectionner —'; $required = false; $selected = $licenseId; $placeholder = '— Sélectionner —'; $required = false;
@@ -31,16 +49,61 @@ $licenseTypes = $db->getAllLicenseTypes();
<?php <?php
$name = 'license_custom'; $label = 'Ou précisez une autre licence :'; $name = 'license_custom'; $label = 'Ou précisez une autre licence :';
$value = htmlspecialchars($licenseCustom); $value = htmlspecialchars($licenseCustom);
$hint = 'Ex: CC BY-NC 4.0, Tous droits réservés...'; $hint = 'Ex: CC BY-NC 4.0, Tous droits réservés';
include APP_ROOT . '/templates/partials/form/text-field.php'; include APP_ROOT . '/templates/partials/form/text-field.php';
?> ?>
</fieldset>
<?php elseif ($accessTypeId === '2'): ?>
<fieldset class="licence-options-fieldset">
<legend>Options de licence</legend>
<p class="licence-note">Mon TFE est accessible sur place et sur la plateforme xamxam par la communauté erg. J'autorise une (ré-)utilisation dans un contexte académique au sein de l'erg.</p>
<div class="admin-form-group">
<label class="admin-checkbox-label">
<input type="checkbox" name="want_license" value="1"
hx-post="/admin/licence-fragment.php"
hx-target=".licence-license-choice"
hx-swap="outerHTML"
hx-include="closest fieldset"
<?= $wantLicense ? 'checked' : '' ?>>
<strong>Je souhaite appliquer une licence sur mon TFE</strong>
</label>
<small>Par défaut, aucune licence spécifique n'est appliquée. Cochez pour en choisir une.</small>
</div>
<?php if ($wantLicense): ?>
<div class="admin-form-group"> <div class="admin-form-group">
<label class="admin-checkbox-label"> <label class="admin-checkbox-label">
<input type="checkbox" name="cc2r" value="1" <input type="checkbox" name="cc2r" value="1"
<?= $cc2r ? 'checked' : '' ?>> <?= $cc2r ? 'checked' : '' ?>>
J'accepte les Conditions Collectives de Réutilisation (CC2r) J'accepte les conditions collectives de réutilisation (CC2r)
</label> </label>
<small><a href="https://cc2r.net/" target="_blank" rel="noopener">En savoir plus sur la CC2r ↗</a></small> <small><a href="https://cc2r.net/" target="_blank" rel="noopener">En savoir plus sur la CC2r ↗</a></small>
</div> </div>
<?php
$name = 'license_id'; $label = 'Licence :'; $options = $licenseTypes;
$selected = $licenseId; $placeholder = '— Sélectionner —'; $required = false;
include APP_ROOT . '/templates/partials/form/select-field.php';
?>
<?php
$name = 'license_custom'; $label = 'Ou précisez une autre licence :';
$value = htmlspecialchars($licenseCustom);
$hint = 'Ex: CC BY-NC 4.0, Tous droits réservés…';
include APP_ROOT . '/templates/partials/form/text-field.php';
?>
<p class="licence-note"><em>Je suis conscient·e des obligations légales venant avec la licence choisie et acquiesce avoir lu la documentation prévue à cet effet par l'erg, ainsi qu'avoir discuté des enjeux des licences avec l'équipe pédagogique.</em></p>
<?php endif; ?>
</fieldset>
<?php elseif ($accessTypeId === '3'): ?>
<fieldset class="licence-options-fieldset">
<legend>Options de licence</legend>
<p class="licence-note">Mon TFE n'est pas disponible en physique ni sur le site. Une note descriptive est visible publiquement.</p>
<p class="licence-note"><em>Aucune licence n'est applicable.</em></p>
</fieldset>
<?php endif; ?>
</div> </div>

View File

@@ -743,3 +743,18 @@ details > summary::-webkit-details-marker {
audio::-webkit-media-controls-enclosure { audio::-webkit-media-controls-enclosure {
border-radius: var(--radius); border-radius: var(--radius);
} }
details {
padding: var(--space-s);
}
summary {
font-family: var(--font-display);
font-weight: 600;
text-decoration: 1px wavy;
color: var(--accent-secondary);
svg {
fill: var(--accent-secondary);
vertical-align: text-bottom;
height: 1.4em;
}
}

View File

@@ -1362,3 +1362,135 @@ legend {
#format-fichiers-block { #format-fichiers-block {
container-type: inline-size; container-type: inline-size;
} }
/* ── Licence choice or-separator ─────────────────────────── */
.licence-or-separator {
display: flex;
align-items: center;
gap: var(--space-xs);
margin: var(--space-xs) 0;
color: var(--text-tertiary);
font-size: var(--step--1);
}
.licence-or-separator::before,
.licence-or-separator::after {
content: '';
flex: 1;
height: 1px;
background: var(--accent-muted);
}
/* ── Nested licence fieldset ─────────────────────────────── */
.licence-options-fieldset {
border: 1px solid var(--accent-muted);
border-radius: var(--radius);
padding: var(--space-s);
margin-top: var(--space-xs);
}
.licence-options-fieldset legend {
font-weight: 600;
font-size: var(--step--1);
color: var(--text-secondary);
padding: 0 var(--space-2xs);
}
/* ── File figures (recap + edit existing files) ──────────── */
.admin-file-list,
.recap-files-list {
list-style: none;
padding: 0;
margin: 0;
display: flex;
flex-direction: column;
gap: var(--space-s);
}
.admin-file-list-item,
.recap-files-list-item {
list-style: none;
}
.admin-file-figure,
.recap-file-figure {
display: flex;
align-items: flex-start;
gap: var(--space-s);
background: var(--bg-secondary, rgba(149, 87, 181, 0.05));
border-radius: var(--radius);
padding: var(--space-s);
margin: 0;
}
.admin-file-icon,
.recap-file-icon {
font-size: 1.5rem;
flex-shrink: 0;
width: 2.5rem;
height: 2.5rem;
display: flex;
align-items: center;
justify-content: center;
background: var(--accent-muted);
border-radius: var(--radius);
}
.admin-file-thumb,
.recap-file-thumb {
max-width: 160px;
max-height: 120px;
object-fit: cover;
border-radius: var(--radius);
flex-shrink: 0;
}
.admin-file-caption,
.recap-file-caption {
flex: 1;
display: flex;
flex-direction: column;
gap: var(--space-3xs);
}
.admin-file-name,
.recap-file-name {
font-weight: 600;
font-size: var(--step--1);
color: var(--text-primary);
}
.admin-file-peertube-id,
.recap-file-peertube-id {
font-size: var(--step--2);
color: var(--text-tertiary);
font-family: monospace;
}
.admin-file-meta-row,
.recap-file-meta-row {
display: flex;
gap: var(--space-xs);
align-items: center;
font-size: var(--step--2);
color: var(--text-tertiary);
}
.recap-file-label {
font-size: var(--step--1);
color: var(--text-secondary);
font-style: italic;
}
.admin-file-label-input {
font-size: var(--step--2);
padding: var(--space-3xs) var(--space-2xs);
border: 1px solid var(--accent-muted);
border-radius: var(--radius);
background: var(--bg-primary);
color: var(--text-primary);
margin-top: var(--space-3xs);
}

View File

@@ -79,20 +79,19 @@ $websiteLabel = htmlspecialchars($_POST['website_label'] ?? '');
if ($editMode && (!empty($_efiles) || $_cover)): if ($editMode && (!empty($_efiles) || $_cover)):
?> ?>
<div class="admin-form-group"> <div class="admin-form-group">
<ul id="existing-files-sortable" class="admin-file-list"> <ul id="existing-files-sortable" class="admin-file-list" role="list">
<?php if ($_cover): ?> <?php if ($_cover): ?>
<li class="admin-file-list-item"> <li class="admin-file-list-item">
<span class="admin-file-icon-col">🖼️</span> <figure class="admin-file-figure">
<span class="admin-file-info">
<span class="admin-file-name">Couverture</span>
<span class="admin-file-meta-row">
<img src="/media?path=<?= urlencode($_cover) ?>" <img src="/media?path=<?= urlencode($_cover) ?>"
alt="Couverture actuelle" style="max-height:120px;border-radius:var(--radius);margin-top:var(--space-3xs);"> alt="Couverture actuelle" class="admin-file-thumb">
</span> <figcaption class="admin-file-caption">
<span class="admin-file-name">Couverture</span>
<label class="admin-checkbox-label" style="margin-top:var(--space-3xs);"> <label class="admin-checkbox-label" style="margin-top:var(--space-3xs);">
<input type="checkbox" name="remove_cover" value="1"> Supprimer <input type="checkbox" name="remove_cover" value="1"> Supprimer
</label> </label>
</span> </figcaption>
</figure>
</li> </li>
<?php endif; ?> <?php endif; ?>
<?php <?php
@@ -118,11 +117,15 @@ $websiteLabel = htmlspecialchars($_POST['website_label'] ?? '');
?> ?>
<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-icon-col"><?= $_fIcon ?></span> <figure class="admin-file-figure">
<span class="admin-file-info"> <span class="admin-file-icon"><?= $_fIcon ?></span>
<figcaption class="admin-file-caption">
<a href="<?= $_fLinkHref ?>" 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>
<?php if ($_fIsPeerTube): ?>
<span class="admin-file-peertube-id">ID: <?= htmlspecialchars(substr($_fPath, strlen("peertube_ids:"))) ?></span>
<?php endif; ?>
<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"]) && $_f["file_size"] > 0): ?> <?php if (!empty($_f["file_size"]) && $_f["file_size"] > 0): ?>
@@ -133,12 +136,12 @@ $websiteLabel = htmlspecialchars($_POST['website_label'] ?? '');
value="<?= htmlspecialchars($_f["display_label"] ?? "") ?>" value="<?= htmlspecialchars($_f["display_label"] ?? "") ?>"
placeholder="Légende / description (optionnel)" placeholder="Légende / description (optionnel)"
class="admin-file-label-input"> class="admin-file-label-input">
</span> </figcaption>
</figure>
<input type="hidden" name="delete_files[]" value="<?= (int)$_f["id"] ?>" disabled> <input type="hidden" name="delete_files[]" value="<?= (int)$_f["id"] ?>" disabled>
<button type="button" <button type="button"
class="admin-btn-remove fq-remove" class="btn btn--danger btn--sm"
onclick="this.previousElementSibling.disabled=false;this.closest('li').style.opacity='0.4';this.disabled=true;" onclick="this.previousElementSibling.previousElementSibling.disabled=false;this.closest('li').style.opacity='0.4';this.disabled=true;">Supprimer</button>
aria-label="Supprimer <?= htmlspecialchars($_f["file_name"] ?? basename($_f["file_path"])) ?>">&#x2715;</button>
</li> </li>
<?php endforeach; ?> <?php endforeach; ?>
</ul> </ul>

View File

@@ -1,47 +1,108 @@
<?php <?php
/** /**
* HTMX fragment: renders the licence section with conditional required states. * HTMX fragment (partage): renders the licence section with conditional options.
* *
* POST: access_type_id, license_id, license_custom, cc2r, admin_mode * POST: access_type_id, license_id, license_custom, cc2r, want_license, admin_mode
* *
* When access_type_id=1 (Libre): licence fields are required. * Libre (1): CC2r checkbox + licence select/custom (au moins une des deux)
* When access_type_id=2|3 (Interne/Interdit): licence fields are optional. * Interne (2): opt-in "Je souhaite appliquer une licence" → CC2r + licence
* Interdit (3): no licence options
*/ */
require_once __DIR__ . '/../../bootstrap.php'; require_once __DIR__ . '/../../bootstrap.php';
$accessTypeId = $_POST['access_type_id'] ?? '2'; $accessTypeId = $_POST['access_type_id'] ?? '';
$licenseId = $_POST['license_id'] ?? ''; $licenseId = $_POST['license_id'] ?? '';
$licenseCustom = $_POST['license_custom'] ?? ''; $licenseCustom = $_POST['license_custom'] ?? '';
$cc2r = !empty($_POST['cc2r']); $cc2r = !empty($_POST['cc2r']);
$wantLicense = !empty($_POST['want_license']);
$adminMode = ($_POST['admin_mode'] ?? '0') === '1'; $adminMode = ($_POST['admin_mode'] ?? '0') === '1';
$required = !$adminMode && $accessTypeId === '1';
require_once APP_ROOT . '/src/Database.php'; require_once APP_ROOT . '/src/Database.php';
$db = Database::getInstance(); $db = Database::getInstance();
$licenseTypes = $db->getAllLicenseTypes(); $licenseTypes = $db->getAllLicenseTypes();
?> ?>
<div class="licence-license-choice"> <div class="licence-license-choice">
<?php if ($accessTypeId === '1'): ?>
<fieldset class="licence-options-fieldset">
<legend>Options de licence</legend>
<p class="licence-note">J'autorise que mon TFE soit disponible en libre accès. Je suis conscient·e des responsabilités légales.</p>
<div class="admin-form-group">
<label class="admin-checkbox-label">
<input type="checkbox" name="cc2r" value="1"
<?= $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 class="licence-or-separator"><span>et / ou</span></div>
<?php <?php
$name = 'license_id'; $label = 'Licence :'; $options = $licenseTypes; $name = 'license_id'; $label = 'Licence :'; $options = $licenseTypes;
$selected = $licenseId; $placeholder = '— Sélectionner —'; $selected = $licenseId; $placeholder = '— Sélectionner —'; $required = false;
include APP_ROOT . '/templates/partials/form/select-field.php'; include APP_ROOT . '/templates/partials/form/select-field.php';
?> ?>
<?php <?php
$name = 'license_custom'; $label = 'Ou précisez une autre licence :'; $name = 'license_custom'; $label = 'Ou précisez une autre licence :';
$value = htmlspecialchars($licenseCustom); $value = htmlspecialchars($licenseCustom);
$hint = 'Ex: CC BY-NC 4.0, Tous droits réservés...'; $hint = 'Ex: CC BY-NC 4.0, Tous droits réservés';
include APP_ROOT . '/templates/partials/form/text-field.php'; include APP_ROOT . '/templates/partials/form/text-field.php';
?> ?>
</fieldset>
<?php elseif ($accessTypeId === '2'): ?>
<fieldset class="licence-options-fieldset">
<legend>Options de licence</legend>
<p class="licence-note">Mon TFE est accessible sur place et sur la plateforme xamxam par la communauté erg. J'autorise une (ré-)utilisation dans un contexte académique au sein de l'erg.</p>
<div class="admin-form-group">
<label class="admin-checkbox-label">
<input type="checkbox" name="want_license" value="1"
hx-post="/partage/licence-fragment"
hx-target=".licence-license-choice"
hx-swap="outerHTML"
hx-include="closest fieldset"
<?= $wantLicense ? 'checked' : '' ?>>
<strong>Je souhaite appliquer une licence sur mon TFE</strong>
</label>
<small>Par défaut, aucune licence spécifique n'est appliquée. Cochez pour en choisir une.</small>
</div>
<?php if ($wantLicense): ?>
<div class="admin-form-group"> <div class="admin-form-group">
<label class="admin-checkbox-label"> <label class="admin-checkbox-label">
<input type="checkbox" name="cc2r" value="1" <input type="checkbox" name="cc2r" value="1"
<?= $cc2r ? 'checked' : '' ?>> <?= $cc2r ? 'checked' : '' ?>>
J'accepte les Conditions Collectives de Réutilisation (CC2r) J'accepte les conditions collectives de réutilisation (CC2r)
</label> </label>
<small><a href="https://cc2r.net/" target="_blank" rel="noopener">En savoir plus sur la CC2r ↗</a></small> <small><a href="https://cc2r.net/" target="_blank" rel="noopener">En savoir plus sur la CC2r ↗</a></small>
</div> </div>
<?php
$name = 'license_id'; $label = 'Licence :'; $options = $licenseTypes;
$selected = $licenseId; $placeholder = '— Sélectionner —'; $required = false;
include APP_ROOT . '/templates/partials/form/select-field.php';
?>
<?php
$name = 'license_custom'; $label = 'Ou précisez une autre licence :';
$value = htmlspecialchars($licenseCustom);
$hint = 'Ex: CC BY-NC 4.0, Tous droits réservés…';
include APP_ROOT . '/templates/partials/form/text-field.php';
?>
<p class="licence-note"><em>Je suis conscient·e des obligations légales venant avec la licence choisie et acquiesce avoir lu la documentation prévue à cet effet par l'erg, ainsi qu'avoir discuté des enjeux des licences avec l'équipe pédagogique.</em></p>
<?php endif; ?>
</fieldset>
<?php elseif ($accessTypeId === '3'): ?>
<fieldset class="licence-options-fieldset">
<legend>Options de licence</legend>
<p class="licence-note">Mon TFE n'est pas disponible en physique ni sur le site. Une note descriptive est visible publiquement.</p>
<p class="licence-note"><em>Aucune licence n'est applicable.</em></p>
</fieldset>
<?php endif; ?>
</div> </div>

View File

@@ -1065,6 +1065,32 @@
+%%%%%%% diff from: somsyvxz 249f7943 "Bulk bar anti-shift, tags icons, AP no-wrap, credits reorder" (rebased revision) +%%%%%%% diff from: somsyvxz 249f7943 "Bulk bar anti-shift, tags icons, AP no-wrap, credits reorder" (rebased revision)
+\\\\\\\ to: mrtoqozz a1fbaf50 "server-side upload progress: poll /admin/actions/upload-progress.php via token; progress file written during PeerTube uploads" (rebased revision) +\\\\\\\ to: mrtoqozz a1fbaf50 "server-side upload progress: poll /admin/actions/upload-progress.php via token; progress file written during PeerTube uploads" (rebased revision)
++ $linkName = $link['name'] ?? ''; ++ $linkName = $link['name'] ?? '';
++ $linkExpiresVal = $link['expires_at'] ? date('Y-m-d\TH:i', strtotime($link['expires_at'])) : '';
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% diff from: mrtoqozz a1fbaf50 "server-side upload progress: poll /admin/actions/upload-progress.php via token; progress file written during PeerTube uploads" (rebased revision)
\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ to: somsyvxz 249f7943 "Bulk bar anti-shift, tags icons, AP no-wrap, credits reorder" (rebased revision)
- $linkName = $link['name'] ?? '';
- $linkExpiresVal = $link['expires_at'] ? date('Y-m-d\TH:i', strtotime($link['expires_at'])) : '';
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% diff from: somsyvxz 14a3cd10 "Bulk bar anti-shift, tags icons, AP no-wrap, credits reorder" (rebase destination)
\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ to: pnyrukop 065bd011 "Added details and summary to the license choosing element." (rebased revision)
$linkName = $link['name'] ?? '';
$linkExpiresVal = $link['expires_at'] ? date('Y-m-d\TH:i', strtotime($link['expires_at'])) : '';
$linkLockedYear = $link['locked_year'] ?? null;
+%%%%%%% diff from: somsyvxz 249f7943 "Bulk bar anti-shift, tags icons, AP no-wrap, credits reorder" (rebased revision)
+\\\\\\\ to: pnyrukop 12bfbc93 "Added details and summary to the license choosing element." (rebased revision)
++ $linkName = $link['name'] ?? '';
++ $linkExpiresVal = $link['expires_at'] ? date('Y-m-d\TH:i', strtotime($link['expires_at'])) : '';
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% diff from: pnyrukop 12bfbc93 "Added details and summary to the license choosing element." (rebased revision)
\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ to: somsyvxz 249f7943 "Bulk bar anti-shift, tags icons, AP no-wrap, credits reorder" (rebased revision)
- $linkName = $link['name'] ?? '';
- $linkExpiresVal = $link['expires_at'] ? date('Y-m-d\TH:i', strtotime($link['expires_at'])) : '';
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% diff from: somsyvxz 14a3cd10 "Bulk bar anti-shift, tags icons, AP no-wrap, credits reorder" (rebase destination)
\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ to: yupunuzo d7e461fa "refactor licence fragment: Libre→CC2r+licence, Interne→opt-in want_license, Interdit→none" (rebased revision)
$linkName = $link['name'] ?? '';
$linkExpiresVal = $link['expires_at'] ? date('Y-m-d\TH:i', strtotime($link['expires_at'])) : '';
$linkLockedYear = $link['locked_year'] ?? null;
+%%%%%%% diff from: somsyvxz 249f7943 "Bulk bar anti-shift, tags icons, AP no-wrap, credits reorder" (rebased revision)
+\\\\\\\ to: yupunuzo c830a93c "refactor licence fragment: Libre→CC2r+licence, Interne→opt-in want_license, Interdit→none" (rebased revision)
++ $linkName = $link['name'] ?? '';
++ $linkExpiresVal = $link['expires_at'] ? date('Y-m-d\TH:i', strtotime($link['expires_at'])) : ''; ++ $linkExpiresVal = $link['expires_at'] ? date('Y-m-d\TH:i', strtotime($link['expires_at'])) : '';
?> ?>
<tr class="admin-table-row" onclick="event.stopPropagation(); window.open('/partage/<?= urlencode($link['slug']) ?>', '_blank')" style="cursor:pointer"> <tr class="admin-table-row" onclick="event.stopPropagation(); window.open('/partage/<?= urlencode($link['slug']) ?>', '_blank')" style="cursor:pointer">

View File

@@ -35,8 +35,9 @@
<p><a href="/admin/add.php" class="btn btn--secondary">Retour au formulaire</a></p> <p><a href="/admin/add.php" class="btn btn--secondary">Retour au formulaire</a></p>
<?php elseif ($thesis): ?> <?php elseif ($thesis): ?>
<!-- ═══════════ Identité ═══════════ -->
<section> <section>
<h2>Informations de base</h2> <h2>Identité</h2>
<dl> <dl>
<dt>Identifiant</dt><dd><?= htmlspecialchars($thesis['identifier']) ?></dd> <dt>Identifiant</dt><dd><?= htmlspecialchars($thesis['identifier']) ?></dd>
<dt>Titre</dt><dd><?= htmlspecialchars($thesis['title']) ?></dd> <dt>Titre</dt><dd><?= htmlspecialchars($thesis['title']) ?></dd>
@@ -44,101 +45,221 @@
<dt>Sous-titre</dt><dd><?= htmlspecialchars($thesis['subtitle']) ?></dd> <dt>Sous-titre</dt><dd><?= htmlspecialchars($thesis['subtitle']) ?></dd>
<?php endif; ?> <?php endif; ?>
<dt>Auteur·ice(s)</dt><dd><?= htmlspecialchars($thesis['authors']) ?></dd> <dt>Auteur·ice(s)</dt><dd><?= htmlspecialchars($thesis['authors']) ?></dd>
<dt>Année</dt><dd><?= htmlspecialchars($thesis['year']) ?></dd> <?php if (!empty($thesis['contact_interne'])): ?>
<dt>Contact (interne)</dt><dd><?= htmlspecialchars($thesis['contact_interne']) ?></dd>
<?php endif; ?>
<dt>Contact public</dt><dd><?= !empty($thesis['contact_public']) ? 'Oui' : 'Non' ?></dd>
<dt>Année</dt><dd><?= htmlspecialchars((string)$thesis['year']) ?></dd>
<dt>Objet</dt><dd><?= htmlspecialchars($thesis['objet'] ?? 'tfe') ?></dd>
<?php if ($thesis['is_doctoral']): ?>
<dt>Doctoral</dt><dd>Oui</dd>
<?php endif; ?>
</dl> </dl>
</section> </section>
<!-- ═══════════ Cadre académique ═══════════ -->
<section> <section>
<h2>Détails académiques</h2> <h2>Cadre académique</h2>
<dl> <dl>
<dt>Orientation</dt><dd><?= htmlspecialchars($thesis['orientation'] ?? '') ?></dd> <dt>Orientation</dt><dd><?= htmlspecialchars($thesis['orientation'] ?? '') ?></dd>
<dt>Atelier pratique</dt><dd><?= htmlspecialchars($thesis['ap_program'] ?? '') ?></dd> <dt>Atelier Pratique</dt><dd><?= htmlspecialchars($thesis['ap_program'] ?? '') ?></dd>
<dt>Finalité</dt><dd><?= htmlspecialchars($thesis['finality_type'] ?? '') ?></dd> <dt>Finalité</dt><dd><?= htmlspecialchars($thesis['finality_type'] ?? '') ?></dd>
<?php if ($thesis['supervisors']): ?>
<dt>Promoteur·ice(s)</dt><dd><?= htmlspecialchars($thesis['supervisors']) ?></dd>
<?php endif; ?>
<?php if (!empty($thesis['jury_promoteurs_ulb'])): ?>
<dt>Promoteur·ice(s) ULB</dt><dd><?= htmlspecialchars($thesis['jury_promoteurs_ulb']) ?></dd>
<?php endif; ?>
<?php if (!empty($thesis['jury_lecteurs_internes'])): ?>
<dt>Lecteur·ices (interne)</dt><dd><?= htmlspecialchars($thesis['jury_lecteurs_internes']) ?></dd>
<?php endif; ?>
<?php if (!empty($thesis['jury_lecteurs_externes'])): ?>
<dt>Lecteur·ices (externe)</dt><dd><?= htmlspecialchars($thesis['jury_lecteurs_externes']) ?></dd>
<?php endif; ?>
</dl> </dl>
</section> </section>
<!-- ═══════════ Jury ═══════════ -->
<?php
$hasJury = $thesis['jury_president']
|| $thesis['jury_promoteurs']
|| $thesis['jury_promoteurs_ulb']
|| $thesis['jury_lecteurs_internes']
|| $thesis['jury_lecteurs_externes']
|| $thesis['supervisors'];
?>
<?php if ($hasJury): ?>
<section>
<h2>Jury</h2>
<dl>
<?php if ($thesis['jury_president']): ?>
<dt>Président·e</dt><dd><?= htmlspecialchars($thesis['jury_president']) ?></dd>
<?php endif; ?>
<?php if ($thesis['jury_promoteurs']): ?>
<dt>Promoteur·ice(s)</dt><dd><?= htmlspecialchars($thesis['jury_promoteurs']) ?></dd>
<?php endif; ?>
<?php if ($thesis['jury_promoteurs_ulb']): ?>
<dt>Promoteur·ice(s) ULB</dt><dd><?= htmlspecialchars($thesis['jury_promoteurs_ulb']) ?></dd>
<?php endif; ?>
<?php if ($thesis['jury_lecteurs_internes']): ?>
<dt>Lecteur·ice(s) interne</dt><dd><?= htmlspecialchars($thesis['jury_lecteurs_internes']) ?></dd>
<?php endif; ?>
<?php if ($thesis['jury_lecteurs_externes']): ?>
<dt>Lecteur·ice(s) externe</dt><dd><?= htmlspecialchars($thesis['jury_lecteurs_externes']) ?></dd>
<?php endif; ?>
<?php if ($thesis['supervisors'] && !$thesis['jury_promoteurs']): ?>
<dt>Promoteur·ice(s)</dt><dd><?= htmlspecialchars($thesis['supervisors']) ?></dd>
<?php endif; ?>
<?php if ($thesis['jury_points'] !== null): ?>
<dt>Note du jury</dt><dd><?= htmlspecialchars($thesis['jury_points']) ?>/20</dd>
<?php endif; ?>
</dl>
</section>
<?php endif; ?>
<!-- ═══════════ Contenu ═══════════ -->
<section> <section>
<h2>Contenu</h2> <h2>Contenu</h2>
<dl> <dl>
<dt>Synopsis</dt>
<dd class="recap-synopsis"><?= nl2br(htmlspecialchars($thesis['synopsis'] ?? '')) ?></dd>
<?php if ($thesis['context_note']): ?>
<dt>Note contextuelle</dt>
<dd class="recap-long-text"><?= nl2br(htmlspecialchars($thesis['context_note'])) ?></dd>
<?php endif; ?>
<?php if ($thesis['languages']): ?> <?php if ($thesis['languages']): ?>
<dt>Langue(s)</dt><dd><?= htmlspecialchars($thesis['languages']) ?></dd> <dt>Langue(s)</dt><dd><?= htmlspecialchars($thesis['languages']) ?></dd>
<?php endif; ?> <?php endif; ?>
<?php if ($thesis['formats']): ?> <?php if ($thesis['formats']): ?>
<dt>Format(s)</dt><dd><?= htmlspecialchars($thesis['formats']) ?></dd> <dt>Format(s)</dt><dd><?= htmlspecialchars($thesis['formats']) ?></dd>
<?php endif; ?> <?php endif; ?>
<?php if ($thesis['keywords']): ?> <?php if ($thesis['keywords']): ?>
<dt>Mots-clés</dt><dd><?= htmlspecialchars($thesis['keywords']) ?></dd> <dt>Mots-clés</dt><dd><?= htmlspecialchars($thesis['keywords']) ?></dd>
<?php endif; ?> <?php endif; ?>
<?php if ($thesis['baiu_link']): ?> <?php if ($thesis['baiu_link']): ?>
<dt>Lien</dt><dd><a href="<?= htmlspecialchars($thesis['baiu_link']) ?>" target="_blank" rel="noopener"><?= htmlspecialchars($thesis['baiu_link']) ?></a></dd> <dt>Lien BAIU</dt><dd><a href="<?= htmlspecialchars($thesis['baiu_link']) ?>" target="_blank" rel="noopener"><?= htmlspecialchars($thesis['baiu_link']) ?></a></dd>
<?php endif; ?> <?php endif; ?>
</dl> </dl>
</section> </section>
<!-- ═══════════ Accès et licence ═══════════ -->
<section>
<h2>Accès et licence</h2>
<dl>
<dt>Degré d'ouverture</dt><dd><?= htmlspecialchars($thesis['access_type'] ?? '') ?></dd>
<?php if ($thesis['license_type']): ?>
<dt>Licence</dt><dd><?= htmlspecialchars($thesis['license_type']) ?></dd>
<?php endif; ?>
<?php if ($thesis['license_custom']): ?>
<dt>Licence personnalisée</dt><dd><?= htmlspecialchars($thesis['license_custom']) ?></dd>
<?php endif; ?>
<dt>CC2r</dt><dd><?= !empty($thesis['cc2r']) ? 'Acceptée' : 'Non' ?></dd>
<dt>Exemplaire BAIU</dt><dd><?= !empty($thesis['exemplaire_baiu']) ? 'Oui' : 'Non' ?></dd>
<dt>Exemplaire ERG</dt><dd><?= !empty($thesis['exemplaire_erg']) ? 'Oui' : 'Non' ?></dd>
</dl>
</section>
<!-- ═══════════ Dates et publication ═══════════ -->
<section>
<h2>Dates et publication</h2>
<dl>
<?php if ($thesis['submitted_at']): ?>
<dt>Soumis le</dt><dd><?= date('d/m/Y à H:i', strtotime($thesis['submitted_at'])) ?></dd>
<?php endif; ?>
<?php if ($thesis['defense_date']): ?>
<dt>Date de défense</dt><dd><?= date('d/m/Y', strtotime($thesis['defense_date'])) ?></dd>
<?php endif; ?>
<?php if ($thesis['published_at']): ?>
<dt>Publié le</dt><dd><?= date('d/m/Y à H:i', strtotime($thesis['published_at'])) ?></dd>
<?php endif; ?>
<dt>Publié</dt><dd><?= !empty($thesis['is_published']) ? 'Oui' : 'Non' ?></dd>
<?php if ($thesis['jury_note_added']): ?>
<dt>Note du jury ajoutée</dt><dd><?= date('d/m/Y', strtotime($thesis['jury_note_added'])) ?></dd>
<?php endif; ?>
</dl>
</section>
<!-- ═══════════ Remarques ═══════════ -->
<?php if (!empty($thesis['remarks'])): ?>
<section>
<h2>Remarques</h2>
<p class="recap-long-text"><?= nl2br(htmlspecialchars($thesis['remarks'])) ?></p>
</section>
<?php endif; ?>
<!-- ═══════════ Fichiers ═══════════ -->
<?php if (!empty($files)): ?> <?php if (!empty($files)): ?>
<section> <section>
<h2>Fichiers</h2> <h2>Fichiers (<?= count($files) ?>)</h2>
<ul class="recap-file-list"> <ul class="recap-files-list" role="list">
<?php foreach ($files as $f): ?> <?php foreach ($files as $f): ?>
<?php <?php
$mime = $f['mime_type'] ?? ''; $mime = $f['mime_type'] ?? '';
$isImage = str_starts_with($mime, 'image/');
$filePath = $f['file_path'] ?? ''; $filePath = $f['file_path'] ?? '';
$fileName = htmlspecialchars($f['file_name'] ?? basename($filePath));
$fileType = htmlspecialchars($f['file_type']);
$displayLabel = $f['display_label'] ?? $f['description'] ?? '';
$isImage = str_starts_with($mime, 'image/');
$isPeerTube = str_starts_with($filePath, 'peertube_ids:'); $isPeerTube = str_starts_with($filePath, 'peertube_ids:');
$isExternal = str_starts_with($filePath, 'http://') || str_starts_with($filePath, 'https://');
if ($isPeerTube) { if ($isPeerTube) {
$_ptUuid = substr($filePath, strlen('peertube_ids:'));
require_once APP_ROOT . '/src/PeerTubeService.php'; require_once APP_ROOT . '/src/PeerTubeService.php';
$_ptDb = new Database(); $_ptDb = new Database();
$_ptUuid = substr($filePath, strlen('peertube_ids:'));
$_ptS = PeerTubeService::getSettings($_ptDb); $_ptS = PeerTubeService::getSettings($_ptDb);
// Only link to watch page for public (1) and unlisted (2) videos $mediaUrl = ((int)$_ptS['privacy'] <= 2)
if ((int)$_ptS['privacy'] <= 2) { ? PeerTubeService::getWatchUrl($_ptDb, $_ptUuid)
$mediaUrl = PeerTubeService::getWatchUrl($_ptDb, $_ptUuid); : '#';
} else { } elseif ($isExternal) {
$mediaUrl = '#'; $mediaUrl = htmlspecialchars($filePath);
}
} else { } else {
$mediaUrl = '/media?path=' . urlencode($filePath); $mediaUrl = '/media?path=' . urlencode($filePath);
} }
$fileName = htmlspecialchars($f['file_name'] ?? basename($filePath));
$fileType = htmlspecialchars($f['file_type']); $typeIcon = match (true) {
$isPeerTube && $fileType === 'video' => '🎬',
$isPeerTube && $fileType === 'audio' => '🔊',
$fileType === 'cover' => '🖼️',
$fileType === 'note_intention' => '📝',
$fileType === 'main' || ($mime === 'application/pdf') => '📄',
$fileType === 'video' || str_starts_with($mime, 'video/') => '🎬',
$fileType === 'audio' || str_starts_with($mime, 'audio/') => '🔊',
$isImage => '🖼️',
$fileType === 'caption' => '💬',
$fileType === 'annex' => '📎',
$fileType === 'website' => '🌐',
default => '📎',
};
?> ?>
<li class="recap-file-item"> <li class="recap-files-list-item">
<?php if ($isImage): ?> <figure class="recap-file-figure">
<a href="<?= $mediaUrl ?>" target="_blank" rel="noopener" class="recap-file-thumb-link"> <?php if ($isImage && !$isPeerTube): ?>
<img src="<?= $mediaUrl ?>" alt="<?= $fileName ?>" class="recap-file-thumb" loading="lazy"> <img src="<?= $mediaUrl ?>" alt="<?= $fileName ?>" class="recap-file-thumb" loading="lazy">
</a>
<?php else: ?> <?php else: ?>
<span class="recap-file-icon"> <span class="recap-file-icon"><?= $typeIcon ?></span>
<?php
if ($isPeerTube && $f['file_type'] === 'video') echo '🎬';
elseif ($isPeerTube && $f['file_type'] === 'audio') echo '🎵';
elseif ($mime === 'application/pdf') echo '📄';
elseif (str_starts_with($mime, 'video/')) echo '🎬';
elseif (str_starts_with($mime, 'audio/')) echo '🎵';
elseif (in_array($mime, ['application/zip','application/x-zip-compressed'])) echo '🗜️';
elseif (str_ends_with($f['file_name'] ?? '', '.vtt')) echo '💬';
else echo '📎';
?>
</span>
<?php endif; ?> <?php endif; ?>
<div class="recap-file-meta"> <figcaption class="recap-file-caption">
<div class="recap-file-name-row">
<?php if ($isPeerTube && $mediaUrl !== '#'): ?>
<a href="<?= $mediaUrl ?>" target="_blank" rel="noopener" class="recap-file-name"><?= $fileName ?></a> <a href="<?= $mediaUrl ?>" target="_blank" rel="noopener" class="recap-file-name"><?= $fileName ?></a>
<span class="recap-file-type-badge"><?= $fileType ?></span> <?php elseif ($isExternal): ?>
<span class="recap-file-size"><?= formatFileSize($f['file_size']) ?></span> <a href="<?= $mediaUrl ?>" target="_blank" rel="noopener" class="recap-file-name"><?= $fileName ?></a>
<span class="recap-file-date"><?= date('d/m/Y H:i', strtotime($f['uploaded_at'])) ?></span> <?php else: ?>
<a href="<?= $mediaUrl ?>" target="_blank" rel="noopener" class="recap-file-name"><?= $fileName ?></a>
<?php endif; ?>
</div> </div>
<?php if ($isPeerTube): ?>
<div class="recap-file-peertube-meta">
<span class="recap-file-peertube-id">ID: <?= htmlspecialchars($_ptUuid) ?></span>
</div>
<?php endif; ?>
<?php if ($displayLabel): ?>
<div class="recap-file-label"><?= htmlspecialchars($displayLabel) ?></div>
<?php endif; ?>
<div class="recap-file-meta-row">
<span class="recap-file-meta"><?= formatFileSize($f['file_size']) ?></span>
<span class="recap-file-meta"><?= date('d/m/Y H:i', strtotime($f['uploaded_at'])) ?></span>
</div>
</figcaption>
</figure>
</li> </li>
<?php endforeach; ?> <?php endforeach; ?>
</ul> </ul>

View File

@@ -61,8 +61,15 @@ $adminMode = $adminMode ?? false;
hx-swap="outerHTML" hx-swap="outerHTML"
hx-include="closest fieldset" hx-include="closest fieldset"
<?= (string)$selectedAccess === '1' ? 'checked' : '' ?> <?= $adminMode ? '' : 'required' ?>> <?= (string)$selectedAccess === '1' ? 'checked' : '' ?> <?= $adminMode ? '' : '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. <strong>🔓 Libre</strong>
<br>
</label> </label>
<details>
<summary> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256"><path d="M128,24A104,104,0,1,0,232,128,104.11,104.11,0,0,0,128,24Zm0,192a88,88,0,1,1,88-88A88.1,88.1,0,0,1,128,216Zm16-40a8,8,0,0,1-8,8,16,16,0,0,1-16-16V128a8,8,0,0,1,0-16,16,16,0,0,1,16,16v40A8,8,0,0,1,144,176ZM112,84a12,12,0,1,1,12,12A12,12,0,0,1,112,84Z"></path></svg> Info</summary>
<p>
Mon TFE est en libre accès à tout le monde sur la plateforme des TFE ainsi que dans la bibliothèque de lerg. Je suis conscient 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>
</details>
</div> </div>
<?php endif; ?> <?php endif; ?>
@@ -75,8 +82,16 @@ $adminMode = $adminMode ?? false;
hx-swap="outerHTML" hx-swap="outerHTML"
hx-include="closest fieldset" hx-include="closest fieldset"
<?= (string)$selectedAccess === '2' ? 'checked' : '' ?> <?= $adminMode ? '' : 'required' ?>> <?= (string)$selectedAccess === '2' ? 'checked' : '' ?> <?= $adminMode ? '' : 'required' ?>>
<strong>🔒 Interne</strong> — Mon TFE n'est accessible que sur place en physique. Une note descriptive est disponible sur le site. <strong>🔒 Interne</strong>
</label> </label>
<br>
<details>
<summary> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256"><path d="M128,24A104,104,0,1,0,232,128,104.11,104.11,0,0,0,128,24Zm0,192a88,88,0,1,1,88-88A88.1,88.1,0,0,1,128,216Zm16-40a8,8,0,0,1-8,8,16,16,0,0,1-16-16V128a8,8,0,0,1,0-16,16,16,0,0,1,16,16v40A8,8,0,0,1,144,176ZM112,84a12,12,0,1,1,12,12A12,12,0,0,1,112,84Z"></path></svg> Info</summary>
<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. Jautorise une (ré-)utilisation et diffusion dans un contexte académique et didactique au sein de l'erg.
</p>
</details>
</div> </div>
<?php endif; ?> <?php endif; ?>
@@ -89,8 +104,16 @@ $adminMode = $adminMode ?? false;
hx-swap="outerHTML" hx-swap="outerHTML"
hx-include="closest fieldset" hx-include="closest fieldset"
<?= (string)$selectedAccess === '3' ? 'checked' : '' ?> <?= $adminMode ? '' : 'required' ?>> <?= (string)$selectedAccess === '3' ? 'checked' : '' ?> <?= $adminMode ? '' : 'required' ?>>
<strong>🚫 Interdit</strong> — Mon TFE n'est pas disponible en physique ni sur le site. Une note descriptive est disponible sur le site. <strong>🚫 Interdit</strong>
</label> </label>
<br>
<details>
<summary> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256"><path d="M128,24A104,104,0,1,0,232,128,104.11,104.11,0,0,0,128,24Zm0,192a88,88,0,1,1,88-88A88.1,88.1,0,0,1,128,216Zm16-40a8,8,0,0,1-8,8,16,16,0,0,1-16-16V128a8,8,0,0,1,0-16,16,16,0,0,1,16,16v40A8,8,0,0,1,144,176ZM112,84a12,12,0,1,1,12,12A12,12,0,0,1,112,84Z"></path></svg> Info</summary>
<p>
Mon TFE est en libre accès à tout le monde sur la plateforme des TFE ainsi que dans la bibliothèque de lerg. Je suis conscient 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>
</details>
</div> </div>
<?php endif; ?> <?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> <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>

255
qq Normal file
View File

@@ -0,0 +1,255 @@
@ pknwmmky pontoporeia@happyngreen.fr 2026-05-11 14:44:19 7e8166d7
(no description set)
kmxvkmqm pontoporeia@happyngreen.fr 2026-05-11 14:36:01 3f7b2961
(empty) wrap licence fragment content in nested fieldset with legend
yupunuzo pontoporeia@happyngreen.fr 2026-05-11 14:36:01 678f1d3c
│ refactor licence fragment: Libre→CC2r+licence, Interne→opt-in want_license, Interdit→none
pnyrukop pontoporeia@happyngreen.fr 2026-05-11 14:24:27 8cc0bc34
│ Added details and summary to the license choosing element.
llmtqprz pontoporeia@happyngreen.fr 2026-05-11 13:55:19 f0bf86cb
(empty) guard syncOrderInput against undefined pond instance during FilePond init
mrtoqozz pontoporeia@happyngreen.fr 2026-05-11 13:55:19 b531602f
│ server-side upload progress: poll /admin/actions/upload-progress.php via token; progress file written during PeerTube uploads
nwozruss pontoporeia@happyngreen.fr 2026-05-11 13:38:35 415a03cb
│ decorelate formats from fichiers: no HTMX toggles, all slots always visible; progress bar 25/75 split; sticky formats fieldset
qvnunynl pontoporeia@happyngreen.fr 2026-05-11 13:33:20 1209931c
│ upload progress bar: fieldset layout, accent colors, file name display, completion animation, 800ms delay before redirect
qlvmyvuo pontoporeia@happyngreen.fr 2026-05-11 13:24:09 24e1048a
│ Bump peertube embed audio player height + remove figure for iframes in tfe.php
txzqmwnx pontoporeia@happyngreen.fr 2026-05-11 13:10:01 b754732c
│ fix PeerTube upload: switch to simple multipart POST /api/v1/videos/upload with CURLFile; remove resumable protocol
surwmkqz pontoporeia@happyngreen.fr 2026-05-11 13:00:07 1b34dd14
│ fix PeerTube upload: pass original filename in init body; chunk Content-Type → application/octet-stream
usskovxu pontoporeia@happyngreen.fr 2026-05-11 12:53:57 8c0f90fe
│ fix PeerTube init: remove waitTranscoding, add category+commentsEnabled; switch PATCH back to POSTFIELDS; remove verbose logging; clean curl_close
nllmqxnz pontoporeia@happyngreen.fr 2026-05-11 12:50:44 686be3c3
│ fix PeerTube PATCH: use CURLOPT_INFILE stream for binary body; global CURL_HTTP_VERSION_2_0; fix chunkNum off-by-one
mxqulkkl pontoporeia@happyngreen.fr 2026-05-11 12:46:02 3f28ed35
│ fix PeerTube PATCH: force HTTP/2 (CURL_HTTP_VERSION_2_0) to match init connection
ollozskx pontoporeia@happyngreen.fr 2026-05-11 12:43:41 ff9070c4
│ debug PeerTube PATCH 400: let curl negotiate HTTP version, log raw hex body
luxqovts pontoporeia@happyngreen.fr 2026-05-11 12:42:45 837f6ef9
│ debug PeerTube PATCH 400: force HTTP/1.1, set CURLOPT_INFILESIZE, log verbose tail
mosupsso pontoporeia@happyngreen.fr 2026-05-11 12:40:24 5989baac
│ fix PeerTube upload: force HTTP/1.1 for PATCH chunks; remove deprecated CURLOPT_BINARYTRANSFER
nnoxlkll pontoporeia@happyngreen.fr 2026-05-11 12:38:56 d9357537
│ fix PeerTube upload: add CURLOPT_VERBOSE debug; use BINARYTRANSFER for chunk body
yxkvwkqy pontoporeia@happyngreen.fr 2026-05-11 12:35:54 7ea832f1
│ fix PeerTube upload: Google-resumable (Location header, PATCH), +debug logging
kkxkwlvw pontoporeia@happyngreen.fr 2026-05-11 12:21:45 d1213cf9
│ fix PeerTube upload: use Google-resumable protocol (Location header + PATCH) instead of PUT; capture response headers; ensure chunk size multiple of 256KB
usxlqwxk pontoporeia@happyngreen.fr 2026-05-11 12:09:14 7e028d3f
│ Cleanup acces fichier section
omwsuqoy pontoporeia@happyngreen.fr 2026-05-11 11:40:50 56ab779e
│ move Restrictions d'accès aux fichiers from contenus.php to acces.php
usmyqlwr pontoporeia@happyngreen.fr 2026-05-11 11:35:11 03c9d317
│ cleanup: merge SMTP fields into single fieldset, rename to Emails
rxwmppwn pontoporeia@happyngreen.fr 2026-05-11 11:28:58 5a45cb02
│ fix: remove alt labels, fix curl_close deprecation, fix PeerTube description param
mqoyqups pontoporeia@happyngreen.fr 2026-05-11 11:24:57 42722c21
│ feat: PeerTube channel by name, test button, always-visible FilePond pools
qmoswsvt pontoporeia@happyngreen.fr 2026-05-11 11:15:06 679120b6
│ feat: shared SMTP credentials + resumable PeerTube upload + embed improvements
sxpsrqsl pontoporeia@happyngreen.fr 2026-05-11 11:01:25 fd3ad3d3
│ feat: add PeerTube alternate audio/video labels and FilePond pools
qrtmmwro pontoporeia@happyngreen.fr 2026-05-11 10:31:19 2fb392f6
│ fix: make schema.sql fully idempotent — add IF NOT EXISTS to all CREATE INDEX, CREATE TRIGGER, and CREATE VIEW statements
pylyqurz pontoporeia@happyngreen.fr 2026-05-11 04:45:23 6745734d
│ feat(backup): deploy cron-based SQLite backups to production
pqnovwxx pontoporeia@happyngreen.fr 2026-05-11 03:43:09 164df763
│ fix(production): fix multiple remote server errors from nginx logs
rxpvwzkt pontoporeia@happyngreen.fr 2026-05-11 03:43:09 cfc55073
│ feat(admin): add htmx toast feedback for settings checkboxes in contenus.php
sstzwlpk pontoporeia@happyngreen.fr 2026-05-11 03:35:43 677f5050
│ feat(deploy): upload deploy-server.sh, run migrations, fix migrate.sh server layout
mysnnsru pontoporeia@happyngreen.fr 2026-05-11 03:35:43 d6548157
│ feat(deploy): upload and run deploy-server.sh before permission verification, run migrations
unnmorqw pontoporeia@happyngreen.fr 2026-05-11 03:35:43 0ff5abbe
│ feat(deploy): upload and run deploy-server.sh before permission verification
rqrkkkuo pontoporeia@happyngreen.fr 2026-05-11 03:35:43 178a8577
│ feat(deploy): add deploy-verify-permissions recipe to check ownership/permissions after rsync
kpvxplms pontoporeia@happyngreen.fr 2026-05-11 03:35:43 ac53d0da
│ fix: settings handler was treating hidden value="0" as truthy
kuslmqps pontoporeia@happyngreen.fr 2026-05-11 03:35:43 97abb41b
│ fix: use HTMX auto-save with hidden value=0 inputs in separate fieldsets
qqssmzsl pontoporeia@happyngreen.fr 2026-05-11 03:35:43 bd4d5bff
│ fix: split formulaire section into separate forms to avoid cross-resets
tmklpprq pontoporeia@happyngreen.fr 2026-05-11 03:35:43 7a2525e0
│ fix: replace HTMX auto-save checkboxes with regular form + Enregistrer button
rswkvuzm pontoporeia@happyngreen.fr 2026-05-11 03:35:43 618356af
│ fix: settings checkboxes could not be unchecked + could not be checked independently
wstuyzym pontoporeia@happyngreen.fr 2026-05-11 03:35:43 18d9bd1a
│ feat: implement SQLite backup & data integrity plan (Phases 2-4)
ouqzmwvn pontoporeia@happyngreen.fr 2026-05-11 03:35:42 28608d2c
│ feat: implement SQLite backup & data integrity plan (Phases 2-4)
psvklxsu pontoporeia@happyngreen.fr 2026-05-11 03:35:42 c7012c91
│ fix: exclude entire var/ from rsync --delete to preserve logs
nlvsnzkz pontoporeia@happyngreen.fr 2026-05-11 03:35:42 9c4138fa
│ fix: add hx-swap="none" to admin auto-save checkboxes to prevent page swap
olzzwmwr pontoporeia@happyngreen.fr 2026-05-11 03:35:42 ae12c463
│ feat: require 3 mots-clés in partage, language asterisk toggle, admin auto-save checkboxes
nqmqrqmo pontoporeia@happyngreen.fr 2026-05-11 03:35:42 f6544c5d
│ fix: obfuscate email in contact links, raise rate limits, make Libre toggleable
ryqustol pontoporeia@happyngreen.fr 2026-05-11 03:35:42 edfc1fd0
│ import dialog: add Terminé button, fix padding, make success permanent, avoid POST resend
snsxswsz pontoporeia@happyngreen.fr 2026-05-11 03:35:42 4be7b931
│ fix: jury-fieldset.php calling old() with wrong signature for partage
wpspurnq pontoporeia@happyngreen.fr 2026-05-11 03:35:42 c361702d
│ Make Auteur(s) and Accès columns sortable alphabetically in admin list
zxryvzkv pontoporeia@happyngreen.fr 2026-05-11 03:35:42 d2570bc3
│ Improve recap page + fix CSV import for jury roles
vqxpnkox pontoporeia@happyngreen.fr 2026-05-10 22:20:29 9dd791cd
│ fix migration 028, promoteurice repopulation, DB bootstrap
xznsyswm pontoporeia@happyngreen.fr 2026-05-10 22:18:05 b952709e
│ FilePond production hardening
vqnonzxp pontoporeia@happyngreen.fr 2026-05-10 21:31:56 99c51201
│ remove accept attributes from FilePond inputs, rely on beforeAddFile validation
rlqsxozn pontoporeia@happyngreen.fr 2026-05-10 21:29:40 9b9445fe
│ fix file validation: use extension-based check in beforeAddFile, drop plugin
wmzntpxp pontoporeia@happyngreen.fr 2026-05-10 21:25:45 fa1697bf
│ add filepond-plugin-file-validate-type + server-side file warnings
stmuuwmv pontoporeia@happyngreen.fr 2026-05-10 21:19:58 ccdf1c36
│ add server-side video/audio size limits (2 GB) and fix missing annexe queue validation
tqwpzqtq pontoporeia@happyngreen.fr 2026-05-10 21:10:51 2524574a
(no description set)
wvkvvpmv pontoporeia@happyngreen.fr 2026-05-10 21:06:35 dc099a5f
│ fix annexe validation, FilePond type validation, and styling
rptplqsr pontoporeia@happyngreen.fr 2026-05-10 21:06:35 703b5dc2
│ Add FilePond pools for couverture, note_intention, video, audio
zmvkxops pontoporeia@happyngreen.fr 2026-05-10 21:06:35 39ce6dbf
(no description set)
yuuqpnwp pontoporeia@happyngreen.fr 2026-05-10 21:06:34 558d7c06
│ Fix FilePond integration: decouple format extras from main file inputs
ztztkrsu pontoporeia@happyngreen.fr 2026-05-10 21:06:34 017e2657
(no description set)
qwltvwqq pontoporeia@happyngreen.fr 2026-05-10 21:06:34 fe503dcd
│ Replace custom file-upload-queue.js with FilePond
pnotuyzv pontoporeia@happyngreen.fr 2026-05-10 21:06:34 191d2d7a
(no description set)
urrtosru pontoporeia@happyngreen.fr 2026-05-10 21:06:34 02272555
│ fix: stop checkbox click in admin index from navigating to recapitulatif
ymrzpvln pontoporeia@happyngreen.fr 2026-05-10 21:06:34 2ec34999
│ fix: spurious HTMX console warnings from checkbox-list default hx-include
muzswpkw pontoporeia@happyngreen.fr 2026-05-10 21:06:34 404696bf
│ fix: repair form submission with queued files + add comprehensive debug logging
ownwlmpo pontoporeia@happyngreen.fr 2026-05-10 21:06:34 6e99f3e9
│ Replace HTMX+PHP file upload queues with client-side JS
xlprulto pontoporeia@happyngreen.fr 2026-05-10 21:06:34 52ee3530
│ Replace HTMX+PHP file upload queues with client-side JS
qukzomwx pontoporeia@happyngreen.fr 2026-05-10 21:06:34 0e31760f
│ Replace HTMX+PHP file upload queues with client-side JS
mqnvolnz pontoporeia@happyngreen.fr 2026-05-10 21:06:34 db300c32
│ Replace HTMX+PHP file upload queues with client-side JS
utnzmtom pontoporeia@happyngreen.fr 2026-05-10 21:06:34 e2cd7804
│ Replace HTMX+PHP file upload queues with client-side JS
yrpmtrsy pontoporeia@happyngreen.fr 2026-05-10 16:35:04 0b59d667
│ fix: scoped HTMX file validation, add validation to TFE/PeerTube inputs
mxvvqust pontoporeia@happyngreen.fr 2026-05-10 16:32:34 a66ff5c1
│ refactor: session-based incremental TFE upload via HTMX, drop SortableJS
uvnvvyny pontoporeia@happyngreen.fr 2026-05-10 15:55:35 04c1c3c6
│ fix: req annexes, add HTMX inline file validation (MIME/size)
yztqkpzz pontoporeia@happyngreen.fr 2026-05-10 15:31:02 dd64d5c9
│ fix: TFE and annexes files not saved, plus keyword validation and file preview CSS
roqtyzln pontoporeia@happyngreen.fr 2026-05-10 15:05:24 94f0df20
│ feat: obfuscate all email addresses and mailto links as HTML entities
svprwkko pontoporeia@happyngreen.fr 2026-05-10 14:51:25 a90b8b4d
│ fix: add help email, preserve file names on validation error, license fix
kvyyvksn pontoporeia@happyngreen.fr 2026-05-10 14:51:25 bbfd786c
│ fix: add help email, preserve file names on validation error, license fix
moumuszm pontoporeia@happyngreen.fr 2026-05-10 14:26:45 ab4601b9
│ Fix language-search fragment
xvqonoyt pontoporeia@happyngreen.fr 2026-05-10 14:26:45 64b18fdb
│ Add sidebar TOC, simplify Données Secondaires section
tyotlpxt pontoporeia@happyngreen.fr 2026-05-10 14:26:45 22799820
│ Add Mots-clés and Langues management to contenus page
sntroxlt pontoporeia@happyngreen.fr 2026-05-10 14:26:45 47670245
│ Move Formulaire settings to contenus, remove delete-all TFE
qxuprqpt pontoporeia@happyngreen.fr 2026-05-10 14:26:45 1b5d0db6
│ Add language-search component for Autre Langue input + active search in lists
sttrwkly pontoporeia@happyngreen.fr 2026-05-10 14:26:45 58623625
│ CSV importer: boolean and ap variants/typos
pntwsqvs pontoporeia@happyngreen.fr 2026-05-10 14:26:45 b83b316e
│ Rename author_email→contact_interne, author_show_contact→contact_public across view/controllers/templates
vpwuyvyv pontoporeia@happyngreen.fr 2026-05-10 14:26:45 43cf6b4b
│ Fix: email clearing in findOrCreateAuthor, htmlspecialchars(null) crash in old(), dead contact_interne field, access_type_id radio clearing
szktqmnn pontoporeia@happyngreen.fr 2026-05-10 14:26:45 809d77b1
│ Error tests, FK violations fix
unnnvyqs pontoporeia@happyngreen.fr 2026-05-10 14:26:45 6ca873c8
│ Admin mobile block: fix inline style beating media query
nzllwsxo pontoporeia@happyngreen.fr 2026-05-10 14:26:45 ba41cc9e
│ Fix bulk form nesting, remove count bar, stopPropagation on actions
vumvtlyz pontoporeia@happyngreen.fr 2026-05-10 14:26:45 efd01b93
│ Rename Liens étudiant·e, add link name + edit dialog
somsyvxz pontoporeia@happyngreen.fr 2026-05-10 14:26:45 c8be0aae
│ Bulk bar anti-shift, tags icons, AP no-wrap, credits reorder
zlkousvq pontoporeia@happyngreen.fr 2026-05-10 14:26:45 73ef6301
│ Add back buttons, row click navigation, remove Voir button
zmuyukuk pontoporeia@happyngreen.fr 2026-05-10 14:26:45 db021566
│ Replace emoji/text buttons with Phosphor SVG icons, add credits
kxwyyswm pontoporeia@happyngreen.fr 2026-05-10 14:26:45 92cea357
│ Replace large admin index script with minimal JS
pkrnmpks pontoporeia@happyngreen.fr 2026-05-10 14:26:45 30ef10fd
│ Move export DB to Exporter modal, color stats
turvpxsq pontoporeia@happyngreen.fr 2026-05-10 14:26:45 b2b09243
│ Refactor: Admin index
yxpntrsp pontoporeia@happyngreen.fr 2026-05-10 14:26:45 dff6e2af
│ add explanation hint to is_published checkbox in Backoffice fieldset
ntmqzyop pontoporeia@happyngreen.fr 2026-05-10 14:26:45 f6b4ac09
│ Merge Publication fieldset's is_published checkbox into Backoffice fieldset
rtqxvoqk pontoporeia@happyngreen.fr 2026-05-10 14:26:45 cc65b385
│ Remove duration_pages/duration_minutes/file_size_info; rename cc4r → cc2r in DB and code
klpxpwlm pontoporeia@happyngreen.fr 2026-05-10 14:26:45 adf080d1
│ fix: resolve partage form submission issues
ruznqknn pontoporeia@happyngreen.fr 2026-05-10 14:26:45 8eebf684
│ css: moved + tweaked styles to common.css
mttylpmt pontoporeia@happyngreen.fr 2026-05-10 14:26:45 cbef12b5
│ link creation: fieldset with checkboxes for objet restriction, TFE checked by default
wwrwtomz pontoporeia@happyngreen.fr 2026-05-10 14:26:45 dd133522
│ switch thesis-add-header to grid layout: title | badge, subtitle spans full width
oolsxuow pontoporeia@happyngreen.fr 2026-05-10 14:26:45 d7ff6b42
│ Update: subtitle below header + top gradient
vnprsrzm pontoporeia@happyngreen.fr 2026-05-10 14:26:45 23ec2c7b
│ remove duplicate Image de couverture in edit form — moved cover preview+removal into fichiers-fragment.php, removed duplicate from edit-existing-files-block
ztkmwwkl pontoporeia@happyngreen.fr 2026-05-10 14:26:45 c0668225
│ .env and todo
toqvqlop pontoporeia@happyngreen.fr 2026-05-10 14:26:45 1fdd450f
│ overtype editor rounded corners
mswkvnyl pontoporeia@happyngreen.fr 2026-05-10 14:26:45 d87158db
│ Cleanup and normalize headers
uxqyvyvt pontoporeia@happyngreen.fr 2026-05-10 14:26:45 f1e2651f
│ refactor public search bar
xvtqzyom pontoporeia@happyngreen.fr 2026-05-10 14:26:45 58521915
│ Redesign focus rings
vrqmwvrx pontoporeia@happyngreen.fr 2026-05-10 14:26:45 10cfa469
│ unify form element styles in common.css
rvvmuxsz pontoporeia@happyngreen.fr 2026-05-10 14:25:53 0be7f533
│ style: tweaking admin section + some public
towstynu pontoporeia@happyngreen.fr 2026-05-09 14:44:48 b7ff2776
│ EDIT THIS → refactor: unify edit mode Format+Fichiers with add/partage HTMX fragment
qnttznqt pontoporeia@happyngreen.fr 2026-05-09 14:44:48 3cdd3893
│ Refactor: Form improvements and cleanup: note contextuel, annexes, fichiers
wqmrrrwu pontoporeia@happyngreen.fr 2026-05-09 14:44:48 a8aeb0d6
│ feat: dual upload system — direct file storage + PeerTube API integration
nsnurqzv pontoporeia@happyngreen.fr 2026-05-09 14:44:48 5fbd6243
│ fix: justfile shebang recipes indentation (spaces → tabs)
rqzyxzyt pontoporeia@happyngreen.fr 2026-05-09 14:44:48 4c030bd5
│ Refactor + feat: unify format/fichiers HTMX fragment, reorder format types, add file constraints, fix admin auth
muzruvwl pontoporeia@happyngreen.fr 2026-05-09 14:44:48 7bedda02
│ Encrypt SMTP password at rest with AES-256-GCM
ovqmwmol pontoporeia@happyngreen.fr 2026-05-09 14:42:07 627b0c4e
│ Remove required from all admin add/edit form inputs
vpomqrst pontoporeia@happyngreen.fr 2026-05-08 22:58:25 b8331a08
│ fix: replace .php-suffixed public URLs blocked by nginx catch-all
qnxltqyq pontoporeia@happyngreen.fr 2026-05-08 22:58:25 189f4368
│ fix: 403 on /language-autre-fragment.php — add explicit nginx location block
nslznwmk pontoporeia@happyngreen.fr 2026-05-08 22:58:25 6ba13e00
│ test: add ShareLinkTest + PureLogicTest (TDD), fix coverMap undefined in SearchController
nvntmkyx pontoporeia@happyngreen.fr 2026-05-08 22:58:25 15d54fa1
│ add Néerlandais language option and make language_autre conditionally required
qxqyzmzp pontoporeia@happyngreen.fr 2026-05-08 22:58:25 f3d96155
│ merge banners into covers: remove banner field, migrate files, add covers to search/home/repertoire cards
rxnnnvpu pontoporeia@happyngreen.fr 2026-05-08 22:58:05 e3896811
│ Fix migrations and deploy issues + errors + linting
◆ pqntkzml pontoporeia@happyngreen.fr 2026-05-07 23:39:41 main bdd95341
│ Extract shared TFE form partial — single source of truth for add/edit/partage
~