Migrate all <img>-based icons to inline SVG via PHP helper

Replace every <img src="/assets/icons/..."> with <?= icon('name') ?>
across 26 template files. The PHP helper inlines the SVG markup into the
DOM so CSS color cascades naturally through fill="currentColor".

- Add src/icon.php helper: reads SVG file, sets width/height to 1em,
  injects aria-hidden, supports optional CSS class
- Fix 12 icon SVGs that had hardcoded fill="#000000" or missing fill attr
- Replace search.svg with Phosphor fill-based magnifying glass
- Add explicit SVG sizes for admin header nav icons (16px/20px)
- Scope public search icon CSS to form[role=search]:not(.header-search-form)
  to avoid breaking admin header layout; change stroke to fill
- Remove <img> filter: brightness(0) invert(1) hacks from admin.css
This commit is contained in:
Pontoporeia
2026-06-21 17:23:37 +02:00
parent b1774e6e97
commit dfde88eaa5
42 changed files with 166 additions and 108 deletions

20
TODO.md
View File

@@ -1,12 +1,18 @@
# TODO
> Last updated: 2026-06-21
> Context: nettoyage modal styling fixes + inline SVG → icon files migration
> Context: Migrate all <img>-based icons to inline SVG via PHP helper so CSS color cascade works
## In Progress
- [ ] #inline-svg-to-icons Move all inline SVGs to asset icon files, ensure correct fill colors `(24 icon files, 22 PHP/HTML files updated)`
## Pending
- [ ] #icon-color-verify Verify icon colors render correctly across all pages (header, admin tables, forms, dialogs, cleanup modal)
## Completed
- [x] #inline-icon-helper Create `icon()` PHP helper + auto-load in bootstrap `(src/icon.php, bootstrap.php)`
- [x] #icon-fill-currentcolor Ensure all 36 icon SVGs use fill="currentColor" or stroke="currentColor" `(assets/icons/*.svg)`
- [x] #migrate-all-img-icons Replace all remaining `<img src="/assets/icons/">` with `<?= icon() ?>` across 26 template files ✓
- [x] #icon-css-cleanup Remove `<img>` filter hacks from admin.css, add explicit sizes for header nav + public search icons `(admin.css, search.css)`
- [x] #icon-em-sizing Set icon helper to output width="1em" height="1em" so icons scale with parent font-size `(icon.php)`
- [x] #inline-svg-to-icons Move all inline SVGs to asset icon files, ensure currentColor fill for proper color inheritance `(33 icons, 22 files, 67+ inline SVGs replaced, 2 dynamic SVGs remain)`
- [x] #cleanup-modal-fixes Fix nettoyage modal: SVG caret icons, margin→padding, BBBDMSans summary `(admin.css, details.css)`
- [x] #structure-formulaire-page Move "Structure du Formulaire" from contenus.php to its own dedicated page with back button `(structure-formulaire.php [new], contenus.php)`
- [x] #contenus-indexes Add index on thesis_languages(language_id) + tags(deleted_at, name); fix count queries to exclude soft-deleted theses `(Database.php, DatabaseMigrations.php, schema.sql, migrations/applied/041_thesis_languages_index.sql)`
@@ -22,7 +28,6 @@
- [x] #context-note-synopsis Display contextual note above synopsis (italic) instead of in meta column on TFE page `(tfe.php, tfe.css)`
- [x] #decouple-contacts Decouple contact_visible (public) & contact_interne (private email): backend already decoupled; made contact_public checkbox functional in admin add/edit forms; contact_public now controls TFE page visibility `(FormBootstrap.php, ThesisCreateController.php, ThesisEditController.php, tfe.php, form.php)`
- [x] #csrf-rotation-race Stop CSRF token rotation in draft.php + remove hx-post from <form> — both broke FilePond uploads and form submission `(admin/actions/draft.php, partage/fragments/draft.php, FormBootstrap.php, pill-search.js)`
- [x] ~~#filepond-csrf-stale~~ (superseded by #csrf-rotation-race)
- [x] #adminold-return-type Fix adminOld closure return type from `:string` to `:string|array` `(FormBootstrap.php)`
- [x] #duration-integer-units Make duration field: integer for pages/Mo, dedicated h/m/s time inputs `(form.php, ThesisCreateController.php, tfe.php, form-base.css)`
- [x] #licence-svg-fix Fix licence details/summary SVG: width 1rem, inline-flex layout `(fieldset-licence-explanation.php, form-base.css)`
@@ -53,11 +58,4 @@
- [x] #split-form-css Split `form.css` into `form-base.css` and `form-admin.css`
- [x] #extra-css-admin Update `head.php` to support `$extraCssAdmin` for admin-only stylesheets `(head.php)`
## Pending
- [ ] #overtype-analysis Analyse and fix OverType editor reliability on contenus-edit.php
- [ ] #contact-test-manual Test contact decoupling end-to-end: student submission → admin edit → public TFE display
- [ ] #aria-test-manual Test WCAG changes with VoiceOver and NVDA on full add/edit/partage form flows
- [ ] #nojs-upload-test Test end-to-end: submit partage form with JS disabled, verify files arrive via `$_FILES`
- [ ] #csp-media-iframe-deploy Deploy nginx config fix to server, test PDF iframe on /tfe?id=221
## Deferred / Blocked

View File

