mirror of
https://codeberg.org/PostERG/xamxam.git
synced 2026-06-25 16:19:19 +02:00
Split form.css into form-base.css and form-admin.css, drop dead upload-progress code
Also introduces $extraCssAdmin support in head.php for admin-only stylesheets (form-admin.css, filepond CSS, system.css). Admin pages now use $extraCssAdmin for admin-only assets and $extraCss for shared stylesheets like form-base.css.
This commit is contained in:
@@ -32,12 +32,7 @@ require_once APP_ROOT . '/src/ErrorHandler.php';
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
$ctrl = ThesisEditController::create();
|
$ctrl = ThesisEditController::create();
|
||||||
$progressToken = $_POST['progress_token'] ?? bin2hex(random_bytes(8));
|
$ctrl->save($thesisId, $_POST, $_FILES);
|
||||||
$ctrl->save($thesisId, $_POST, $_FILES, $progressToken);
|
|
||||||
|
|
||||||
// Clean up progress file
|
|
||||||
require_once APP_ROOT . '/src/PeerTubeService.php';
|
|
||||||
PeerTubeService::clearProgress($progressToken);
|
|
||||||
|
|
||||||
// Regenerate CSRF token after successful save
|
// Regenerate CSRF token after successful save
|
||||||
$_SESSION['csrf_token'] = bin2hex(random_bytes(32));
|
$_SESSION['csrf_token'] = bin2hex(random_bytes(32));
|
||||||
|
|||||||
@@ -1,45 +0,0 @@
|
|||||||
<?php
|
|
||||||
/**
|
|
||||||
* upload-progress.php
|
|
||||||
*
|
|
||||||
* Returns the current upload/processing progress for a given token.
|
|
||||||
* Called by the client-side upload-progress.js while the form XHR is in flight.
|
|
||||||
*
|
|
||||||
* GET /admin/actions/upload-progress.php?token=<token>
|
|
||||||
*
|
|
||||||
* Response: JSON
|
|
||||||
* { "stage": "upload"|"processing"|"done", "pct": 45, "file": "video.mp4" }
|
|
||||||
*
|
|
||||||
* Progress data is written by ThesisEditController / ThesisCreateController
|
|
||||||
* to a temp file during processing.
|
|
||||||
*/
|
|
||||||
|
|
||||||
require_once __DIR__ . '/../../../bootstrap.php';
|
|
||||||
// No AdminAuth check here — this endpoint is called by client-side JS during
|
|
||||||
// both admin and partage (student) form uploads. Access is guarded by the
|
|
||||||
// progress token (64 bits of entropy, fresh per form render) which must match
|
|
||||||
// a temp file that only exists during an active upload.
|
|
||||||
|
|
||||||
header('Content-Type: application/json');
|
|
||||||
|
|
||||||
$token = $_GET['token'] ?? '';
|
|
||||||
if (!preg_match('/^[a-f0-9]{16}$/', $token)) {
|
|
||||||
echo json_encode(['stage' => 'error', 'error' => 'Invalid token']);
|
|
||||||
exit;
|
|
||||||
}
|
|
||||||
|
|
||||||
$progressFile = sys_get_temp_dir() . '/xamxam_upload_' . $token . '.json';
|
|
||||||
|
|
||||||
if (!file_exists($progressFile)) {
|
|
||||||
// No progress file yet — still in upload phase (or token invalid)
|
|
||||||
echo json_encode(['stage' => 'upload', 'pct' => 0, 'file' => '']);
|
|
||||||
exit;
|
|
||||||
}
|
|
||||||
|
|
||||||
$data = json_decode(file_get_contents($progressFile), true);
|
|
||||||
if (!$data) {
|
|
||||||
echo json_encode(['stage' => 'upload', 'pct' => 0, 'file' => '']);
|
|
||||||
exit;
|
|
||||||
}
|
|
||||||
|
|
||||||
echo json_encode($data);
|
|
||||||
@@ -54,7 +54,8 @@ function wasSelected($key, $value) {
|
|||||||
|
|
||||||
$isAdmin = true;
|
$isAdmin = true;
|
||||||
$bodyClass = 'admin-body';
|
$bodyClass = 'admin-body';
|
||||||
$extraCss = ['/assets/css/form.css', '/assets/css/filepond.min.css', '/assets/css/filepond-plugin-image-preview.min.css'];
|
$extraCss = ['/assets/css/form-base.css'];
|
||||||
|
$extraCssAdmin = ['/assets/css/form-admin.css', '/assets/css/filepond.min.css', '/assets/css/filepond-plugin-image-preview.min.css'];
|
||||||
$extraJs = ['/assets/js/vendor/filepond.min.js', '/assets/js/vendor/filepond-plugin-file-validate-type.min.js', '/assets/js/vendor/filepond-plugin-file-validate-size.min.js', '/assets/js/vendor/filepond-plugin-image-preview.min.js', '/assets/js/vendor/filepond-plugin-image-exif-orientation.min.js', '/assets/js/app/file-upload-filepond.js', '/assets/js/app/beforeunload-guard.js', '/assets/js/app/pill-search.js', '/assets/js/app/jury-autocomplete.js'];
|
$extraJs = ['/assets/js/vendor/filepond.min.js', '/assets/js/vendor/filepond-plugin-file-validate-type.min.js', '/assets/js/vendor/filepond-plugin-file-validate-size.min.js', '/assets/js/vendor/filepond-plugin-image-preview.min.js', '/assets/js/vendor/filepond-plugin-image-exif-orientation.min.js', '/assets/js/app/file-upload-filepond.js', '/assets/js/app/beforeunload-guard.js', '/assets/js/app/pill-search.js', '/assets/js/app/jury-autocomplete.js'];
|
||||||
require_once APP_ROOT . '/templates/head.php';
|
require_once APP_ROOT . '/templates/head.php';
|
||||||
include APP_ROOT . '/templates/header.php';
|
include APP_ROOT . '/templates/header.php';
|
||||||
|
|||||||
@@ -39,7 +39,8 @@ try {
|
|||||||
}
|
}
|
||||||
|
|
||||||
$isAdmin = true; $bodyClass = 'admin-body';
|
$isAdmin = true; $bodyClass = 'admin-body';
|
||||||
$extraCss = ['/assets/css/form.css', '/assets/css/filepond.min.css', '/assets/css/filepond-plugin-image-preview.min.css'];
|
$extraCss = ['/assets/css/form-base.css'];
|
||||||
|
$extraCssAdmin = ['/assets/css/form-admin.css', '/assets/css/filepond.min.css', '/assets/css/filepond-plugin-image-preview.min.css'];
|
||||||
$extraJs = ['/assets/js/vendor/filepond.min.js', '/assets/js/vendor/filepond-plugin-file-validate-type.min.js', '/assets/js/vendor/filepond-plugin-file-validate-size.min.js', '/assets/js/vendor/filepond-plugin-image-preview.min.js', '/assets/js/vendor/filepond-plugin-image-exif-orientation.min.js', '/assets/js/app/file-upload-filepond.js', '/assets/js/app/beforeunload-guard.js', '/assets/js/app/pill-search.js', '/assets/js/app/jury-autocomplete.js'];
|
$extraJs = ['/assets/js/vendor/filepond.min.js', '/assets/js/vendor/filepond-plugin-file-validate-type.min.js', '/assets/js/vendor/filepond-plugin-file-validate-size.min.js', '/assets/js/vendor/filepond-plugin-image-preview.min.js', '/assets/js/vendor/filepond-plugin-image-exif-orientation.min.js', '/assets/js/app/file-upload-filepond.js', '/assets/js/app/beforeunload-guard.js', '/assets/js/app/pill-search.js', '/assets/js/app/jury-autocomplete.js'];
|
||||||
require_once APP_ROOT . '/templates/head.php';
|
require_once APP_ROOT . '/templates/head.php';
|
||||||
include APP_ROOT . '/templates/header.php';
|
include APP_ROOT . '/templates/header.php';
|
||||||
|
|||||||
@@ -515,7 +515,7 @@ if ($isHtmx) {
|
|||||||
include APP_ROOT . '/templates/admin/index-table.php';
|
include APP_ROOT . '/templates/admin/index-table.php';
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
$extraCss = ['/assets/css/filepond.min.css', '/assets/css/filepond-plugin-image-preview.min.css'];
|
$extraCssAdmin = ['/assets/css/filepond.min.css', '/assets/css/filepond-plugin-image-preview.min.css'];
|
||||||
$extraJs = ['/assets/js/vendor/filepond.min.js', '/assets/js/vendor/filepond-plugin-file-validate-type.min.js', '/assets/js/vendor/filepond-plugin-file-validate-size.min.js', '/assets/js/vendor/filepond-plugin-image-preview.min.js', '/assets/js/vendor/filepond-plugin-image-exif-orientation.min.js', '/assets/js/app/file-upload-filepond.js'];
|
$extraJs = ['/assets/js/vendor/filepond.min.js', '/assets/js/vendor/filepond-plugin-file-validate-type.min.js', '/assets/js/vendor/filepond-plugin-file-validate-size.min.js', '/assets/js/vendor/filepond-plugin-image-preview.min.js', '/assets/js/vendor/filepond-plugin-image-exif-orientation.min.js', '/assets/js/app/file-upload-filepond.js'];
|
||||||
require_once APP_ROOT . '/templates/head.php';
|
require_once APP_ROOT . '/templates/head.php';
|
||||||
include APP_ROOT . '/templates/header.php';
|
include APP_ROOT . '/templates/header.php';
|
||||||
|
|||||||
@@ -74,7 +74,7 @@ if (empty($_SESSION['csrf_token'])) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
$isAdmin = true; $bodyClass = 'admin-body';
|
$isAdmin = true; $bodyClass = 'admin-body';
|
||||||
$extraCss = ['/assets/css/system.css'];
|
$extraCssAdmin = ['/assets/css/system.css'];
|
||||||
require_once APP_ROOT . '/templates/head.php';
|
require_once APP_ROOT . '/templates/head.php';
|
||||||
include APP_ROOT . '/templates/header.php';
|
include APP_ROOT . '/templates/header.php';
|
||||||
include APP_ROOT . '/templates/admin/parametres.php';
|
include APP_ROOT . '/templates/admin/parametres.php';
|
||||||
|
|||||||
@@ -115,7 +115,7 @@
|
|||||||
fill: currentColor;
|
fill: currentColor;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ── Form styles → see form.css ─────────────────────────────────────────── */
|
/* ── Form styles → see form-base.css + form-admin.css ───────────────────── */
|
||||||
|
|
||||||
/* ── Buttons ────────────────────────────────────────────────────────────── */
|
/* ── Buttons ────────────────────────────────────────────────────────────── */
|
||||||
.admin-form-footer {
|
.admin-form-footer {
|
||||||
@@ -763,7 +763,7 @@ th.admin-ap-col {
|
|||||||
padding: 0;
|
padding: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ── Jury fieldset → see form.css ───────────────────────────────────────── */
|
/* ── Jury fieldset → see form-base.css ──────────────────────────────────── */
|
||||||
|
|
||||||
/* ── Inline form (tags page) ────────────────────────────────────────────── */
|
/* ── Inline form (tags page) ────────────────────────────────────────────── */
|
||||||
.admin-inline-form {
|
.admin-inline-form {
|
||||||
@@ -1679,7 +1679,7 @@ th.admin-ap-col {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* ── Form group, student mode, thanks page → see form.css ───────────────── */
|
/* ── Form group, student mode, thanks page → see form-base.css ──────────── */
|
||||||
|
|
||||||
/* ── Utility ─────────────────────────────────────────────────────────────── */
|
/* ── Utility ─────────────────────────────────────────────────────────────── */
|
||||||
|
|
||||||
|
|||||||
234
app/public/assets/css/form-admin.css
Normal file
234
app/public/assets/css/form-admin.css
Normal file
@@ -0,0 +1,234 @@
|
|||||||
|
/* ============================================================
|
||||||
|
FORM — Admin-only extensions
|
||||||
|
Styles used exclusively by admin pages (not the student partage form).
|
||||||
|
Loaded after form-base.css.
|
||||||
|
============================================================ */
|
||||||
|
|
||||||
|
/* ── Recap files table ─────────────────────────────────────────── */
|
||||||
|
|
||||||
|
.recap-files-table {
|
||||||
|
width: 100%;
|
||||||
|
border-collapse: collapse;
|
||||||
|
font-size: var(--step--1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.recap-files-table thead th {
|
||||||
|
text-align: left;
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--text-secondary);
|
||||||
|
font-size: var(--step--2);
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 0.05em;
|
||||||
|
padding: var(--space-xs) var(--space-s);
|
||||||
|
border-bottom: 2px solid var(--border-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.recap-files-table tbody td {
|
||||||
|
padding: var(--space-xs) var(--space-s);
|
||||||
|
border-bottom: 1px solid var(--border-secondary);
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
|
||||||
|
.recap-files-table tbody tr:last-child td {
|
||||||
|
border-bottom: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.recap-files-icon {
|
||||||
|
width: 1px;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.recap-files-icon-emoji {
|
||||||
|
font-size: 1.3rem;
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
width: 2.2rem;
|
||||||
|
height: 2.2rem;
|
||||||
|
background: var(--accent-muted);
|
||||||
|
border-radius: var(--radius);
|
||||||
|
}
|
||||||
|
|
||||||
|
.recap-files-thumb {
|
||||||
|
max-width: 80px;
|
||||||
|
max-height: 60px;
|
||||||
|
object-fit: cover;
|
||||||
|
border-radius: var(--radius);
|
||||||
|
}
|
||||||
|
|
||||||
|
.recap-files-name {
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.recap-files-name a {
|
||||||
|
color: var(--text-primary);
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.recap-files-name a:hover {
|
||||||
|
color: var(--accent-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.recap-files-peertube-id {
|
||||||
|
display: block;
|
||||||
|
font-size: var(--step--2);
|
||||||
|
color: var(--text-tertiary);
|
||||||
|
font-family: monospace;
|
||||||
|
}
|
||||||
|
|
||||||
|
.recap-files-label {
|
||||||
|
display: block;
|
||||||
|
font-size: var(--step--2);
|
||||||
|
color: var(--text-secondary);
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
|
||||||
|
.recap-files-type {
|
||||||
|
font-size: var(--step--2);
|
||||||
|
color: var(--text-tertiary);
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.recap-files-size {
|
||||||
|
font-size: var(--step--2);
|
||||||
|
color: var(--text-tertiary);
|
||||||
|
white-space: nowrap;
|
||||||
|
font-variant-numeric: tabular-nums;
|
||||||
|
}
|
||||||
|
|
||||||
|
.recap-files-date {
|
||||||
|
font-size: var(--step--2);
|
||||||
|
color: var(--text-tertiary);
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── File browser (relink) ──────────────────────────────────────── */
|
||||||
|
|
||||||
|
.file-browser-trigger {
|
||||||
|
margin-top: var(--space-2xs);
|
||||||
|
font-size: var(--step--2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.relink-modal {
|
||||||
|
width: min(90vw, 700px);
|
||||||
|
max-height: 80vh;
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
border-radius: var(--radius);
|
||||||
|
background: var(--bg-primary);
|
||||||
|
padding: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
color: var(--text-primary);
|
||||||
|
}
|
||||||
|
.relink-modal::backdrop {
|
||||||
|
background: rgba(0,0,0,0.5);
|
||||||
|
}
|
||||||
|
.relink-modal-header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
padding: var(--space-s) var(--space-m);
|
||||||
|
border-bottom: 1px solid var(--border);
|
||||||
|
background: var(--bg-secondary);
|
||||||
|
}
|
||||||
|
.relink-modal-header h3 {
|
||||||
|
margin: 0;
|
||||||
|
font-size: var(--step-0);
|
||||||
|
}
|
||||||
|
.relink-modal-footer {
|
||||||
|
padding: var(--space-xs) var(--space-m);
|
||||||
|
border-top: 1px solid var(--border);
|
||||||
|
background: var(--bg-secondary);
|
||||||
|
}
|
||||||
|
#relink-modal-body {
|
||||||
|
padding: var(--space-s) var(--space-m);
|
||||||
|
overflow-y: auto;
|
||||||
|
max-height: 60vh;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── File browser tree ─────────────────────────────────────────── */
|
||||||
|
|
||||||
|
.file-browser {
|
||||||
|
font-size: var(--step--1);
|
||||||
|
}
|
||||||
|
.file-browser-hint,
|
||||||
|
.file-browser-empty,
|
||||||
|
.file-browser-loading,
|
||||||
|
.file-browser-error {
|
||||||
|
color: var(--text-secondary);
|
||||||
|
text-align: center;
|
||||||
|
padding: var(--space-m);
|
||||||
|
}
|
||||||
|
.file-browser-error {
|
||||||
|
color: var(--error);
|
||||||
|
}
|
||||||
|
.file-browser-breadcrumb {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: var(--space-2xs);
|
||||||
|
flex-wrap: wrap;
|
||||||
|
padding-bottom: var(--space-s);
|
||||||
|
margin-bottom: var(--space-s);
|
||||||
|
border-bottom: 1px solid var(--border);
|
||||||
|
font-size: var(--step--2);
|
||||||
|
}
|
||||||
|
.file-browser-breadcrumb a {
|
||||||
|
color: var(--accent-blue, var(--link));
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
.file-browser-breadcrumb a:hover {
|
||||||
|
color: var(--accent-primary);
|
||||||
|
}
|
||||||
|
.file-browser-sep {
|
||||||
|
color: var(--text-tertiary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.file-browser-list {
|
||||||
|
list-style: none;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
.file-browser-entry {
|
||||||
|
border-bottom: 1px solid var(--border-subtle);
|
||||||
|
}
|
||||||
|
.file-browser-entry a,
|
||||||
|
.file-browser-select-btn {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: var(--space-xs);
|
||||||
|
width: 100%;
|
||||||
|
padding: var(--space-xs) var(--space-2xs);
|
||||||
|
text-decoration: none;
|
||||||
|
color: var(--text-primary);
|
||||||
|
background: none;
|
||||||
|
border: none;
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: var(--step--1);
|
||||||
|
text-align: left;
|
||||||
|
transition: background 0.15s;
|
||||||
|
}
|
||||||
|
.file-browser-entry a:hover,
|
||||||
|
.file-browser-select-btn:hover {
|
||||||
|
background: var(--bg-secondary);
|
||||||
|
}
|
||||||
|
.file-browser-icon {
|
||||||
|
flex-shrink: 0;
|
||||||
|
font-size: var(--step-0);
|
||||||
|
}
|
||||||
|
.file-browser-name {
|
||||||
|
flex: 1;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
.file-browser-size {
|
||||||
|
flex-shrink: 0;
|
||||||
|
color: var(--text-tertiary);
|
||||||
|
font-size: var(--step--2);
|
||||||
|
font-variant-numeric: tabular-nums;
|
||||||
|
}
|
||||||
|
.file-browser-file .file-browser-select-btn {
|
||||||
|
color: var(--accent-green, var(--success));
|
||||||
|
}
|
||||||
|
.file-browser-file .file-browser-select-btn:hover {
|
||||||
|
background: var(--bg-secondary);
|
||||||
|
}
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
/* ============================================================
|
/* ============================================================
|
||||||
FORM — TFE submission form
|
FORM — TFE submission form (base styles)
|
||||||
Shared between admin add/edit pages and the student partage form.
|
Shared between admin add/edit pages and the student partage form.
|
||||||
|
Admin-only extensions live in form-admin.css.
|
||||||
Variables loaded via style.css (colors.css + typography.css).
|
Variables loaded via style.css (colors.css + typography.css).
|
||||||
============================================================ */
|
============================================================ */
|
||||||
|
|
||||||
@@ -1156,61 +1157,7 @@
|
|||||||
color: var(--text-tertiary);
|
color: var(--text-tertiary);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ── Upload progress bar ─────────────────────────────────── */
|
|
||||||
|
|
||||||
#upload-progress-wrap {
|
|
||||||
border: 1px solid var(--accent-muted);
|
|
||||||
border-radius: var(--radius);
|
|
||||||
padding: var(--space-s);
|
|
||||||
margin-bottom: var(--space-s);
|
|
||||||
}
|
|
||||||
|
|
||||||
#upload-progress-wrap legend {
|
|
||||||
font-weight: 600;
|
|
||||||
font-size: var(--step--1);
|
|
||||||
color: var(--text-primary);
|
|
||||||
padding: 0 var(--space-2xs);
|
|
||||||
}
|
|
||||||
|
|
||||||
#upload-progress-bar {
|
|
||||||
display: block;
|
|
||||||
width: 100%;
|
|
||||||
height: 0.85rem;
|
|
||||||
border-radius: var(--radius);
|
|
||||||
overflow: hidden;
|
|
||||||
background: var(--accent-muted);
|
|
||||||
border: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
#upload-progress-bar::-webkit-progress-bar {
|
|
||||||
background: var(--accent-muted);
|
|
||||||
border-radius: var(--radius);
|
|
||||||
}
|
|
||||||
|
|
||||||
#upload-progress-bar::-webkit-progress-value {
|
|
||||||
background: var(--accent-primary);
|
|
||||||
border-radius: var(--radius);
|
|
||||||
transition: width 0.3s ease;
|
|
||||||
}
|
|
||||||
|
|
||||||
#upload-progress-bar::-moz-progress-bar {
|
|
||||||
background: var(--accent-primary);
|
|
||||||
border-radius: var(--radius);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Completion state */
|
|
||||||
#upload-progress-bar[data-complete] {
|
|
||||||
box-shadow: 0 0 12px rgba(149, 87, 181, 0.4);
|
|
||||||
}
|
|
||||||
|
|
||||||
#upload-progress-bar[data-complete]::-webkit-progress-value {
|
|
||||||
background: var(--accent-green);
|
|
||||||
box-shadow: 0 0 8px rgba(76, 175, 80, 0.3);
|
|
||||||
}
|
|
||||||
|
|
||||||
#upload-progress-bar[data-complete]::-moz-progress-bar {
|
|
||||||
background: var(--accent-green);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ── Sticky formats fieldset ──────────────────────────────── */
|
/* ── Sticky formats fieldset ──────────────────────────────── */
|
||||||
|
|
||||||
@@ -1266,324 +1213,6 @@ legend {
|
|||||||
padding: 0 var(--space-2xs);
|
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;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: var(--space-s);
|
|
||||||
}
|
|
||||||
|
|
||||||
.admin-file-figure {
|
|
||||||
display: flex;
|
|
||||||
align-items: flex-start;
|
|
||||||
gap: var(--space-s);
|
|
||||||
background: var(--bg-secondary);
|
|
||||||
border-radius: var(--radius);
|
|
||||||
padding: var(--space-s);
|
|
||||||
margin: 0;
|
|
||||||
flex: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.admin-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 {
|
|
||||||
max-width: 120px;
|
|
||||||
max-height: 90px;
|
|
||||||
object-fit: cover;
|
|
||||||
border-radius: var(--radius);
|
|
||||||
flex-shrink: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.admin-file-caption {
|
|
||||||
flex: 1;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: var(--space-3xs);
|
|
||||||
}
|
|
||||||
|
|
||||||
.admin-file-name-row {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: var(--space-2xs);
|
|
||||||
}
|
|
||||||
|
|
||||||
.admin-file-name {
|
|
||||||
font-weight: 600;
|
|
||||||
font-size: var(--step--1);
|
|
||||||
color: var(--text-primary);
|
|
||||||
}
|
|
||||||
|
|
||||||
.admin-file-peertube-id {
|
|
||||||
font-size: var(--step--2);
|
|
||||||
color: var(--text-tertiary);
|
|
||||||
font-family: monospace;
|
|
||||||
}
|
|
||||||
|
|
||||||
.admin-file-meta-row {
|
|
||||||
display: flex;
|
|
||||||
gap: var(--space-xs);
|
|
||||||
align-items: center;
|
|
||||||
font-size: var(--step--2);
|
|
||||||
color: var(--text-tertiary);
|
|
||||||
}
|
|
||||||
|
|
||||||
.admin-file-label {
|
|
||||||
font-size: var(--step--1);
|
|
||||||
color: var(--text-secondary);
|
|
||||||
font-style: italic;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ── Recap files table ─────────────────────────────────────────── */
|
|
||||||
|
|
||||||
.recap-files-table {
|
|
||||||
width: 100%;
|
|
||||||
border-collapse: collapse;
|
|
||||||
font-size: var(--step--1);
|
|
||||||
}
|
|
||||||
|
|
||||||
.recap-files-table thead th {
|
|
||||||
text-align: left;
|
|
||||||
font-weight: 600;
|
|
||||||
color: var(--text-secondary);
|
|
||||||
font-size: var(--step--2);
|
|
||||||
text-transform: uppercase;
|
|
||||||
letter-spacing: 0.05em;
|
|
||||||
padding: var(--space-xs) var(--space-s);
|
|
||||||
border-bottom: 2px solid var(--border-primary);
|
|
||||||
}
|
|
||||||
|
|
||||||
.recap-files-table tbody td {
|
|
||||||
padding: var(--space-xs) var(--space-s);
|
|
||||||
border-bottom: 1px solid var(--border-secondary);
|
|
||||||
vertical-align: middle;
|
|
||||||
}
|
|
||||||
|
|
||||||
.recap-files-table tbody tr:last-child td {
|
|
||||||
border-bottom: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.recap-files-icon {
|
|
||||||
width: 1px;
|
|
||||||
white-space: nowrap;
|
|
||||||
}
|
|
||||||
|
|
||||||
.recap-files-icon-emoji {
|
|
||||||
font-size: 1.3rem;
|
|
||||||
display: inline-flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
width: 2.2rem;
|
|
||||||
height: 2.2rem;
|
|
||||||
background: var(--accent-muted);
|
|
||||||
border-radius: var(--radius);
|
|
||||||
}
|
|
||||||
|
|
||||||
.recap-files-thumb {
|
|
||||||
max-width: 80px;
|
|
||||||
max-height: 60px;
|
|
||||||
object-fit: cover;
|
|
||||||
border-radius: var(--radius);
|
|
||||||
}
|
|
||||||
|
|
||||||
.recap-files-name {
|
|
||||||
font-weight: 500;
|
|
||||||
}
|
|
||||||
|
|
||||||
.recap-files-name a {
|
|
||||||
color: var(--text-primary);
|
|
||||||
text-decoration: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.recap-files-name a:hover {
|
|
||||||
color: var(--accent-primary);
|
|
||||||
}
|
|
||||||
|
|
||||||
.recap-files-peertube-id {
|
|
||||||
display: block;
|
|
||||||
font-size: var(--step--2);
|
|
||||||
color: var(--text-tertiary);
|
|
||||||
font-family: monospace;
|
|
||||||
}
|
|
||||||
|
|
||||||
.recap-files-label {
|
|
||||||
display: block;
|
|
||||||
font-size: var(--step--2);
|
|
||||||
color: var(--text-secondary);
|
|
||||||
font-style: italic;
|
|
||||||
}
|
|
||||||
|
|
||||||
.recap-files-type {
|
|
||||||
font-size: var(--step--2);
|
|
||||||
color: var(--text-tertiary);
|
|
||||||
white-space: nowrap;
|
|
||||||
}
|
|
||||||
|
|
||||||
.recap-files-size {
|
|
||||||
font-size: var(--step--2);
|
|
||||||
color: var(--text-tertiary);
|
|
||||||
white-space: nowrap;
|
|
||||||
font-variant-numeric: tabular-nums;
|
|
||||||
}
|
|
||||||
|
|
||||||
.recap-files-date {
|
|
||||||
font-size: var(--step--2);
|
|
||||||
color: var(--text-tertiary);
|
|
||||||
white-space: nowrap;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ── File browser (relink) ──────────────────────────────────────── */
|
|
||||||
|
|
||||||
.file-browser-trigger {
|
|
||||||
margin-top: var(--space-2xs);
|
|
||||||
font-size: var(--step--2);
|
|
||||||
}
|
|
||||||
|
|
||||||
.relink-modal {
|
|
||||||
width: min(90vw, 700px);
|
|
||||||
max-height: 80vh;
|
|
||||||
border: 1px solid var(--border);
|
|
||||||
border-radius: var(--radius);
|
|
||||||
background: var(--bg-primary);
|
|
||||||
padding: 0;
|
|
||||||
overflow: hidden;
|
|
||||||
color: var(--text-primary);
|
|
||||||
}
|
|
||||||
.relink-modal::backdrop {
|
|
||||||
background: rgba(0,0,0,0.5);
|
|
||||||
}
|
|
||||||
.relink-modal-header {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: space-between;
|
|
||||||
padding: var(--space-s) var(--space-m);
|
|
||||||
border-bottom: 1px solid var(--border);
|
|
||||||
background: var(--bg-secondary);
|
|
||||||
}
|
|
||||||
.relink-modal-header h3 {
|
|
||||||
margin: 0;
|
|
||||||
font-size: var(--step-0);
|
|
||||||
}
|
|
||||||
.relink-modal-footer {
|
|
||||||
padding: var(--space-xs) var(--space-m);
|
|
||||||
border-top: 1px solid var(--border);
|
|
||||||
background: var(--bg-secondary);
|
|
||||||
}
|
|
||||||
#relink-modal-body {
|
|
||||||
padding: var(--space-s) var(--space-m);
|
|
||||||
overflow-y: auto;
|
|
||||||
max-height: 60vh;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ── File browser tree ─────────────────────────────────────────── */
|
|
||||||
|
|
||||||
.file-browser {
|
|
||||||
font-size: var(--step--1);
|
|
||||||
}
|
|
||||||
.file-browser-hint,
|
|
||||||
.file-browser-empty,
|
|
||||||
.file-browser-loading,
|
|
||||||
.file-browser-error {
|
|
||||||
color: var(--text-secondary);
|
|
||||||
text-align: center;
|
|
||||||
padding: var(--space-m);
|
|
||||||
}
|
|
||||||
.file-browser-error {
|
|
||||||
color: var(--error);
|
|
||||||
}
|
|
||||||
.file-browser-breadcrumb {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: var(--space-2xs);
|
|
||||||
flex-wrap: wrap;
|
|
||||||
padding-bottom: var(--space-s);
|
|
||||||
margin-bottom: var(--space-s);
|
|
||||||
border-bottom: 1px solid var(--border);
|
|
||||||
font-size: var(--step--2);
|
|
||||||
}
|
|
||||||
.file-browser-breadcrumb a {
|
|
||||||
color: var(--accent-blue, var(--link));
|
|
||||||
text-decoration: none;
|
|
||||||
}
|
|
||||||
.file-browser-breadcrumb a:hover {
|
|
||||||
color: var(--accent-primary);
|
|
||||||
}
|
|
||||||
.file-browser-sep {
|
|
||||||
color: var(--text-tertiary);
|
|
||||||
}
|
|
||||||
|
|
||||||
.file-browser-list {
|
|
||||||
list-style: none;
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
.file-browser-entry {
|
|
||||||
border-bottom: 1px solid var(--border-subtle);
|
|
||||||
}
|
|
||||||
.file-browser-entry a,
|
|
||||||
.file-browser-select-btn {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: var(--space-xs);
|
|
||||||
width: 100%;
|
|
||||||
padding: var(--space-xs) var(--space-2xs);
|
|
||||||
text-decoration: none;
|
|
||||||
color: var(--text-primary);
|
|
||||||
background: none;
|
|
||||||
border: none;
|
|
||||||
cursor: pointer;
|
|
||||||
font-size: var(--step--1);
|
|
||||||
text-align: left;
|
|
||||||
transition: background 0.15s;
|
|
||||||
}
|
|
||||||
.file-browser-entry a:hover,
|
|
||||||
.file-browser-select-btn:hover {
|
|
||||||
background: var(--bg-secondary);
|
|
||||||
}
|
|
||||||
.file-browser-icon {
|
|
||||||
flex-shrink: 0;
|
|
||||||
font-size: var(--step-0);
|
|
||||||
}
|
|
||||||
.file-browser-name {
|
|
||||||
flex: 1;
|
|
||||||
overflow: hidden;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
white-space: nowrap;
|
|
||||||
}
|
|
||||||
.file-browser-size {
|
|
||||||
flex-shrink: 0;
|
|
||||||
color: var(--text-tertiary);
|
|
||||||
font-size: var(--step--2);
|
|
||||||
font-variant-numeric: tabular-nums;
|
|
||||||
}
|
|
||||||
.file-browser-file .file-browser-select-btn {
|
|
||||||
color: var(--accent-green, var(--success));
|
|
||||||
}
|
|
||||||
.file-browser-file .file-browser-select-btn:hover {
|
|
||||||
background: var(--bg-secondary);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ── Mobile-responsive layout ──────────────────────────────────────────── */
|
/* ── Mobile-responsive layout ──────────────────────────────────────────── */
|
||||||
/* Below 600px: labels stack above inputs, single-column form layout.
|
/* Below 600px: labels stack above inputs, single-column form layout.
|
||||||
@@ -1,221 +0,0 @@
|
|||||||
/**
|
|
||||||
* upload-progress.js
|
|
||||||
*
|
|
||||||
* Intercepts admin form submissions with files and submits via XMLHttpRequest.
|
|
||||||
* Polls GET /admin/actions/upload-progress.php?token=xxx for server-side
|
|
||||||
* processing progress (PeerTube uploads, file moves).
|
|
||||||
*
|
|
||||||
* Progress display:
|
|
||||||
* 0%–25% : browser → server upload (XHR upload.progress)
|
|
||||||
* 25%–99% : server processing (polled from progress endpoint)
|
|
||||||
* 100% : response received — "Téléversé avec succès", then redirect
|
|
||||||
*/
|
|
||||||
(() => {
|
|
||||||
const FORMS = document.querySelectorAll("form[data-upload-progress]");
|
|
||||||
if (!FORMS.length) return;
|
|
||||||
|
|
||||||
const POLL_INTERVAL = 400;
|
|
||||||
const UPLOAD_CAP = 25;
|
|
||||||
const PROCESSING_MAX = 99;
|
|
||||||
const SUCCESS_DELAY = 800;
|
|
||||||
|
|
||||||
for (const form of FORMS) {
|
|
||||||
const progressWrap = form.querySelector("#upload-progress-wrap");
|
|
||||||
const progressBar = form.querySelector("#upload-progress-bar");
|
|
||||||
const progressLabel = form.querySelector("#upload-progress-label");
|
|
||||||
const progressFile = form.querySelector("#upload-progress-file");
|
|
||||||
const submitBtn = form.querySelector('button[type="submit"]');
|
|
||||||
const tokenInput = form.querySelector('input[name="progress_token"]');
|
|
||||||
|
|
||||||
if (!progressBar || !progressWrap) continue;
|
|
||||||
|
|
||||||
function collectFileNames() {
|
|
||||||
const names = [];
|
|
||||||
// Check raw <input type="file"> elements (non-FilePond)
|
|
||||||
const inputs = form.querySelectorAll('input[type="file"]');
|
|
||||||
for (const fi of inputs) {
|
|
||||||
if (fi.files) {
|
|
||||||
for (const f of fi.files) {
|
|
||||||
if (f.name) names.push(f.name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Read processed file names from FilePond instances (async mode)
|
|
||||||
if (typeof FilePond !== "undefined") {
|
|
||||||
const pondInputs = form.querySelectorAll(".tfe-file-picker");
|
|
||||||
for (const pi of pondInputs) {
|
|
||||||
const pond = FilePond.find(pi);
|
|
||||||
if (pond) {
|
|
||||||
const pondFiles = pond.getFiles();
|
|
||||||
for (const pf of pondFiles) {
|
|
||||||
// Only count successfully uploaded files (have serverId)
|
|
||||||
if (pf.serverId) {
|
|
||||||
const name = pf.filename || pf.file?.name || pf.serverId;
|
|
||||||
if (name) names.push(name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return names;
|
|
||||||
}
|
|
||||||
|
|
||||||
form.addEventListener("submit", (e) => {
|
|
||||||
// ── Guard: block submit if any FilePond item is still uploading ──
|
|
||||||
if (typeof FilePond !== "undefined") {
|
|
||||||
let stillUploading = false;
|
|
||||||
const pondInputs = form.querySelectorAll(".tfe-file-picker");
|
|
||||||
for (const pi of pondInputs) {
|
|
||||||
const pond = FilePond.find(pi);
|
|
||||||
if (pond) {
|
|
||||||
const pondFiles = pond.getFiles();
|
|
||||||
for (const pf of pondFiles) {
|
|
||||||
if (
|
|
||||||
pf.status === FilePond.FileStatus.PROCESSING ||
|
|
||||||
pf.status === FilePond.FileStatus.IDLE
|
|
||||||
) {
|
|
||||||
stillUploading = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (stillUploading) break;
|
|
||||||
}
|
|
||||||
if (stillUploading) {
|
|
||||||
e.preventDefault();
|
|
||||||
progressLabel.textContent =
|
|
||||||
"Veuillez attendre la fin du téléversement…";
|
|
||||||
progressWrap.style.display = "";
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const fileNames = collectFileNames();
|
|
||||||
if (!fileNames.length) return;
|
|
||||||
|
|
||||||
e.preventDefault();
|
|
||||||
|
|
||||||
const token = tokenInput ? tokenInput.value : "";
|
|
||||||
|
|
||||||
progressWrap.style.display = "";
|
|
||||||
progressBar.value = 0;
|
|
||||||
progressBar.removeAttribute("data-complete");
|
|
||||||
progressLabel.textContent = "Téléversement en cours…";
|
|
||||||
progressFile.textContent =
|
|
||||||
fileNames.length === 1 ? fileNames[0] : `${fileNames.length} fichiers`;
|
|
||||||
if (submitBtn) submitBtn.disabled = true;
|
|
||||||
|
|
||||||
const fd = new FormData(form);
|
|
||||||
const xhr = new XMLHttpRequest();
|
|
||||||
|
|
||||||
let _uploadDone = false;
|
|
||||||
let lastUploadPct = 0;
|
|
||||||
let pollingTimer = null;
|
|
||||||
|
|
||||||
/** Poll server-side progress */
|
|
||||||
function startPolling() {
|
|
||||||
if (pollingTimer || !token) return;
|
|
||||||
progressLabel.textContent = "Traitement en cours…";
|
|
||||||
pollingTimer = setInterval(() => {
|
|
||||||
fetch(
|
|
||||||
"/admin/actions/upload-progress.php?token=" +
|
|
||||||
encodeURIComponent(token),
|
|
||||||
)
|
|
||||||
.then((r) => r.json())
|
|
||||||
.then((data) => {
|
|
||||||
if (data?.stage && data.stage !== "upload") {
|
|
||||||
const pct = Math.min(
|
|
||||||
PROCESSING_MAX,
|
|
||||||
Math.max(UPLOAD_CAP, data.pct || UPLOAD_CAP),
|
|
||||||
);
|
|
||||||
progressBar.value = pct;
|
|
||||||
if (data.file) {
|
|
||||||
progressFile.textContent = data.file;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.catch(() => {
|
|
||||||
/* ignore poll errors */
|
|
||||||
});
|
|
||||||
}, POLL_INTERVAL);
|
|
||||||
}
|
|
||||||
|
|
||||||
function stopPolling() {
|
|
||||||
if (pollingTimer) {
|
|
||||||
clearInterval(pollingTimer);
|
|
||||||
pollingTimer = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function finishSuccess() {
|
|
||||||
stopPolling();
|
|
||||||
progressBar.value = 100;
|
|
||||||
progressBar.setAttribute("data-complete", "");
|
|
||||||
progressLabel.textContent = "Téléversé avec succès";
|
|
||||||
progressFile.textContent = "";
|
|
||||||
}
|
|
||||||
|
|
||||||
// ── Upload phase (0% → UPLOAD_CAP) ──
|
|
||||||
xhr.upload.addEventListener("progress", (evt) => {
|
|
||||||
if (evt.lengthComputable) {
|
|
||||||
const rawPct = Math.round((evt.loaded / evt.total) * 100);
|
|
||||||
const scaled = Math.round((rawPct / 100) * UPLOAD_CAP);
|
|
||||||
if (scaled > lastUploadPct) {
|
|
||||||
lastUploadPct = scaled;
|
|
||||||
progressBar.value = scaled;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
xhr.upload.addEventListener("loadend", () => {
|
|
||||||
_uploadDone = true;
|
|
||||||
progressBar.value = UPLOAD_CAP;
|
|
||||||
startPolling();
|
|
||||||
});
|
|
||||||
|
|
||||||
// ── Response handling ──
|
|
||||||
xhr.addEventListener("readystatechange", () => {
|
|
||||||
if (xhr.readyState !== XMLHttpRequest.DONE) return;
|
|
||||||
|
|
||||||
stopPolling();
|
|
||||||
|
|
||||||
if (xhr.status >= 200 && xhr.status < 300) {
|
|
||||||
finishSuccess();
|
|
||||||
|
|
||||||
setTimeout(() => {
|
|
||||||
const finalUrl = xhr.responseURL || "";
|
|
||||||
if (finalUrl && finalUrl !== form.action) {
|
|
||||||
window.location.href = finalUrl;
|
|
||||||
} else {
|
|
||||||
document.open();
|
|
||||||
document.write(xhr.responseText);
|
|
||||||
document.close();
|
|
||||||
}
|
|
||||||
}, SUCCESS_DELAY);
|
|
||||||
} else {
|
|
||||||
progressLabel.textContent = "Erreur";
|
|
||||||
progressFile.textContent = "Échec du téléversement";
|
|
||||||
document.open();
|
|
||||||
document.write(xhr.responseText);
|
|
||||||
document.close();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
xhr.addEventListener("error", () => {
|
|
||||||
stopPolling();
|
|
||||||
progressLabel.textContent = "Erreur réseau";
|
|
||||||
progressFile.textContent = "";
|
|
||||||
if (submitBtn) submitBtn.disabled = false;
|
|
||||||
});
|
|
||||||
|
|
||||||
xhr.addEventListener("abort", () => {
|
|
||||||
stopPolling();
|
|
||||||
progressWrap.style.display = "none";
|
|
||||||
if (submitBtn) submitBtn.disabled = false;
|
|
||||||
});
|
|
||||||
|
|
||||||
xhr.open("POST", form.action, true);
|
|
||||||
xhr.send(fd);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
})();
|
|
||||||
@@ -58,7 +58,7 @@ $pageTitle = 'Merci — TFE enregistré';
|
|||||||
<link rel="icon" type="image/png" sizes="16x16" href="/assets/favicon/favicon-16x16.png">
|
<link rel="icon" type="image/png" sizes="16x16" href="/assets/favicon/favicon-16x16.png">
|
||||||
<link rel="shortcut icon" href="/assets/favicon/favicon.ico">
|
<link rel="shortcut icon" href="/assets/favicon/favicon.ico">
|
||||||
<link rel="stylesheet" href="<?= App::assetV('/assets/css/style.css') ?>">
|
<link rel="stylesheet" href="<?= App::assetV('/assets/css/style.css') ?>">
|
||||||
<link rel="stylesheet" href="<?= App::assetV('/assets/css/form.css') ?>">
|
<link rel="stylesheet" href="<?= App::assetV('/assets/css/form-base.css') ?>">
|
||||||
</head>
|
</head>
|
||||||
<body class="student-body">
|
<body class="student-body">
|
||||||
<main id="main-content" class="partage-recap">
|
<main id="main-content" class="partage-recap">
|
||||||
|
|||||||
@@ -71,7 +71,7 @@ $pageTitle = 'Corriger l\'adresse e-mail';
|
|||||||
<link rel="icon" type="image/png" sizes="16x16" href="/assets/favicon/favicon-16x16.png">
|
<link rel="icon" type="image/png" sizes="16x16" href="/assets/favicon/favicon-16x16.png">
|
||||||
<link rel="shortcut icon" href="/assets/favicon/favicon.ico">
|
<link rel="shortcut icon" href="/assets/favicon/favicon.ico">
|
||||||
<link rel="stylesheet" href="<?= App::assetV('/assets/css/style.css') ?>">
|
<link rel="stylesheet" href="<?= App::assetV('/assets/css/style.css') ?>">
|
||||||
<link rel="stylesheet" href="<?= App::assetV('/assets/css/form.css') ?>">
|
<link rel="stylesheet" href="<?= App::assetV('/assets/css/form-base.css') ?>">
|
||||||
</head>
|
</head>
|
||||||
<body class="student-body">
|
<body class="student-body">
|
||||||
<main id="main-content" class="partage-retry-email">
|
<main id="main-content" class="partage-retry-email">
|
||||||
|
|||||||
@@ -163,7 +163,7 @@ class ThesisEditController
|
|||||||
* the transaction is still open, but this method rolls
|
* the transaction is still open, but this method rolls
|
||||||
* back internally before re-throwing).
|
* back internally before re-throwing).
|
||||||
*/
|
*/
|
||||||
public function save(int $thesisId, array $post, array $files, ?string $progressToken = null): void
|
public function save(int $thesisId, array $post, array $files): void
|
||||||
{
|
{
|
||||||
if ($thesisId <= 0) {
|
if ($thesisId <= 0) {
|
||||||
throw new InvalidArgumentException('ID de TFE invalide.');
|
throw new InvalidArgumentException('ID de TFE invalide.');
|
||||||
|
|||||||
@@ -455,30 +455,4 @@ class PeerTubeService
|
|||||||
throw new \RuntimeException('Erreur réseau PeerTube : ' . $e->getMessage(), 0, $e);
|
throw new \RuntimeException('Erreur réseau PeerTube : ' . $e->getMessage(), 0, $e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// -------------------------------------------------------------------------
|
|
||||||
// Progress reporting (for upload-progress.js polling)
|
|
||||||
// -------------------------------------------------------------------------
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Write upload progress to a temp file polled by the progress endpoint.
|
|
||||||
*/
|
|
||||||
public static function writeProgress(string $token, string $stage, int $pct, string $file = ''): void
|
|
||||||
{
|
|
||||||
$progressFile = sys_get_temp_dir() . '/xamxam_upload_' . $token . '.json';
|
|
||||||
file_put_contents($progressFile, json_encode([
|
|
||||||
'stage' => $stage,
|
|
||||||
'pct' => $pct,
|
|
||||||
'file' => $file,
|
|
||||||
]), LOCK_EX);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Remove the progress file for a given token.
|
|
||||||
*/
|
|
||||||
public static function clearProgress(string $token): void
|
|
||||||
{
|
|
||||||
$progressFile = sys_get_temp_dir() . '/xamxam_upload_' . $token . '.json';
|
|
||||||
@unlink($progressFile);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
// Admin: append suffix to title and prepend admin.css
|
// Admin: append suffix to title and prepend admin.css
|
||||||
if (!empty($isAdmin)) {
|
if (!empty($isAdmin)) {
|
||||||
$pageTitle = isset($pageTitle) ? $pageTitle . ' – Admin' : 'Admin';
|
$pageTitle = isset($pageTitle) ? $pageTitle . ' – Admin' : 'Admin';
|
||||||
$extraCss = array_merge(['/assets/css/admin.css'], $extraCss ?? []);
|
$extraCss = array_merge(['/assets/css/admin.css'], $extraCssAdmin ?? [], $extraCss ?? []);
|
||||||
}
|
}
|
||||||
?>
|
?>
|
||||||
<title><?= htmlspecialchars($pageTitle ?? 'XAMXAM') ?></title>
|
<title><?= htmlspecialchars($pageTitle ?? 'XAMXAM') ?></title>
|
||||||
|
|||||||
Reference in New Issue
Block a user