feat : versionnage semver, migrations contenu, bandeau mise à jour admin
- CHANGELOG.md : structure semver (1.0.0 / 1.1.0 / 1.2.0) remplace le journal non versionné - public/version.txt : généré à chaque push depuis la première entrée CHANGELOG - scripts/push.sh : extrait la version CHANGELOG avant git add - src/UpdateChecker.php : compare version déployée vs version Gitea (raw file), cache 1 h - templates/layout.php : bandeau alerte admin (nouvelle version / migrations en attente) - templates/admin.php : dashboard moteur Folio (version déployée / disponible) - scripts/migrate_content.php + migration_001 : ajout # titre dans les articles existants - templates/maintenance.php : page HTTP 503 pendant une migration - src/helpers.php : extractMarkdownTitle(), normalisation \r\n dans lineDiff() - templates/wizard/step1.php : suppression champ titre, plan TOC dynamique - public/assets/js/wizard.js : scope titleEl, scrollToCursor, buildToc, handlers externalisés Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
+75
-40
@@ -26,6 +26,17 @@ require_once BASE_PATH . '/src/ArticleManager.php';
|
||||
|
||||
$articles = new ArticleManager(BASE_PATH . '/data');
|
||||
|
||||
// ─── Mode maintenance ──────────────────────────────────────────────────────
|
||||
if (file_exists(BASE_PATH . '/data/.maintenance')) {
|
||||
http_response_code(503);
|
||||
header('Retry-After: 60');
|
||||
include BASE_PATH . '/templates/maintenance.php';
|
||||
exit;
|
||||
}
|
||||
|
||||
require_once BASE_PATH . '/src/UpdateChecker.php';
|
||||
$_updateChecker = new UpdateChecker(BASE_PATH . '/data', BASE_PATH);
|
||||
|
||||
$action = $_GET['action'] ?? 'list';
|
||||
$uuid = $_GET['uuid'] ?? '';
|
||||
$slug = $_GET['slug'] ?? '';
|
||||
@@ -512,28 +523,24 @@ switch ($action) {
|
||||
|
||||
case 1:
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$title = trim($_POST['title'] ?? '');
|
||||
$content = $_POST['content'] ?? '';
|
||||
$content = str_replace("\r\n", "\n", $_POST['content'] ?? '');
|
||||
$title = extractMarkdownTitle($content) ?: ($draft['title'] ?? 'Sans titre');
|
||||
$postSlug = trim($_POST['slug'] ?? '');
|
||||
if ($title === '') {
|
||||
$errors[] = 'Le titre est obligatoire.';
|
||||
} else {
|
||||
if ($draft === null) {
|
||||
$uuid = $articles->create($title, $content, false, $postSlug, date('Y-m-d H:i:s'), currentUserEmail() ?? '', '', '', '', '', []);
|
||||
foreach ($_FILES['files']['tmp_name'] ?? [] as $_fi => $_tmpName) {
|
||||
if ($_FILES['files']['error'][$_fi] === UPLOAD_ERR_OK) {
|
||||
$articles->addFile($uuid, ['name' => $_FILES['files']['name'][$_fi], 'tmp_name' => $_tmpName, 'error' => UPLOAD_ERR_OK]);
|
||||
}
|
||||
if ($draft === null) {
|
||||
$uuid = $articles->create($title, $content, false, $postSlug, date('Y-m-d H:i:s'), currentUserEmail() ?? '', '', '', '', '', []);
|
||||
foreach ($_FILES['files']['tmp_name'] ?? [] as $_fi => $_tmpName) {
|
||||
if ($_FILES['files']['error'][$_fi] === UPLOAD_ERR_OK) {
|
||||
$articles->addFile($uuid, ['name' => $_FILES['files']['name'][$_fi], 'tmp_name' => $_tmpName, 'error' => UPLOAD_ERR_OK]);
|
||||
}
|
||||
$_SESSION['wizard_create'] = $uuid;
|
||||
} else {
|
||||
$articles->autosave($uuid, $title, $content, $postSlug);
|
||||
}
|
||||
header('Location: /new/' . rawurlencode($uuid) . '/2');
|
||||
exit;
|
||||
$_SESSION['wizard_create'] = $uuid;
|
||||
} else {
|
||||
$articles->autosave($uuid, $title, $content, $postSlug);
|
||||
}
|
||||
header('Location: /new/' . rawurlencode($uuid) . '/2');
|
||||
exit;
|
||||
}
|
||||
$title = $draft['title'] ?? ($_POST['title'] ?? '');
|
||||
$title = $draft['title'] ?? '';
|
||||
$content = $draft['content'] ?? '';
|
||||
$postSlug = $draft['slug'] ?? '';
|
||||
$existingFiles = $uuid !== '' ? $articles->getFiles($uuid) : [];
|
||||
@@ -865,15 +872,11 @@ switch ($action) {
|
||||
|
||||
case 1:
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$title = trim($_POST['title'] ?? '');
|
||||
$content = $_POST['content'] ?? '';
|
||||
if ($title === '') {
|
||||
$errors[] = 'Le titre est obligatoire.';
|
||||
} else {
|
||||
$articles->saveDraftOverlay($uuid, ['title' => $title, 'slug' => trim($_POST['slug'] ?? $draft['slug'])], $content);
|
||||
header('Location: /edit/' . rawurlencode($uuid) . '/2');
|
||||
exit;
|
||||
}
|
||||
$content = str_replace("\r\n", "\n", $_POST['content'] ?? '');
|
||||
$title = extractMarkdownTitle($content) ?: ($draft['title'] ?? 'Sans titre');
|
||||
$articles->saveDraftOverlay($uuid, ['title' => $title, 'slug' => trim($_POST['slug'] ?? $draft['slug'] ?? '')], $content);
|
||||
header('Location: /edit/' . rawurlencode($uuid) . '/2');
|
||||
exit;
|
||||
}
|
||||
$title = $draft['title'];
|
||||
$content = $draft['content'];
|
||||
@@ -1465,15 +1468,13 @@ switch ($action) {
|
||||
echo json_encode(['ok' => false]);
|
||||
exit;
|
||||
}
|
||||
$asTitle = trim($_POST['title'] ?? '');
|
||||
$asContent = $_POST['content'] ?? '';
|
||||
$asContent = str_replace("\r\n", "\n", $_POST['content'] ?? '');
|
||||
$asSlug = trim($_POST['slug'] ?? '');
|
||||
if ($asTitle === '') {
|
||||
echo json_encode(['ok' => false]);
|
||||
exit;
|
||||
}
|
||||
$_asCurrent = $articles->getByUuid($uuid);
|
||||
$asTitle = extractMarkdownTitle($asContent) ?: ($_asCurrent['title'] ?? 'Sans titre');
|
||||
$ok = $articles->autosave($uuid, $asTitle, $asContent, $asSlug);
|
||||
echo json_encode(['ok' => $ok, 'time' => date('H:i:s')]);
|
||||
$_asSlugFinal = $ok ? ($articles->getByUuid($uuid)['slug'] ?? '') : '';
|
||||
echo json_encode(['ok' => $ok, 'time' => date('H:i:s'), 'title' => $asTitle, 'slug' => $_asSlugFinal]);
|
||||
exit;
|
||||
|
||||
case 'autosave_draft':
|
||||
@@ -1488,14 +1489,12 @@ switch ($action) {
|
||||
echo json_encode(['ok' => false]);
|
||||
exit;
|
||||
}
|
||||
$_adTitle = trim($_POST['title'] ?? '');
|
||||
$_adContent = $_POST['content'] ?? null;
|
||||
if ($_adTitle === '') {
|
||||
echo json_encode(['ok' => false]);
|
||||
exit;
|
||||
}
|
||||
$_adContent = isset($_POST['content']) ? str_replace("\r\n", "\n", $_POST['content']) : null;
|
||||
$_adTitle = $_adContent !== null
|
||||
? (extractMarkdownTitle($_adContent) ?: ($_adArticle['title'] ?? 'Sans titre'))
|
||||
: ($_adArticle['title'] ?? 'Sans titre');
|
||||
$articles->saveDraftOverlay($uuid, ['title' => $_adTitle], $_adContent);
|
||||
echo json_encode(['ok' => true, 'time' => date('H:i:s')]);
|
||||
echo json_encode(['ok' => true, 'time' => date('H:i:s'), 'title' => $_adTitle]);
|
||||
exit;
|
||||
|
||||
case 'edit_discard_draft':
|
||||
@@ -2733,6 +2732,42 @@ switch ($action) {
|
||||
header('Location: /admin/users');
|
||||
exit;
|
||||
|
||||
case 'run_content_migrations':
|
||||
requireAuth();
|
||||
if (!isAdmin() || $_SERVER['REQUEST_METHOD'] !== 'POST') {
|
||||
http_response_code(403);
|
||||
exit;
|
||||
}
|
||||
$_cmDataDir = BASE_PATH . '/data';
|
||||
$_cmTrack = $_cmDataDir . '/.content_migrations.json';
|
||||
$_cmFlag = $_cmDataDir . '/.maintenance';
|
||||
$_cmApplied = file_exists($_cmTrack) ? (json_decode((string) file_get_contents($_cmTrack), true) ?? []) : [];
|
||||
$_cmFiles = glob(BASE_PATH . '/scripts/content/migration_*.php') ?: [];
|
||||
sort($_cmFiles);
|
||||
$_cmPending = array_values(array_filter($_cmFiles, fn ($f) => !isset($_cmApplied[basename($f)])));
|
||||
if (empty($_cmPending)) {
|
||||
header('Location: /admin?tab=dashboard¬ice=no_migrations');
|
||||
exit;
|
||||
}
|
||||
file_put_contents($_cmFlag, date('Y-m-d H:i:s'));
|
||||
$_cmErrors = 0;
|
||||
$dataDir = $_cmDataDir;
|
||||
foreach ($_cmPending as $_cmFile) {
|
||||
try {
|
||||
require $_cmFile;
|
||||
$_cmApplied[basename($_cmFile)] = date('Y-m-d H:i:s');
|
||||
file_put_contents($_cmTrack, json_encode($_cmApplied, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE) . "\n");
|
||||
} catch (Throwable $_cmEx) {
|
||||
$_cmErrors++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (file_exists($_cmFlag)) {
|
||||
unlink($_cmFlag);
|
||||
}
|
||||
header('Location: /admin?tab=dashboard¬ice=' . ($_cmErrors ? 'migration_error' : 'migrated'));
|
||||
exit;
|
||||
|
||||
case 'admin_save_site':
|
||||
requireAuth();
|
||||
if (!isAdmin() || $_SERVER['REQUEST_METHOD'] !== 'POST') {
|
||||
|
||||
Reference in New Issue
Block a user