feat: stockage articles en fichiers Markdown, SSO intégré, URLs propres

This commit is contained in:
Cedric Abonnel
2026-05-08 22:36:04 +02:00
parent aa9c04d154
commit fd3fced0d8
22 changed files with 863 additions and 352 deletions
+85 -28
View File
@@ -1,64 +1,121 @@
<?php
ob_start();
// Valeur par défaut pour le champ datetime-local
$dateValue = $published_at ?? date('Y-m-d\TH:i');
$dateValue = isset($published_at)
? (str_contains($published_at, ' ')
? date('Y-m-d\TH:i', strtotime($published_at))
: $published_at)
: date('Y-m-d\TH:i');
?>
<h1 class="mb-4"><?= $action === 'edit' ? 'Modifier le post' : 'Créer un nouveau post' ?></h1>
<h1 class="mb-4"><?= $action === 'edit' ? 'Modifier l\'article' : 'Nouvel article' ?></h1>
<?php if (!empty($errors)): ?>
<div class="alert alert-danger">
<ul class="mb-0">
<?php foreach ($errors as $error): ?>
<li><?= htmlspecialchars($error) ?></li>
<?php endforeach; ?>
</ul>
</div>
<div class="alert alert-danger">
<ul class="mb-0">
<?php foreach ($errors as $error): ?>
<li><?= htmlspecialchars($error) ?></li>
<?php endforeach; ?>
</ul>
</div>
<?php endif; ?>
<form method="POST" action="<?= htmlspecialchars($formAction) ?>" enctype="multipart/form-data">
<div class="mb-3">
<label for="title" class="form-label">Titre</label>
<input type="text" class="form-control" id="title" name="title" required value="<?= htmlspecialchars($title) ?>">
<input type="text" class="form-control" id="title" name="title" required
value="<?= htmlspecialchars($title ?? '') ?>"
oninput="autoSlug(this.value)">
</div>
<div class="mb-3">
<label for="slug" class="form-label">
Slug <small class="text-muted">(URL : /post/<span id="slug-preview"><?= htmlspecialchars($postSlug ?? '') ?></span>)</small>
</label>
<input type="text" class="form-control form-control-sm font-monospace" id="slug" name="slug"
value="<?= htmlspecialchars($postSlug ?? '') ?>"
pattern="[a-z0-9][a-z0-9-]*"
placeholder="généré automatiquement depuis le titre">
</div>
<div class="mb-2">
<small class="text-muted">
Écris en <strong>Markdown</strong> <a href="https://www.markdownguide.org/cheat-sheet/" target="_blank">guide rapide</a>
Écris en <strong>Markdown</strong> — les fichiers uploadés sont référençables dans le contenu :
<code>![alt](nom-du-fichier.jpg)</code>
</small>
</div>
<div class="mb-3">
<label for="content" class="form-label">Contenu</label>
<textarea class="form-control" id="content" name="content" rows="6"><?= htmlspecialchars($content) ?></textarea>
<textarea class="form-control font-monospace" id="content" name="content" rows="12"><?= htmlspecialchars($content ?? '') ?></textarea>
</div>
<div class="row mb-3">
<div class="col-md-6">
<label for="published_at" class="form-label">Date de publication</label>
<input type="datetime-local" class="form-control" id="published_at" name="published_at" value="<?= $dateValue ?>">
</div>
<div class="col-md-6 d-flex align-items-end">
<div class="form-check">
<input class="form-check-input" type="checkbox" id="published" name="published" <?= ($published ?? false) ? 'checked' : '' ?>>
<label class="form-check-label" for="published">Publié</label>
<div class="row mb-3">
<div class="col-md-6">
<label for="published_at" class="form-label">Date de publication</label>
<input type="datetime-local" class="form-control" id="published_at" name="published_at" value="<?= $dateValue ?>">
</div>
<div class="col-md-6 d-flex align-items-end">
<div class="form-check">
<input class="form-check-input" type="checkbox" id="published" name="published"
<?= ($published ?? false) ? 'checked' : '' ?>>
<label class="form-check-label" for="published">Publié</label>
</div>
</div>
</div>
</div>
<div class="mb-3">
<label for="files" class="form-label">Fichiers</label>
<label for="files" class="form-label">Ajouter des fichiers</label>
<input type="file" class="form-control" id="files" name="files[]" multiple>
<div class="form-text">Images, vidéos, PDF… — intègre-les dans le contenu ou laisse-les en pièces jointes.</div>
</div>
<?php if ($action === 'edit' && !empty($existingFiles)): ?>
<div class="mb-3">
<p class="form-label">Fichiers existants</p>
<ul class="list-unstyled">
<?php foreach ($existingFiles as $f): ?>
<li>
<code><?= htmlspecialchars($f['name']) ?></code>
<small class="text-muted ms-2"><?= htmlspecialchars(number_format($f['size'] / 1024, 1)) ?> Ko</small>
</li>
<?php endforeach; ?>
</ul>
</div>
<?php endif; ?>
<button type="submit" class="btn btn-success">Enregistrer</button>
<a href="route.php" class="btn btn-secondary">Annuler</a>
<a href="/" class="btn btn-secondary">Annuler</a>
</form>
<script>
function slugify(s) {
const map = {'à':'a','â':'a','ä':'a','é':'e','è':'e','ê':'e','ë':'e','î':'i','ï':'i','ô':'o','ö':'o','ù':'u','û':'u','ü':'u','ç':'c','æ':'ae','œ':'oe'};
return s.toLowerCase().replace(/[àâäéèêëîïôöùûüçæœ]/g, c => map[c] || c)
.replace(/[^a-z0-9]+/g, '-').replace(/^-+|-+$/g, '');
}
function autoSlug(title) {
const slugField = document.getElementById('slug');
const preview = document.getElementById('slug-preview');
// N'écrase le slug que s'il est vide ou s'il correspond à la génération automatique
if (slugField._auto !== false) {
const generated = slugify(title);
slugField.value = generated;
preview.textContent = generated;
}
}
document.getElementById('slug').addEventListener('input', function() {
this._auto = (this.value === '');
document.getElementById('slug-preview').textContent = this.value;
});
// En mode édition le champ est pré-rempli : désactive l'auto-génération
(function() {
const s = document.getElementById('slug');
if (s.value !== '') s._auto = false;
})();
</script>
<?php
$content = ob_get_clean();
$title = $action === 'edit' ? 'Modifier le post' : 'Nouveau post';
$title = $action === 'edit' ? 'Modifier l\'article' : 'Nouvel article';
include __DIR__ . '/layout.php';