2025-02-14 18:15:23 +01:00
|
|
|
<?php
|
|
|
|
require_once '../includes/config.php';
|
|
|
|
require_once '../includes/auth.php';
|
|
|
|
require_once '../includes/stories.php';
|
2025-02-14 18:50:05 +01:00
|
|
|
require_once 'upload-handler.php';
|
2025-02-14 18:15:23 +01:00
|
|
|
|
|
|
|
if (!Auth::check()) {
|
|
|
|
header('Location: login.php');
|
|
|
|
exit;
|
|
|
|
}
|
|
|
|
|
|
|
|
$story = null;
|
|
|
|
$error = '';
|
|
|
|
$success = '';
|
|
|
|
|
|
|
|
// Chargement du roman existant si ID fourni
|
|
|
|
if (isset($_GET['id'])) {
|
|
|
|
$story = Stories::get($_GET['id']);
|
|
|
|
if (!$story) {
|
|
|
|
header('Location: index.php');
|
|
|
|
exit;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Traitement de la sauvegarde
|
|
|
|
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
|
|
|
try {
|
|
|
|
$storyData = [
|
|
|
|
'id' => $_POST['id'] ?? generateSlug($_POST['title']),
|
|
|
|
'title' => $_POST['title'],
|
|
|
|
'description' => $_POST['description'],
|
2025-02-14 18:50:05 +01:00
|
|
|
'cover' => $story['cover'] ?? '',
|
2025-02-14 18:15:23 +01:00
|
|
|
'created' => $story['created'] ?? date('Y-m-d'),
|
|
|
|
'updated' => date('Y-m-d'),
|
|
|
|
'chapters' => $story['chapters'] ?? []
|
|
|
|
];
|
|
|
|
|
|
|
|
// Gestion de l'upload de couverture
|
2025-02-14 18:50:05 +01:00
|
|
|
if (isset($_FILES['cover']) && $_FILES['cover']['error'] !== UPLOAD_ERR_NO_FILE) {
|
|
|
|
$uploadHandler = new CoverUploadHandler();
|
|
|
|
$storyData['cover'] = $uploadHandler->handleUpload($_FILES['cover'], $storyData['id']);
|
2025-02-14 18:15:23 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
Stories::save($storyData);
|
|
|
|
$success = 'Roman sauvegardé avec succès';
|
|
|
|
$story = $storyData;
|
|
|
|
} catch (Exception $e) {
|
|
|
|
$error = 'Erreur lors de la sauvegarde : ' . $e->getMessage();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function generateSlug($title) {
|
|
|
|
$slug = strtolower($title);
|
|
|
|
$slug = preg_replace('/[^a-z0-9-]/', '-', $slug);
|
|
|
|
$slug = preg_replace('/-+/', '-', $slug);
|
|
|
|
return trim($slug, '-');
|
|
|
|
}
|
|
|
|
?>
|
|
|
|
<!DOCTYPE html>
|
|
|
|
<html lang="fr">
|
|
|
|
<head>
|
|
|
|
<meta charset="UTF-8">
|
|
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
|
|
<title><?= $story ? 'Modifier' : 'Nouveau' ?> roman - Administration</title>
|
2025-02-15 22:04:35 +01:00
|
|
|
<?php if (file_exists(__DIR__ . '/../assets/images/site/favicon.png')): ?>
|
|
|
|
<link rel="icon" type="image/png" href="../assets/images/site/favicon.png">
|
|
|
|
<?php endif; ?>
|
2025-02-14 19:01:13 +01:00
|
|
|
<link rel="stylesheet" href="../assets/css/main.css">
|
2025-02-14 18:15:23 +01:00
|
|
|
<link href="https://cdn.quilljs.com/1.3.6/quill.snow.css" rel="stylesheet">
|
|
|
|
</head>
|
|
|
|
<body>
|
|
|
|
<nav class="admin-nav">
|
2025-02-15 19:13:31 +01:00
|
|
|
<div class="nav-brand">
|
|
|
|
<?php
|
|
|
|
$config = Config::load();
|
|
|
|
if (!empty($config['site']['logo'])): ?>
|
|
|
|
<img src="<?= htmlspecialchars('../' . $config['site']['logo']) ?>"
|
|
|
|
alt="<?= htmlspecialchars($config['site']['name']) ?>">
|
|
|
|
<?php endif; ?>
|
|
|
|
<span>Administration</span>
|
|
|
|
</div>
|
2025-02-14 18:15:23 +01:00
|
|
|
<div class="nav-menu">
|
|
|
|
<a href="index.php" class="button">Retour</a>
|
|
|
|
</div>
|
|
|
|
</nav>
|
|
|
|
|
|
|
|
<main class="admin-main">
|
2025-02-14 19:57:45 +01:00
|
|
|
<h1><?= $story ? 'Modifier le' : 'Nouveau' ?> roman</h1>
|
2025-02-14 18:15:23 +01:00
|
|
|
|
|
|
|
<?php if ($error): ?>
|
|
|
|
<div class="error-message"><?= htmlspecialchars($error) ?></div>
|
|
|
|
<?php endif; ?>
|
|
|
|
|
|
|
|
<?php if ($success): ?>
|
|
|
|
<div class="success-message"><?= htmlspecialchars($success) ?></div>
|
|
|
|
<?php endif; ?>
|
|
|
|
|
|
|
|
<form method="POST" enctype="multipart/form-data" class="story-form">
|
|
|
|
<?php if ($story): ?>
|
|
|
|
<input type="hidden" name="id" value="<?= htmlspecialchars($story['id']) ?>">
|
|
|
|
<?php endif; ?>
|
|
|
|
|
|
|
|
<div class="form-group">
|
|
|
|
<label for="title">Titre</label>
|
|
|
|
<input type="text" id="title" name="title" required
|
|
|
|
value="<?= htmlspecialchars($story['title'] ?? '') ?>">
|
|
|
|
</div>
|
|
|
|
|
|
|
|
<div class="form-group">
|
|
|
|
<label for="description">Description</label>
|
2025-02-15 19:33:46 +01:00
|
|
|
<input type="hidden" id="description" name="description" required>
|
|
|
|
<div id="descriptionEditor"></div>
|
2025-02-14 18:15:23 +01:00
|
|
|
</div>
|
|
|
|
|
|
|
|
<div class="form-group">
|
|
|
|
<label for="cover">Image de couverture</label>
|
|
|
|
<?php if (isset($story['cover'])): ?>
|
|
|
|
<img src="<?= htmlspecialchars('../' . $story['cover']) ?>"
|
|
|
|
alt="Couverture actuelle" class="current-cover">
|
|
|
|
<?php endif; ?>
|
|
|
|
<input type="file" id="cover" name="cover" accept="image/*">
|
|
|
|
</div>
|
|
|
|
|
|
|
|
<button type="submit" class="button">Enregistrer</button>
|
|
|
|
</form>
|
|
|
|
|
|
|
|
<?php if ($story): ?>
|
|
|
|
<section class="chapters-section">
|
|
|
|
<h2>Chapitres</h2>
|
|
|
|
<button type="button" id="addChapter" class="button">Ajouter un chapitre</button>
|
|
|
|
|
|
|
|
<div id="chaptersList" class="chapters-list">
|
|
|
|
<?php foreach ($story['chapters'] ?? [] as $index => $chapter): ?>
|
2025-02-14 19:57:45 +01:00
|
|
|
<div class="chapter-item" data-id="<?= htmlspecialchars($chapter['id']) ?>">
|
|
|
|
<span class="chapter-number"><?= $index + 1 ?></span>
|
|
|
|
<h3 class="chapter-title"><?= htmlspecialchars($chapter['title']) ?></h3>
|
|
|
|
<div class="chapter-actions">
|
|
|
|
<button type="button" class="button edit-chapter">Éditer</button>
|
2025-02-14 18:15:23 +01:00
|
|
|
<button type="button" class="button delete-chapter">Supprimer</button>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
<?php endforeach; ?>
|
|
|
|
</div>
|
|
|
|
</section>
|
|
|
|
|
|
|
|
<div id="chapterEditor" class="modal" style="display: none;">
|
|
|
|
<div class="modal-content">
|
|
|
|
<h2>Éditer le chapitre</h2>
|
|
|
|
<input type="text" id="chapterTitle" placeholder="Titre du chapitre">
|
|
|
|
<div id="editor"></div>
|
|
|
|
<div class="modal-actions">
|
|
|
|
<button type="button" class="button" id="saveChapter">Enregistrer</button>
|
|
|
|
<button type="button" class="button" id="cancelEdit">Annuler</button>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
<?php endif; ?>
|
|
|
|
</main>
|
|
|
|
|
|
|
|
<script src="https://cdn.quilljs.com/1.3.6/quill.min.js"></script>
|
|
|
|
<script src="https://cdn.jsdelivr.net/npm/sortablejs@1.15.0/Sortable.min.js"></script>
|
|
|
|
<script src="../assets/js/story-edit.js"></script>
|
2025-02-15 19:33:46 +01:00
|
|
|
<script>
|
|
|
|
document.addEventListener('DOMContentLoaded', function() {
|
2025-02-16 00:13:37 +01:00
|
|
|
// Configuration de l'éditeur de description
|
|
|
|
// Récupérer le storyId depuis l'input caché ou l'URL
|
|
|
|
const urlParams = new URLSearchParams(window.location.search);
|
|
|
|
const currentStoryId = document.querySelector('input[name="id"]')?.value || urlParams.get('id');
|
|
|
|
|
|
|
|
// Configuration des handlers pour les images
|
|
|
|
const imageUploadHandler = (editor) => {
|
|
|
|
const input = document.createElement('input');
|
|
|
|
input.setAttribute('type', 'file');
|
|
|
|
input.setAttribute('accept', 'image/*');
|
|
|
|
input.click();
|
|
|
|
|
|
|
|
input.onchange = async () => {
|
|
|
|
const file = input.files[0];
|
|
|
|
if (file) {
|
|
|
|
const formData = new FormData();
|
|
|
|
formData.append('image', file);
|
|
|
|
formData.append('storyId', currentStoryId);
|
|
|
|
|
|
|
|
try {
|
|
|
|
const response = await fetch('api/upload-image.php', {
|
|
|
|
method: 'POST',
|
|
|
|
body: formData
|
|
|
|
});
|
|
|
|
|
|
|
|
if (!response.ok) throw new Error('Upload failed');
|
|
|
|
|
|
|
|
const result = await response.json();
|
|
|
|
if (result.success) {
|
|
|
|
const range = editor.getSelection(true);
|
|
|
|
editor.insertEmbed(range.index, 'image', result.url);
|
|
|
|
editor.setSelection(range.index + 1);
|
|
|
|
} else {
|
|
|
|
showNotification(result.error || 'Erreur lors de l\'upload', 'error');
|
|
|
|
}
|
|
|
|
} catch (error) {
|
|
|
|
console.error('Error:', error);
|
|
|
|
showNotification('Erreur lors de l\'upload de l\'image', 'error');
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
2025-02-15 19:33:46 +01:00
|
|
|
const descriptionEditor = new Quill('#descriptionEditor', {
|
|
|
|
theme: 'snow',
|
|
|
|
modules: {
|
|
|
|
toolbar: {
|
|
|
|
container: [
|
|
|
|
[{ 'header': [1, 2, 3, false] }],
|
|
|
|
['bold', 'italic', 'underline', 'strike'],
|
|
|
|
[{ 'color': [] }, { 'background': [] }],
|
|
|
|
[{ 'font': [] }],
|
|
|
|
[{ 'align': [] }],
|
|
|
|
['blockquote', 'code-block'],
|
|
|
|
[{ 'list': 'ordered'}, { 'list': 'bullet' }],
|
|
|
|
[{ 'script': 'sub'}, { 'script': 'super' }],
|
|
|
|
[{ 'indent': '-1'}, { 'indent': '+1' }],
|
|
|
|
[{ 'direction': 'rtl' }],
|
|
|
|
['link', 'image', 'video'],
|
|
|
|
['divider'],
|
|
|
|
['clean']
|
|
|
|
],
|
|
|
|
handlers: {
|
|
|
|
image: function() {
|
2025-02-16 00:13:37 +01:00
|
|
|
imageUploadHandler(descriptionEditor);
|
2025-02-15 19:33:46 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
|
|
|
keyboard: {
|
|
|
|
bindings: {
|
|
|
|
tab: false,
|
|
|
|
'indent backwards': false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
|
|
|
placeholder: 'Commencez à écrire la description du roman...'
|
|
|
|
});
|
|
|
|
|
|
|
|
// Initialiser le contenu si on édite un roman existant
|
|
|
|
<?php if (isset($story['description'])): ?>
|
|
|
|
descriptionEditor.root.innerHTML = <?= json_encode($story['description']) ?>;
|
|
|
|
<?php endif; ?>
|
|
|
|
|
|
|
|
// Mettre à jour le champ caché avant la soumission
|
|
|
|
const form = document.querySelector('form');
|
|
|
|
form.addEventListener('submit', function() {
|
|
|
|
const description = document.querySelector('#description');
|
|
|
|
description.value = descriptionEditor.root.innerHTML;
|
|
|
|
});
|
|
|
|
});
|
|
|
|
</script>
|
2025-02-16 13:11:50 +01:00
|
|
|
<link rel="stylesheet" href="../assets/css/dialog.css">
|
|
|
|
<script src="../assets/js/dialog.js"></script>
|
2025-02-14 18:15:23 +01:00
|
|
|
</body>
|
|
|
|
</html>
|