@@ -40,6 +40,7 @@ if (php_sapi_name() === 'cli-server') {
// Central application helper (boot, auth guard, CSRF, flash, render)
require_once APP_ROOT . '/src/App.php';
require_once APP_ROOT . '/src/icon.php';
// Maintenance mode gate — block public pages; allow /admin/ through.
// The flag file lives in storage/ (outside webroot) to avoid web exposure.

View File

@@ -45,7 +45,7 @@ if ($totalStale === 0 && $totalFiles === 0): ?>
<div id="tmp-cleanup-stats-wrapper">
<details class="n-section" open>
<summary>
<img src="/assets/icons/paint-brush-household.svg" width="14" height="14" alt="" aria-hidden="true">
<?= icon('paint-brush-household') ?>
Fichiers temporaires
</summary>
<p style="margin:0;color:var(--accent-green)">✓ Aucun fichier temporaire.</p>
@@ -56,13 +56,13 @@ if ($totalStale === 0 && $totalFiles === 0): ?>
<div id="tmp-cleanup-stats-wrapper">
<details class="n-section" open>
<summary>
<img src="/assets/icons/paint-brush-household.svg" width="14" height="14" alt="" aria-hidden="true">
<?= icon('paint-brush-household') ?>
Fichiers temporaires <span class="n-meta"><?= htmlspecialchars($summaryMeta) ?></span>
</summary>
<?php if ($fpStale > 0): ?>
<p class="n-heading">
<img src="/assets/icons/paint-brush-household.svg" width="14" height="14" alt="" aria-hidden="true">
<?= icon('paint-brush-household') ?>
Téléversements abandonnés
</p>
<table class="n-table">
@@ -80,7 +80,7 @@ if ($totalStale === 0 && $totalFiles === 0): ?>
hx-target="#tmp-cleanup-stats-wrapper"
hx-swap="outerHTML"
hx-indicator="#tmp-cleanup-stats-wrapper">
<img src="/assets/icons/trash.svg" width="14" height="14" alt="" aria-hidden="true">
<?= icon('trash') ?>
Supprimer
</button>
</td>
@@ -92,7 +92,7 @@ if ($totalStale === 0 && $totalFiles === 0): ?>
<?php if ($trStale > 0): ?>
<p class="n-heading">
<img src="/assets/icons/trash.svg" width="14" height="14" alt="" aria-hidden="true">
<?= icon('trash') ?>
Corbeille
</p>
<table class="n-table">
@@ -110,7 +110,7 @@ if ($totalStale === 0 && $totalFiles === 0): ?>
hx-target="#tmp-cleanup-stats-wrapper"
hx-swap="outerHTML"
hx-indicator="#tmp-cleanup-stats-wrapper">
<img src="/assets/icons/trash.svg" width="14" height="14" alt="" aria-hidden="true">
<?= icon('trash') ?>
Supprimer
</button>
</td>

View File

@@ -20,7 +20,7 @@ if (!($d['configured'] ?? false)): ?>
<div id="peertube-orphans-wrapper">
<details id="peertube-orphans-col" class="n-section" open>
<summary>
<img src="/assets/icons/video.svg" width="14" height="14" alt="" aria-hidden="true">
<?= icon('video') ?>
Vidéos PeerTube orphelines
</summary>
<div id="peertube-orphans-stats">
@@ -34,7 +34,7 @@ if (!($d['configured'] ?? false)): ?>
<div id="peertube-orphans-wrapper">
<details id="peertube-orphans-col" class="n-section" open>
<summary>
<img src="/assets/icons/video.svg" width="14" height="14" alt="" aria-hidden="true">
<?= icon('video') ?>
Vidéos PeerTube orphelines
</summary>
<div id="peertube-orphans-stats">
@@ -47,7 +47,7 @@ if (!($d['configured'] ?? false)): ?>
<div id="peertube-orphans-wrapper">
<details id="peertube-orphans-col" class="n-section" open>
<summary>
<img src="/assets/icons/video.svg" width="14" height="14" alt="" aria-hidden="true">
<?= icon('video') ?>
Vidéos PeerTube orphelines <span class="n-meta"><?= (int)($d['orphan_count'] ?? 0) ?> vidéos orphelines</span>
</summary>
<div id="peertube-orphans-stats">
@@ -70,7 +70,7 @@ if (!($d['configured'] ?? false)): ?>
hx-swap="outerHTML"
hx-trigger="click"
hx-indicator="#peertube-orphans-wrapper">
<img src="/assets/icons/trash.svg" width="14" height="14" alt="" aria-hidden="true">
<?= icon('trash') ?>
Supprimer
</button>
</td>
@@ -87,7 +87,7 @@ if (!($d['configured'] ?? false)): ?>
<?php if (($d['stale_count'] ?? 0) > 0): ?>
<details id="peertube-stale-section" class="n-section" open>
<summary>
<img src="/assets/icons/warning-diamond.svg" width="14" height="14" alt="" aria-hidden="true">
<?= icon('warning-diamond') ?>
Références DB obsolètes <span class="n-meta"><?= $d['stale_count'] ?></span>
</summary>
<p style="margin:0 0 var(--space-sm) 0;font-size:0.85em;color:var(--text-secondary)">Ces UUID sont référencés en base de données mais n'existent plus sur la chaîne PeerTube. Les TFE liés affichent des liens morts.</p>

View File

@@ -28,19 +28,19 @@ try {
<strong><span id="langues-selected-count">0</span> langue(s) sélectionnée(s)</strong>
<div class="admin-bulk-btns">
<button type="button" class="btn btn--sm btn--secondary" onclick="languesCancelSelection()" title="Annuler la sélection">
<img src="/assets/icons/x-close.svg" width="32" height="32" alt="" aria-hidden="true">
<?= icon('x-close') ?>
Annuler
</button>
<button type="button" class="btn btn--sm btn--red admin-btn-delete"
onclick="languesConfirmBulkDelete()"
title="Supprimer les langues sélectionnées">
<img src="/assets/icons/trash.svg" width="32" height="32" alt="" aria-hidden="true">
<?= icon('trash') ?>
Supprimer
</button>
<button type="button" class="btn btn--sm btn--warning admin-btn-merge"
onclick="languesConfirmBulkMerge()"
title="Fusionner les langues sélectionnées">
<img src="/assets/icons/link-chain.svg" width="32" height="32" alt="" aria-hidden="true">
<?= icon('link-chain') ?>
Fusionner
</button>
</div>
@@ -74,7 +74,7 @@ try {
<span class="tag-name-cell"><?= htmlspecialchars($lang['name']) ?></span>
<button type="button" class="admin-icon-btn admin-icon-btn--edit" title="Renommer"
onclick="languesStartRename(<?= (int)$lang['id'] ?>)">
<img src="/assets/icons/pencil-note.svg" width="32" height="32" alt="" aria-hidden="true">
<?= icon('pencil-note') ?>
</button>
</td>
<td class="admin-tags-count" style="width:1%;white-space:nowrap"><?= (int)$lang['thesis_count'] ?></td>
@@ -87,7 +87,7 @@ try {
<input type="hidden" name="language_id" value="<?= (int)$lang['id'] ?>">
<button type="button" class="admin-icon-btn admin-icon-btn--delete" title="Supprimer"
onclick="languesConfirmDelete(this, <?= htmlspecialchars(json_encode($lang['name']), ENT_QUOTES) ?>)">
<img src="/assets/icons/trash.svg" width="32" height="32" alt="" aria-hidden="true">
<?= icon('trash') ?>
</button>
</form>
</div>

View File

@@ -28,19 +28,19 @@ try {
<strong><span id="motscles-selected-count">0</span> mot(s)-clé(s) sélectionné(s)</strong>
<div class="admin-bulk-btns">
<button type="button" class="btn btn--sm btn--secondary" onclick="motsclesCancelSelection()" title="Annuler la sélection">
<img src="/assets/icons/x-close.svg" width="32" height="32" alt="" aria-hidden="true">
<?= icon('x-close') ?>
Annuler
</button>
<button type="button" class="btn btn--sm btn--red admin-btn-delete"
onclick="motsclesConfirmBulkDelete()"
title="Supprimer les mots-clés sélectionnés">
<img src="/assets/icons/trash.svg" width="32" height="32" alt="" aria-hidden="true">
<?= icon('trash') ?>
Supprimer
</button>
<button type="button" class="btn btn--sm btn--warning admin-btn-merge"
onclick="motsclesConfirmBulkMerge()"
title="Fusionner les mots-clés sélectionnés">
<img src="/assets/icons/link-chain.svg" width="32" height="32" alt="" aria-hidden="true">
<?= icon('link-chain') ?>
Fusionner
</button>
</div>
@@ -74,7 +74,7 @@ try {
<span class="tag-name-cell"><?= htmlspecialchars($tag['name']) ?></span>
<button type="button" class="admin-icon-btn admin-icon-btn--edit" title="Renommer"
onclick="motsclesStartRename(<?= (int)$tag['id'] ?>)">
<img src="/assets/icons/pencil-note.svg" width="32" height="32" alt="" aria-hidden="true">
<?= icon('pencil-note') ?>
</button>
</td>
<td class="admin-tags-count" style="width:1%;white-space:nowrap"><?= (int)$tag['thesis_count'] ?></td>
@@ -87,7 +87,7 @@ try {
<input type="hidden" name="tag_id" value="<?= (int)$tag['id'] ?>">
<button type="button" class="admin-icon-btn admin-icon-btn--delete" title="Supprimer"
onclick="motsclesConfirmDelete(this, <?= htmlspecialchars(json_encode($tag['name']), ENT_QUOTES) ?>)">
<img src="/assets/icons/trash.svg" width="32" height="32" alt="" aria-hidden="true">
<?= icon('trash') ?>
</button>
</form>
</div>

View File

@@ -88,18 +88,18 @@ $rootDirs = ['tfe', 'these', 'frart', 'documents', 'theses'];
function fileIcon(string $ext): string {
$ext = strtolower($ext);
if ($ext === 'pdf') {
return '<img src="/assets/icons/file-text-audio.svg" width="24" height="24" alt="" aria-hidden="true">';
return icon('file-text-audio');
}
if (in_array($ext, ['zip', 'tar', 'gz', 'bz2', 'xz', '7z', 'rar'], true)) {
return '<img src="/assets/icons/file-doc.svg" width="24" height="24" alt="" aria-hidden="true">';
return icon('file-doc');
}
// Default text-file icon for all other extensions
return '<img src="/assets/icons/file-lines.svg" width="24" height="24" alt="" aria-hidden="true">';
return icon('file-lines');
}
// SVG folder icon (same for all directories)
function folderIcon(): string {
return '<img src="/assets/icons/folder.svg" width="24" height="24" alt="" aria-hidden="true">';
return icon('folder');
}
?>
<div id="file-browser-container" class="file-browser">

View File

@@ -98,7 +98,7 @@ if (empty($orphans)) {
<?= $v['linkedTo'] !== null ? 'disabled title="Déjà liée au TFE ' . htmlspecialchars($v['linkedTo']) . '"' : '' ?>
>
<span class="file-browser-icon">
<img src="/assets/icons/arrow-circle-left.svg" width="24" height="24" alt="" aria-hidden="true">
<?= icon('arrow-circle-left') ?>
</span>
<span class="file-browser-name"><?= htmlspecialchars($v['name']) ?></span>
<span class="file-browser-size"><?= !empty($v['createdAt']) ? substr($v['createdAt'], 0, 10) : '' ?></span>

View File

@@ -95,7 +95,7 @@ $rows = [
onclick="this.closest('dialog').close()"
title="Fermer"
aria-label="Fermer">
<img src="/assets/icons/x-circle.svg" width="24" height="24" alt="" aria-hidden="true">
<?= icon('x-circle') ?>
</button>
</div>

View File

@@ -32,7 +32,7 @@ try {
<button type="button" class="btn btn--sm btn--warning admin-btn-merge"
onclick="tagsConfirmBulkMerge()"
title="Fusionner les mots-clés sélectionnés">
<img src="/assets/icons/link-chain.svg" width="32" height="32" alt="" aria-hidden="true">
<?= icon('link-chain') ?>
Fusionner
</button>
</div>
@@ -65,7 +65,7 @@ try {
<span class="tag-name-cell"><?= htmlspecialchars($tag['name']) ?></span>
<button type="button" class="admin-icon-btn admin-icon-btn--edit" title="Renommer"
onclick="tagsStartRename(<?= (int)$tag['id'] ?>)">
<img src="/assets/icons/pencil-note.svg" width="32" height="32" alt="" aria-hidden="true">
<?= icon('pencil-note') ?>
</button>
</td>
<td class="admin-tags-count" style="width:1%;white-space:nowrap"><?= (int)$tag['thesis_count'] ?></td>
@@ -77,7 +77,7 @@ try {
<input type="hidden" name="tag_id" value="<?= (int)$tag['id'] ?>">
<button type="button" class="admin-icon-btn admin-icon-btn--delete" title="Supprimer"
onclick="tagsConfirmDelete(this, <?= htmlspecialchars(json_encode($tag['name']), ENT_QUOTES) ?>)">
<img src="/assets/icons/trash.svg" width="32" height="32" alt="" aria-hidden="true">
<?= icon('trash') ?>
</button>
</form>
</div>

View File

@@ -30,6 +30,8 @@
}
.admin-body header nav ul [data-nav-logout] a svg {
display: block;
width: 20px;
height: 20px;
}
/* Public-site link icon in admin nav */
@@ -40,6 +42,11 @@
column-gap: 0.4em;
}
.admin-body header nav .nav-logo svg {
width: 16px;
height: 16px;
}
.admin-body main {
flex: 1;

View File

@@ -3,6 +3,21 @@
Root class: .header-search-wrap
============================================================ */
/* Public search bar icon (in partials/search-bar.php) */
/* Scoped to forms that are NOT inside .header-search-wrap */
form[role="search"]:not(.header-search-form) > svg {
width: 20px;
height: 20px;
flex-shrink: 0;
fill: var(--text-tertiary);
margin-right: var(--space-2xs);
}
form[role="search"]:not(.header-search-form) {
display: flex;
align-items: center;
}
.header-search-wrap {
padding: 0;
flex-shrink: 0;
@@ -23,7 +38,7 @@
left: var(--space-s);
width: 18px;
height: 18px;
stroke: var(--accent-primary);
fill: var(--accent-primary);
pointer-events: none;
}

View File

@@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" fill="#000000" viewBox="0 0 256 256"><path d="M213.66,101.66l-80,80a8,8,0,0,1-11.32,0l-80-80A8,8,0,0,1,53.66,90.34L128,164.69l74.34-74.35a8,8,0,0,1,11.32,11.32Z"></path></svg>
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" fill="currentColor" viewBox="0 0 256 256"><path d="M213.66,101.66l-80,80a8,8,0,0,1-11.32,0l-80-80A8,8,0,0,1,53.66,90.34L128,164.69l74.34-74.35a8,8,0,0,1,11.32,11.32Z"></path></svg>

Before

Width:  |  Height:  |  Size: 238 B

After

Width:  |  Height:  |  Size: 243 B

View File

@@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" fill="#000000" viewBox="0 0 256 256"><path d="M181.66,133.66l-80,80a8,8,0,0,1-11.32-11.32L164.69,128,90.34,53.66a8,8,0,0,1,11.32-11.32l80,80A8,8,0,0,1,181.66,133.66Z"></path></svg>
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" fill="currentColor" viewBox="0 0 256 256"><path d="M181.66,133.66l-80,80a8,8,0,0,1-11.32-11.32L164.69,128,90.34,53.66a8,8,0,0,1,11.32-11.32l80,80A8,8,0,0,1,181.66,133.66Z"></path></svg>

Before

Width:  |  Height:  |  Size: 243 B

After

Width:  |  Height:  |  Size: 248 B

View File

@@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="1rem" 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>
<svg xmlns="http://www.w3.org/2000/svg" width="1rem" fill="currentColor" 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>

Before

Width:  |  Height:  |  Size: 348 B

After

Width:  |  Height:  |  Size: 368 B

View File

@@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" fill="#000000" viewBox="0 0 256 256"><path d="M230.64,25.36a32,32,0,0,0-45.26,0q-.21.21-.42.45L131.55,88.22,121,77.64a24,24,0,0,0-33.95,0l-76.69,76.7a8,8,0,0,0,0,11.31l80,80a8,8,0,0,0,11.31,0L178.36,169a24,24,0,0,0,0-33.95l-10.58-10.57L230.19,71c.15-.14.31-.28.45-.43A32,32,0,0,0,230.64,25.36ZM96,228.69,79.32,212l22.34-22.35a8,8,0,0,0-11.31-11.31L68,200.68,55.32,188l22.34-22.35a8,8,0,0,0-11.31-11.31L44,176.68,27.31,160,72,115.31,140.69,184ZM219.52,59.1l-68.71,58.81a8,8,0,0,0-.46,11.74L167,146.34a8,8,0,0,1,0,11.31l-15,15L83.32,104l15-15a8,8,0,0,1,11.31,0l16.69,16.69a8,8,0,0,0,11.74-.46L196.9,36.48A16,16,0,0,1,219.52,59.1Z"></path></svg>
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" fill="currentColor" viewBox="0 0 256 256"><path d="M230.64,25.36a32,32,0,0,0-45.26,0q-.21.21-.42.45L131.55,88.22,121,77.64a24,24,0,0,0-33.95,0l-76.69,76.7a8,8,0,0,0,0,11.31l80,80a8,8,0,0,0,11.31,0L178.36,169a24,24,0,0,0,0-33.95l-10.58-10.57L230.19,71c.15-.14.31-.28.45-.43A32,32,0,0,0,230.64,25.36ZM96,228.69,79.32,212l22.34-22.35a8,8,0,0,0-11.31-11.31L68,200.68,55.32,188l22.34-22.35a8,8,0,0,0-11.31-11.31L44,176.68,27.31,160,72,115.31,140.69,184ZM219.52,59.1l-68.71,58.81a8,8,0,0,0-.46,11.74L167,146.34a8,8,0,0,1,0,11.31l-15,15L83.32,104l15-15a8,8,0,0,1,11.31,0l16.69,16.69a8,8,0,0,0,11.74-.46L196.9,36.48A16,16,0,0,1,219.52,59.1Z"></path></svg>

Before

Width:  |  Height:  |  Size: 705 B

After

Width:  |  Height:  |  Size: 710 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" fill="currentColor" 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,216Zm48-88a8,8,0,0,1-8,8H136v32a8,8,0,0,1-16,0V136H88a8,8,0,0,1,0-16h32V88a8,8,0,0,1,16,0v32h32A8,8,0,0,1,176,128Z"></path></svg>

After

Width:  |  Height:  |  Size: 345 B

View File

@@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="11" cy="11" r="8"/><line x1="21" y1="21" x2="16.65" y2="16.65"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" fill="currentColor" viewBox="0 0 256 256"><path d="M229.66,218.34l-50.07-50.06a88.11,88.11,0,1,0-11.31,11.31l50.06,50.07a8,8,0,0,0,11.32-11.32ZM40,112a72,72,0,1,1,72,72A72.08,72.08,0,0,1,40,112Z"></path></svg>

Before

Width:  |  Height:  |  Size: 264 B

After

Width:  |  Height:  |  Size: 273 B

View File

@@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" fill="#000000" viewBox="0 0 256 256"><path d="M216,48H176V40a24,24,0,0,0-24-24H104A24,24,0,0,0,80,40v8H40a8,8,0,0,0,0,16h8V208a16,16,0,0,0,16,16H192a16,16,0,0,0,16-16V64h8a8,8,0,0,0,0-16ZM96,40a8,8,0,0,1,8-8h48a8,8,0,0,1,8,8v8H96Zm96,168H64V64H192ZM112,104v64a8,8,0,0,1-16,0V104a8,8,0,0,1,16,0Zm48,0v64a8,8,0,0,1-16,0V104a8,8,0,0,1,16,0Z"></path></svg>
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" fill="currentColor" viewBox="0 0 256 256"><path d="M216,48H176V40a24,24,0,0,0-24-24H104A24,24,0,0,0,80,40v8H40a8,8,0,0,0,0,16h8V208a16,16,0,0,0,16,16H192a16,16,0,0,0,16-16V64h8a8,8,0,0,0,0-16ZM96,40a8,8,0,0,1,8-8h48a8,8,0,0,1,8,8v8H96Zm96,168H64V64H192ZM112,104v64a8,8,0,0,1-16,0V104a8,8,0,0,1,16,0Zm48,0v64a8,8,0,0,1-16,0V104a8,8,0,0,1,16,0Z"></path></svg>

Before

Width:  |  Height:  |  Size: 416 B

After

Width:  |  Height:  |  Size: 421 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" fill="currentColor" viewBox="0 0 256 256"><path d="M208,32H48A16,16,0,0,0,32,48V208a16,16,0,0,0,16,16H208a16,16,0,0,0,16-16V48A16,16,0,0,0,208,32Zm0,16V152h-28.7A15.86,15.86,0,0,0,168,156.69L148.69,176H107.31L88,156.69A15.86,15.86,0,0,0,76.69,152H48V48Zm0,160H48V168H76.69L96,187.31A15.86,15.86,0,0,0,107.31,192h41.38A15.86,15.86,0,0,0,160,187.31L179.31,168H208v40ZM90.34,109.66a8,8,0,0,1,0-11.32l32-32a8,8,0,0,1,11.32,0l32,32a8,8,0,0,1-11.32,11.32L136,91.31V152a8,8,0,0,1-16,0V91.31l-18.34,18.35A8,8,0,0,1,90.34,109.66Z"></path></svg>

After

Width:  |  Height:  |  Size: 598 B

View File

@@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" fill="#000000" viewBox="0 0 256 256"><path d="M164.44,105.34l-48-32A8,8,0,0,0,104,80v64a8,8,0,0,0,12.44,6.66l48-32a8,8,0,0,0,0-13.32ZM120,129.05V95l25.58,17ZM216,40H40A16,16,0,0,0,24,56V168a16,16,0,0,0,16,16H216a16,16,0,0,0,16-16V56A16,16,0,0,0,216,40Zm0,128H40V56H216V168Zm16,40a8,8,0,0,1-8,8H32a8,8,0,0,1,0-16H224A8,8,0,0,1,232,208Z"></path></svg>
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" fill="currentColor" viewBox="0 0 256 256"><path d="M164.44,105.34l-48-32A8,8,0,0,0,104,80v64a8,8,0,0,0,12.44,6.66l48-32a8,8,0,0,0,0-13.32ZM120,129.05V95l25.58,17ZM216,40H40A16,16,0,0,0,24,56V168a16,16,0,0,0,16,16H216a16,16,0,0,0,16-16V56A16,16,0,0,0,216,40Zm0,128H40V56H216V168Zm16,40a8,8,0,0,1-8,8H32a8,8,0,0,1,0-16H224A8,8,0,0,1,232,208Z"></path></svg>

Before

Width:  |  Height:  |  Size: 412 B

After

Width:  |  Height:  |  Size: 417 B

View File

@@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" fill="#000000" viewBox="0 0 256 256"><path d="M128,72a8,8,0,0,1,8,8v56a8,8,0,0,1-16,0V80A8,8,0,0,1,128,72ZM116,172a12,12,0,1,0,12-12A12,12,0,0,0,116,172Zm124-44a15.85,15.85,0,0,1-4.67,11.28l-96.05,96.06a16,16,0,0,1-22.56,0h0l-96-96.06a16,16,0,0,1,0-22.56l96.05-96.06a16,16,0,0,1,22.56,0l96.05,96.06A15.85,15.85,0,0,1,240,128Zm-16,0L128,32,32,128,128,224h0Z"></path></svg>
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" fill="currentColor" viewBox="0 0 256 256"><path d="M128,72a8,8,0,0,1,8,8v56a8,8,0,0,1-16,0V80A8,8,0,0,1,128,72ZM116,172a12,12,0,1,0,12-12A12,12,0,0,0,116,172Zm124-44a15.85,15.85,0,0,1-4.67,11.28l-96.05,96.06a16,16,0,0,1-22.56,0h0l-96-96.06a16,16,0,0,1,0-22.56l96.05-96.06a16,16,0,0,1,22.56,0l96.05,96.06A15.85,15.85,0,0,1,240,128Zm-16,0L128,32,32,128,128,224h0Z"></path></svg>

Before

Width:  |  Height:  |  Size: 434 B

After

Width:  |  Height:  |  Size: 439 B

35
app/src/icon.php Normal file
View File

@@ -0,0 +1,35 @@
<?php
/**
* Inline SVG icon helper.
*
* Returns the SVG markup for an icon from /assets/icons/{name}.svg.
* The SVG is inlined into the DOM so CSS `color` / `fill` cascade naturally.
* Width/height are set to 1em by default; override via CSS on the parent.
*
* Usage: <?= icon('trash') ?>
* <?= icon('search', 0, 'header-search-icon') ?>
*/
function icon(string $name, int $size = 0, string $class = ''): string {
$path = APP_ROOT . "/public/assets/icons/{$name}.svg";
if (!file_exists($path)) {
return "<!-- icon not found: {$name} -->";
}
$svg = file_get_contents($path);
// Normalise width/height to 1em so icons scale with font-size
$svg = preg_replace('/\bwidth="[^"]*"/', 'width="1em"', $svg);
$svg = preg_replace('/\bheight="[^"]*"/', 'height="1em"', $svg);
// Ensure aria-hidden by default (icons are decorative when used via this helper)
if (!str_contains($svg, 'aria-hidden')) {
$svg = str_replace('<svg', '<svg aria-hidden="true"', $svg);
}
// Inject CSS class if provided
if ($class !== '') {
if (str_contains($svg, 'class="')) {
$svg = str_replace('class="', 'class="' . $class . ' ', $svg);
} else {
$svg = str_replace('<svg', '<svg class="' . $class . '"', $svg);
}
}
return $svg;
}

View File

@@ -73,11 +73,11 @@
<div class="admin-actions">
<a href="/partage/<?= urlencode($link['slug']) ?>" target="_blank" rel="noopener"
class="admin-icon-btn admin-icon-btn--view" title="Visiter le formulaire">
<img src="/assets/icons/play-triangle.svg" width="32" height="32" alt="" aria-hidden="true">
<?= icon('play-triangle') ?>
</a>
<button type="button" class="admin-icon-btn admin-icon-btn--copy" title="Copier l'URL"
onclick="copyUrl(<?= $link['id'] ?>)">
<img src="/assets/icons/copy-duplicate.svg" width="32" height="32" alt="" aria-hidden="true">
<?= icon('copy-duplicate') ?>
</button>
<form method="post" action="actions/acces-etudiante.php" class="publish-form">
<input type="hidden" name="csrf_token" value="<?= htmlspecialchars($_SESSION['csrf_token']) ?>">
@@ -87,15 +87,15 @@
class="admin-icon-btn <?= $link['is_active'] ? 'admin-icon-btn--unpublish' : 'admin-icon-btn--publish' ?>"
title="<?= $link['is_active'] ? 'Désactiver' : 'Activer' ?>">
<?php if ($link['is_active']): ?>
<img src="/assets/icons/columns.svg" width="32" height="32" alt="" aria-hidden="true">
<?= icon('columns') ?>
<?php else: ?>
<img src="/assets/icons/play-triangle.svg" width="32" height="32" alt="" aria-hidden="true">
<?= icon('play-triangle') ?>
<?php endif; ?>
</button>
</form>
<button type="button" class="admin-icon-btn admin-icon-btn--key" title="Modifier le mot de passe"
onclick="openPasswordDialog(<?= $link['id'] ?>, <?= $hasPassword ? 'true' : 'false' ?>)">
<img src="/assets/icons/fingerprint.svg" width="32" height="32" alt="" aria-hidden="true">
<?= icon('fingerprint') ?>
</button>
<form method="post" action="actions/acces-etudiante.php" class="publish-form"
id="delete-link-form-<?= $link['id'] ?>">
@@ -104,7 +104,7 @@
<input type="hidden" name="id" value="<?= $link['id'] ?>">
<button type="button" class="admin-icon-btn admin-icon-btn--delete" title="Supprimer"
onclick="openDeleteLinkDialog(<?= $link['id'] ?>)">
<img src="/assets/icons/trash.svg" width="32" height="32" alt="" aria-hidden="true">
<?= icon('trash') ?>
</button>
</form>
</div>

View File

@@ -86,7 +86,7 @@
<button type="button" class="admin-icon-btn" title="Copier le mot de passe"
onclick="event.stopPropagation(); copyTextToClipboard(document.getElementById('pwd-<?= $link['id'] ?>').value)"
style="width:24px;height:24px;">
<img src="/assets/icons/copy-duplicate.svg" width="16" height="16" alt="" aria-hidden="true">
<?= icon('copy-duplicate') ?>
</button>
</div>
<?php else: ?>
@@ -100,11 +100,11 @@
<div class="admin-actions">
<button type="button" class="admin-icon-btn admin-icon-btn--edit" title="Éditer"
onclick="event.stopPropagation(); openEditDialog(<?= $link['id'] ?>, <?= htmlspecialchars(json_encode($linkName), ENT_QUOTES) ?>, <?= $hasLinkPassword ? 'true' : 'false' ?>, <?= htmlspecialchars(json_encode($linkExpiresVal), ENT_QUOTES) ?>)">
<img src="/assets/icons/pencil-note.svg" width="32" height="32" alt="" aria-hidden="true">
<?= icon('pencil-note') ?>
</button>
<button type="button" class="admin-icon-btn admin-icon-btn--copy" title="Copier l'URL"
onclick="event.stopPropagation(); copyUrl(<?= $link['id'] ?>)">
<img src="/assets/icons/copy-duplicate.svg" width="32" height="32" alt="" aria-hidden="true">
<?= icon('copy-duplicate') ?>
</button>
<form method="post" action="actions/acces-etudiante.php" class="publish-form" onclick="event.stopPropagation()">
<input type="hidden" name="csrf_token" value="<?= htmlspecialchars($_SESSION['csrf_token']) ?>">
@@ -114,9 +114,9 @@
class="admin-icon-btn <?= $link['is_active'] ? 'admin-icon-btn--unpublish' : 'admin-icon-btn--publish' ?>"
title="<?= $link['is_active'] ? 'Désactiver' : 'Activer' ?>">
<?php if ($link['is_active']): ?>
<img src="/assets/icons/columns.svg" width="32" height="32" alt="" aria-hidden="true">
<?= icon('columns') ?>
<?php else: ?>
<img src="/assets/icons/play-triangle.svg" width="32" height="32" alt="" aria-hidden="true">
<?= icon('play-triangle') ?>
<?php endif; ?>
</button>
</form>
@@ -127,7 +127,7 @@
<input type="hidden" name="id" value="<?= $link['id'] ?>">
<button type="button" class="admin-icon-btn admin-icon-btn--archive" title="Archiver"
onclick="openArchiveLinkDialog(<?= $link['id'] ?>)">
<img src="/assets/icons/monitor.svg" width="32" height="32" alt="" aria-hidden="true">
<?= icon('monitor') ?>
</button>
</form>
</div>
@@ -179,7 +179,7 @@
<td class="admin-actions-col">
<button type="button" class="admin-icon-btn admin-icon-btn--delete" title="Supprimer"
onclick="openDeleteArchivedLinkDialog(<?= $link['id'] ?>)">
<img src="/assets/icons/trash.svg" width="16" height="16" alt="" aria-hidden="true">
<?= icon('trash') ?>
</button>
</td>
</tr>

View File

@@ -1,5 +1,5 @@
<main id="main-content">
<h1><a href="/admin/" class="admin-back-btn" title="Retour à la liste"><img src="/assets/icons/arrow-left-circle.svg" width="32" height="32" alt="" aria-hidden="true"></a> Ajouter un TFE</h1>
<h1><a href="/admin/" class="admin-back-btn" title="Retour à la liste"><?= icon('arrow-left-circle') ?></a> Ajouter un TFE</h1>
<?php
// All form variables are already in scope from FormBootstrap::adminFormVariables().

View File

@@ -51,7 +51,7 @@
data-key="<?= $aproposKey ?>">+ Ajouter une entrée</button>
<button type="button" class="admin-icon-btn admin-icon-btn--delete delete-group-btn-f"
data-key="<?= $aproposKey ?>" title="Supprimer ce contact">
<img src="/assets/icons/trash-slash.svg" width="20" height="20" alt="" aria-hidden="true">
<?= icon('trash-slash') ?>
</button>
</fieldset>
<?php endforeach; ?>
@@ -88,7 +88,7 @@
data-key="<?= $aproposKey ?>">+ Ajouter une entrée</button>
<button type="button" class="admin-icon-btn admin-icon-btn--delete delete-group-btn-f"
data-key="<?= $aproposKey ?>" title="Supprimer ce contact">
<img src="/assets/icons/trash-slash.svg" width="20" height="20" alt="" aria-hidden="true">
<?= icon('trash-slash') ?>
</button>
</fieldset>
</template>

View File

@@ -1,5 +1,5 @@
<main id="main-content" class="full-editor-page">
<h1><a href="/admin/contenus.php" class="admin-back-btn" title="Retour"><img src="/assets/icons/arrow-left-circle.svg" width="32" height="32" alt="" aria-hidden="true"></a> Éditer : <?= htmlspecialchars($editTitle) ?></h1>
<h1><a href="/admin/contenus.php" class="admin-back-btn" title="Retour"><?= icon('arrow-left-circle') ?></a> Éditer : <?= htmlspecialchars($editTitle) ?></h1>
<?php if ($editType === 'about_page'): ?>
@@ -63,7 +63,7 @@
</div>
<button type="button" class="admin-icon-btn admin-icon-btn--delete remove-sidebar-link-btn"
title="Supprimer ce lien">
<img src="/assets/icons/trash-slash.svg" width="20" height="20" alt="" aria-hidden="true">
<?= icon('trash-slash') ?>
</button>
</div>
<?php endforeach; ?>
@@ -87,7 +87,7 @@
</div>
<button type="button" class="admin-icon-btn admin-icon-btn--delete remove-sidebar-link-btn"
title="Supprimer ce lien">
<img src="/assets/icons/trash-slash.svg" width="20" height="20" alt="" aria-hidden="true">
<?= icon('trash-slash') ?>
</button>
</div>
</template>

View File

@@ -38,7 +38,7 @@
<td>
<a href="/admin/contenus-edit.php?slug=<?= urlencode($p['slug']) ?>"
class="admin-icon-btn admin-icon-btn--edit" title="Éditer">
<img src="/assets/icons/pencil-note.svg" width="32" height="32" alt="" aria-hidden="true">
<?= icon('pencil-note') ?>
</a>
</td>
</tr>
@@ -309,10 +309,10 @@ function languesStartRename(id) {
+ '<input type="hidden" name="language_id" value="' + id + '">'
+ '<input type="text" name="new_name" value="' + cell.getAttribute('data-name') + '" required class="admin-input--inline">'
+ '<button type="submit" class="admin-icon-btn admin-icon-btn--edit" title="Valider">'
+ '<img src="/assets/icons/check-circle.svg" width="32" height="32" alt="" aria-hidden="true">'
+ '<?= icon('check-circle') ?>'
+ '</button>'
+ '<button type="button" class="admin-icon-btn admin-icon-btn--delete" onclick="languesCancelRename(' + id + ')" title="Annuler">'
+ '<img src="/assets/icons/x-close.svg" width="32" height="32" alt="" aria-hidden="true">'
+ '<?= icon('x-close') ?>'
+ '</button></form>';
cell.querySelector('input').focus();
}
@@ -321,7 +321,7 @@ function languesCancelRename(id) {
var cell = document.getElementById('lang-name-' + id);
cell.innerHTML = '<span class="tag-name-cell">' + cell.getAttribute('data-name') + '</span>'
+ '<button type="button" class="admin-icon-btn admin-icon-btn--edit" title="Renommer" onclick="languesStartRename(' + id + ')">'
+ '<img src="/assets/icons/pencil-note.svg" width="32" height="32" alt="" aria-hidden="true">'
+ '<?= icon('pencil-note') ?>'
+ '</button>';
}
@@ -488,10 +488,10 @@ function motsclesStartRename(id) {
+ '<input type="hidden" name="tag_id" value="' + id + '">'
+ '<input type="text" name="new_name" value="' + cell.getAttribute('data-name') + '" required class="admin-input--inline">'
+ '<button type="submit" class="admin-icon-btn admin-icon-btn--edit" title="Valider">'
+ '<img src="/assets/icons/check-circle.svg" width="32" height="32" alt="" aria-hidden="true">'
+ '<?= icon('check-circle') ?>'
+ '</button>'
+ '<button type="button" class="admin-icon-btn admin-icon-btn--delete" onclick="motsclesCancelRename(' + id + ')" title="Annuler">'
+ '<img src="/assets/icons/x-close.svg" width="32" height="32" alt="" aria-hidden="true">'
+ '<?= icon('x-close') ?>'
+ '</button></form>';
cell.querySelector('input').focus();
}
@@ -500,7 +500,7 @@ function motsclesCancelRename(id) {
var cell = document.getElementById('motscles-name-' + id);
cell.innerHTML = '<span class="tag-name-cell">' + cell.getAttribute('data-name') + '</span>'
+ '<button type="button" class="admin-icon-btn admin-icon-btn--edit" title="Renommer" onclick="motsclesStartRename(' + id + ')">'
+ '<img src="/assets/icons/pencil-note.svg" width="32" height="32" alt="" aria-hidden="true">'
+ '<?= icon('pencil-note') ?>'
+ '</button>';
}

View File

@@ -1,5 +1,5 @@
<main id="main-content">
<h1><a href="/admin/" class="admin-back-btn" title="Retour à la liste"><img src="/assets/icons/arrow-left-circle.svg" width="32" height="32" alt="" aria-hidden="true"></a> Modifier un TFE</h1>
<h1><a href="/admin/" class="admin-back-btn" title="Retour à la liste"><?= icon('arrow-left-circle') ?></a> Modifier un TFE</h1>
<?php
// All form variables are already in scope from FormBootstrap::adminFormVariables().

View File

@@ -87,7 +87,7 @@ $sortArrow = function(string $col) use ($sortCol, $sortDir): string {
<td class="admin-actions-col">
<div class="admin-actions">
<a href="/admin/edit.php?id=<?= $thesis['id'] ?>" class="admin-icon-btn admin-icon-btn--edit" title="Éditer" onclick="event.stopPropagation()">
<img src="/assets/icons/pencil-note.svg" width="32" height="32" alt="" aria-hidden="true">
<?= icon('pencil-note') ?>
</a>
<form method="post" action="actions/publish.php" class="publish-form" onclick="event.stopPropagation()">
<input type="hidden" name="csrf_token" value="<?= htmlspecialchars($_SESSION['csrf_token']) ?>">
@@ -95,12 +95,12 @@ $sortArrow = function(string $col) use ($sortCol, $sortDir): string {
<?php if ($thesis['is_published']): ?>
<input type="hidden" name="action" value="unpublish">
<button type="submit" class="admin-icon-btn admin-icon-btn--unpublish" title="Dépublier">
<img src="/assets/icons/eye-slash.svg" width="32" height="32" alt="" aria-hidden="true">
<?= icon('eye-slash') ?>
</button>
<?php else: ?>
<input type="hidden" name="action" value="publish">
<button type="submit" class="admin-icon-btn admin-icon-btn--publish" title="Publier">
<img src="/assets/icons/eye.svg" width="32" height="32" alt="" aria-hidden="true">
<?= icon('eye') ?>
</button>
<?php endif; ?>
</form>
@@ -109,7 +109,7 @@ $sortArrow = function(string $col) use ($sortCol, $sortDir): string {
<input type="hidden" name="thesis_id" value="<?= $thesis['id'] ?>">
<button type="button" class="admin-icon-btn admin-icon-btn--delete" title="Supprimer"
onclick="event.stopPropagation(); confirmDelete(<?= $thesis['id'] ?>, <?= htmlspecialchars(json_encode($thesis['title']), ENT_QUOTES) ?>)">
<img src="/assets/icons/trash.svg" width="32" height="32" alt="" aria-hidden="true">
<?= icon('trash') ?>
</button>
</form>
</div>

View File

@@ -2,7 +2,7 @@
<div class="admin-list-toolbar admin-list-toolbar--list">
<div class="admin-toolbar-top">
<div class="admin-toolbar-title-row">
<h1><a href="/admin/" class="admin-back-btn" title="Retour à la liste"><img src="/assets/icons/arrow-left-circle.svg" width="32" height="32" alt="" aria-hidden="true"></a> Corbeille</h1>
<h1><a href="/admin/" class="admin-back-btn" title="Retour à la liste"><?= icon('arrow-left-circle') ?></a> Corbeille</h1>
<span class="admin-stat admin-stat--inline" style="margin-left:auto"><?= count($trashedTheses) ?> TFE(s)</span>
</div>
</div>
@@ -60,7 +60,7 @@
<input type="hidden" name="action" value="restore">
<input type="hidden" name="thesis_id" value="<?= (int)$t['id'] ?>">
<button type="submit" class="admin-icon-btn admin-icon-btn--edit" title="Restaurer">
<img src="/assets/icons/archive-box.svg" width="32" height="32" alt="" aria-hidden="true">
<?= icon('archive-box') ?>
</button>
</form>
<form method="post" action="actions/corbeille.php" class="admin-inline-form" style="display:inline"
@@ -69,7 +69,7 @@
<input type="hidden" name="action" value="hard_delete">
<input type="hidden" name="thesis_id" value="<?= (int)$t['id'] ?>">
<button type="submit" class="admin-icon-btn admin-icon-btn--delete" title="Supprimer définitivement">
<img src="/assets/icons/trash.svg" width="32" height="32" alt="" aria-hidden="true">
<?= icon('trash') ?>
</button>
</form>
</div>

View File

@@ -33,7 +33,7 @@ document.addEventListener('htmx:afterSwap',()=>{document.querySelectorAll('input
</div>
</div>
<div class="admin-btn-group">
<a href="/admin/add.php" class="btn btn--primary btn--sm">+ Ajouter un TFE</a>
<a href="/admin/add.php" class="btn btn--primary btn--sm"><?= icon('plus-circle') ?> Ajouter un TFE</a>
<?php if ($trashCount > 0): ?>
<a href="/admin/index.php?tab=trash" class="btn btn--sm <?= $tab === 'trash' ? 'btn--primary' : 'btn--secondary' ?>">
Corbeille (<?= $trashCount ?>)
@@ -42,12 +42,12 @@ document.addEventListener('htmx:afterSwap',()=>{document.querySelectorAll('input
<?php if ($tmpTotalCount > 0): ?>
<button type="button" class="btn btn--sm btn--secondary" id="tmp-cleanup-btn"
onclick="document.getElementById('tmp-cleanup-dialog').showModal(); htmx.trigger('#tmp-cleanup-stats-wrapper','loadStats'); htmx.trigger('#peertube-orphans-wrapper','loadPeertube')">
Nettoyer (<?= $tmpTotalCount ?>)
<?= icon('trash') ?> Nettoyer (<?= $tmpTotalCount ?>)
</button>
<?php endif; ?>
<button type="button" class="btn btn--primary btn--sm" id="import-dialog-btn"
onclick="document.getElementById('import-dialog').showModal(); window.XamxamInitFilePonds()">
Importer
<?= icon('tray-arrow-up') ?> Importer
</button>
</div>
</div>

View File

@@ -15,7 +15,7 @@
hx-indicator="#tmp-cleanup-stats-wrapper">
<details class="n-section" open>
<summary>
<img src="/assets/icons/paint-brush-household.svg" width="14" height="14" alt="" aria-hidden="true">
<?= icon('paint-brush-household') ?>
Fichiers temporaires
</summary>
<p style="margin:0;color:var(--text-secondary)">Chargement…</p>

View File

@@ -28,7 +28,7 @@
<?php else: ?>
<!-- ═══════════════════ ADMIN MODE: Recap page ═══════════════════ -->
<h1><a href="/admin/" class="admin-back-btn" title="Retour à la liste"><img src="/assets/icons/arrow-left-circle.svg" width="32" height="32" alt="" aria-hidden="true"></a> Récapitulatif TFE</h1>
<h1><a href="/admin/" class="admin-back-btn" title="Retour à la liste"><?= icon('arrow-left-circle') ?></a> Récapitulatif TFE</h1>
<?php if ($error): ?>
<p class="toast" role="alert" data-type="error">⚠ <?= htmlspecialchars($error) ?></p>

View File

@@ -52,10 +52,10 @@ function tagsStartRename(id) {
+ '<input type=\"hidden\" name=\"tag_id\" value=\"' + id + '\">'
+ '<input type=\"text\" name=\"new_name\" value=\"' + cell.getAttribute('data-name') + '\" required class=\"admin-input--inline\">'
+ '<button type=\"submit\" class=\"admin-icon-btn admin-icon-btn--edit\" title=\"Valider\">'
+ '<img src=\"/assets/icons/check-circle.svg\" width=\"32\" height=\"32\" alt=\"\" aria-hidden=\"true\">'
+ '<?= icon("check-circle") ?>'
+ '</button>'
+ '<button type=\"button\" class=\"admin-icon-btn admin-icon-btn--delete\" onclick=\"tagsCancelRename(' + id + ')\" title=\"Annuler\">'
+ '<img src=\"/assets/icons/x-close.svg\" width=\"32\" height=\"32\" alt=\"\" aria-hidden=\"true\">'
+ '<?= icon("x-close") ?>'
+ '</button></form>';
cell.querySelector('input').focus();
}
@@ -64,7 +64,7 @@ function tagsCancelRename(id) {
var cell = document.getElementById('tag-name-' + id);
cell.innerHTML = '<span class=\"tag-name-cell\">' + cell.getAttribute('data-name') + '</span>'
+ '<button type=\"button\" class=\"admin-icon-btn admin-icon-btn--edit\" title=\"Renommer\" onclick=\"tagsStartRename(' + id + ')\">'
+ '<img src=\"/assets/icons/pencil-note.svg\" width=\"32\" height=\"32\" alt=\"\" aria-hidden=\"true\">'
+ '<?= icon("pencil-note") ?>'
+ '</button>';
}
@@ -90,7 +90,7 @@ document.addEventListener('htmx:afterSwap', function(evt) {
<div class="admin-list-toolbar admin-list-toolbar--list" style="margin-bottom:var(--space-s)">
<div class="admin-toolbar-top">
<div class="admin-toolbar-title-row">
<h1><a href="/admin/" class="admin-back-btn" title="Retour à la liste"><img src="/assets/icons/arrow-left-circle.svg" width="32" height="32" alt="" aria-hidden="true"></a> Mots-clés</h1>
<h1><a href="/admin/" class="admin-back-btn" title="Retour à la liste"><?= icon('arrow-left-circle') ?></a> Mots-clés</h1>
<span id="tags-total-count" class="admin-stat admin-stat--inline" style="margin-left:auto"></span>
</div>
</div>

View File

@@ -15,7 +15,7 @@ $_thesisId = $_GET['id'] ?? null;
<nav aria-label="Navigation admin">
<ul class="nav-left-links">
<li><a href="/" target="_blank" rel="noopener noreferrer" class="nav-logo">
<img src="/assets/icons/sign-out.svg" width="16" height="16" alt="" aria-hidden="true">XAMXAM<span class="sr-only"> (site public, nouvel onglet)</span>
<?= icon('sign-out') ?>XAMXAM<span class="sr-only"> (site public, nouvel onglet)</span>
</a></li>
</ul>
<ul class="nav-right-links">
@@ -29,7 +29,7 @@ $_thesisId = $_GET['id'] ?? null;
</a></li>
<li><a href="/admin/parametres.php" <?= in_array($_currentPage, ['parametres.php', 'system.php', 'status.php', 'logs.php']) ? 'aria-current="page"' : '' ?>>Paramètres</a></li>
<?php if ($_isAdmin && AdminAuth::hasPassword()): ?>
<li data-nav-logout><a href="/admin/logout.php" aria-label="Déconnexion"><img src="/assets/icons/sign-in.svg" width="20" height="20" alt="" aria-hidden="true"><span class="sr-only">Déconnexion</span></a></li>
<li data-nav-logout><a href="/admin/logout.php" aria-label="Déconnexion"><?= icon('sign-in') ?><span class="sr-only">Déconnexion</span></a></li>
<?php endif; ?>
</ul>
</nav>
@@ -90,7 +90,7 @@ $_thesisId = $_GET['id'] ?? null;
<?php if ($_isAdmin && !$_isLogin): ?>
<div class="admin-mobile-block">
<img src="/assets/icons/desktop-monitor.svg" width="48" height="48" alt="" aria-hidden="true">
<?= icon('desktop-monitor') ?>
<h2>Section administrateur</h2>
<p>L'administration n'est pas accessible sur mobile. Veuillez utiliser un ordinateur.</p>
</div>
@@ -106,7 +106,7 @@ $searchBarValue = $searchBarValue ?? $_GET['query'] ?? '';
role="search" aria-label="Recherche" class="header-search-form">
<label for="site-search-input" class="sr-only">Recherche</label>
<div class="header-search-input-wrap">
<img src="/assets/icons/search.svg" width="24" height="24" alt="" aria-hidden="true" class="header-search-icon">
<?= icon('search', 0, 'header-search-icon') ?>
<input
id="site-search-input"
type="text"

View File

@@ -100,7 +100,7 @@ $websiteLabel = htmlspecialchars($_POST['website_label'] ?? '');
hx-swap="innerHTML"
hx-trigger="click"
onclick="document.getElementById('relink-modal').showModal(); window.__xamxamRelinkCtx = { queueType: 'cover', thesisId: '<?= htmlspecialchars((string)($thesisId ?? $_GET['id'] ?? '')) ?>' };">
<img src="/assets/icons/magic-wand.svg" width="16" height="16" alt="" aria-hidden="true" style="vertical-align:-2px;margin-right:var(--space-3xs)"> Relier un fichier existant
<?= icon('magic-wand') ?> Relier un fichier existant
</button>
<?php endif; ?>
</div>
@@ -128,7 +128,7 @@ $websiteLabel = htmlspecialchars($_POST['website_label'] ?? '');
hx-swap="innerHTML"
hx-trigger="click"
onclick="document.getElementById('relink-modal').showModal(); window.__xamxamRelinkCtx = { queueType: 'note_intention', thesisId: '<?= htmlspecialchars((string)($thesisId ?? $_GET['id'] ?? '')) ?>' };">
<img src="/assets/icons/magic-wand.svg" width="16" height="16" alt="" aria-hidden="true" style="vertical-align:-2px;margin-right:var(--space-3xs)"> Relier un fichier existant
<?= icon('magic-wand') ?> Relier un fichier existant
</button>
<?php endif; ?>
</div>
@@ -166,7 +166,7 @@ $websiteLabel = htmlspecialchars($_POST['website_label'] ?? '');
hx-swap="innerHTML"
hx-trigger="click"
onclick="document.getElementById('relink-modal').showModal(); window.__xamxamRelinkCtx = { queueType: 'tfe', thesisId: '<?= htmlspecialchars((string)($thesisId ?? $_GET['id'] ?? '')) ?>' };">
<img src="/assets/icons/magic-wand.svg" width="16" height="16" alt="" aria-hidden="true" style="vertical-align:-2px;margin-right:var(--space-3xs)"> Relier un fichier existant
<?= icon('magic-wand') ?> Relier un fichier existant
</button>
<?php if ($peerTubeEnabled): ?>
<button type="button" class="btn btn--sm btn--ghost peertube-browser-trigger"
@@ -177,7 +177,7 @@ $websiteLabel = htmlspecialchars($_POST['website_label'] ?? '');
hx-trigger="click"
onclick="document.getElementById('peertube-relink-modal').showModal(); window.__xamxamPeertubeRelinkCtx = { thesisId: '<?= htmlspecialchars((string)($thesisId ?? $_GET['id'] ?? '')) ?>' };"
>
<img src="/assets/icons/magic-wand.svg" width="16" height="16" alt="" aria-hidden="true" style="vertical-align:-2px;margin-right:var(--space-3xs)"> Relier une vidéo PeerTube
<?= icon('magic-wand') ?> Relier une vidéo PeerTube
</button>
<?php endif; ?>
<?php endif; ?>
@@ -207,7 +207,7 @@ $websiteLabel = htmlspecialchars($_POST['website_label'] ?? '');
hx-swap="innerHTML"
hx-trigger="click"
onclick="document.getElementById('relink-modal').showModal(); window.__xamxamRelinkCtx = { queueType: 'annexe', thesisId: '<?= htmlspecialchars((string)($thesisId ?? $_GET['id'] ?? '')) ?>' };">
<img src="/assets/icons/magic-wand.svg" width="16" height="16" alt="" aria-hidden="true" style="vertical-align:-2px;margin-right:var(--space-3xs)"> Relier un fichier existant
<?= icon('magic-wand') ?> Relier un fichier existant
</button>
<?php endif; ?>
</div>

View File

@@ -65,7 +65,7 @@ $adminMode = $adminMode ?? false;
<br>
</label>
<details class="licence-details">
<summary class="licence-summary"> <img src="/assets/icons/circle-i.svg" width="1rem" height="16" alt="" aria-hidden="true"> Info</summary>
<summary class="licence-summary"> <?= icon('circle-i') ?> 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 l'erg. 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>
@@ -86,7 +86,7 @@ $adminMode = $adminMode ?? false;
</label>
<br>
<details class="licence-details">
<summary class="licence-summary"> <img src="/assets/icons/circle-i.svg" width="1rem" height="16" alt="" aria-hidden="true"> Info</summary>
<summary class="licence-summary"> <?= icon('circle-i') ?> 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. J'autorise une (ré-)utilisation et diffusion dans un contexte académique et didactique au sein de l'erg.
</p>
@@ -109,7 +109,7 @@ $adminMode = $adminMode ?? false;
</label>
<br>
<details class="licence-details">
<summary class="licence-summary"> <img src="/assets/icons/circle-i.svg" width="1rem" height="16" alt="" aria-hidden="true"> Info</summary>
<summary class="licence-summary"> <?= icon('circle-i') ?> Info</summary>
<p>
Mon TFE n'est pas disponible en physique ni sur le site. Une note descriptive est disponible sur le site.
</p>

View File

@@ -45,7 +45,7 @@ $langCount = count($selectedLanguages);
<input type="hidden" name="<?= htmlspecialchars($name) ?>[]" value="<?= htmlspecialchars($lang['name']) ?>">
<span class="tag-pill-name"><?= htmlspecialchars($lang['name']) ?></span>
<button type="button" class="tag-pill-remove" title="Retirer «&nbsp;<?= htmlspecialchars($lang['name']) ?>&nbsp;»" aria-label="Retirer <?= htmlspecialchars($lang['name']) ?>">
<img src="/assets/icons/trash-slash.svg" width="16" height="16" alt="" aria-hidden="true">
<?= icon('trash-slash') ?>
</button>
</span>
<?php endforeach; ?>

View File

@@ -48,7 +48,7 @@ $belowMin = $required && $tagCount < $minTags;
<input type="hidden" name="<?= htmlspecialchars($name) ?>[]" value="<?= htmlspecialchars($tag['name']) ?>">
<span class="tag-pill-name"><?= htmlspecialchars($tag['name']) ?></span>
<button type="button" class="tag-pill-remove" title="Retirer «&nbsp;<?= htmlspecialchars($tag['name']) ?>&nbsp;»" aria-label="Retirer <?= htmlspecialchars($tag['name']) ?>">
<img src="/assets/icons/trash-slash.svg" width="16" height="16" alt="" aria-hidden="true">
<?= icon('trash-slash') ?>
</button>
</span>
<?php endforeach; ?>

View File

@@ -6,7 +6,7 @@ $_sbValue = $searchBarValue ?? $_GET['query'] ?? '';
<form method="GET" action="/search"
role="search" aria-label="Recherche">
<label for="site-search-input" class="sr-only">Recherche</label>
<img src="/assets/icons/search.svg" width="24" height="24" alt="" aria-hidden="true">
<?= icon('search') ?>
<input
id="site-search-input"
type="text"