171 lines
5.7 KiB
PHP
171 lines
5.7 KiB
PHP
|
<?php
|
||
|
require_once '../../includes/config.php';
|
||
|
require_once '../../includes/auth.php';
|
||
|
require_once '../../includes/stories.php';
|
||
|
|
||
|
class ChapterCoverHandler {
|
||
|
private $allowedTypes = ['image/jpeg', 'image/png', 'image/gif', 'image/webp'];
|
||
|
private $maxFileSize = 5242880; // 5MB
|
||
|
private $uploadDir;
|
||
|
|
||
|
public function __construct($storyId) {
|
||
|
$this->uploadDir = __DIR__ . '/../../assets/images/chapters/' . $storyId . '/covers/';
|
||
|
$this->ensureUploadDirectory();
|
||
|
}
|
||
|
|
||
|
public function handleUpload($file, $chapterId) {
|
||
|
try {
|
||
|
// Vérifications de base
|
||
|
if ($file['error'] !== UPLOAD_ERR_OK) {
|
||
|
throw new Exception($this->getUploadErrorMessage($file['error']));
|
||
|
}
|
||
|
|
||
|
// Vérification du type MIME
|
||
|
$finfo = new finfo(FILEINFO_MIME_TYPE);
|
||
|
$mimeType = $finfo->file($file['tmp_name']);
|
||
|
if (!in_array($mimeType, $this->allowedTypes)) {
|
||
|
throw new Exception('Type de fichier non autorisé. Types acceptés : JPG, PNG, GIF, WEBP');
|
||
|
}
|
||
|
|
||
|
// Vérification de la taille
|
||
|
if ($file['size'] > $this->maxFileSize) {
|
||
|
throw new Exception('Fichier trop volumineux. Taille maximum : 5MB');
|
||
|
}
|
||
|
|
||
|
// Génération du nom de fichier
|
||
|
$extension = $this->getExtensionFromMimeType($mimeType);
|
||
|
$filename = $chapterId . '-cover.' . $extension;
|
||
|
$targetPath = $this->uploadDir . $filename;
|
||
|
|
||
|
// Suppression de l'ancienne image si elle existe
|
||
|
$this->removeOldCover($chapterId);
|
||
|
|
||
|
// Déplacement du fichier
|
||
|
if (!move_uploaded_file($file['tmp_name'], $targetPath)) {
|
||
|
throw new Exception('Erreur lors du déplacement du fichier uploadé');
|
||
|
}
|
||
|
|
||
|
// Retourner le chemin relatif pour stockage en BDD
|
||
|
return 'assets/images/chapters/' . basename(dirname($this->uploadDir)) . '/covers/' . $filename;
|
||
|
|
||
|
} catch (Exception $e) {
|
||
|
throw $e;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public function removeCover($chapterId) {
|
||
|
foreach (glob($this->uploadDir . $chapterId . '-cover.*') as $file) {
|
||
|
unlink($file);
|
||
|
}
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
private function ensureUploadDirectory() {
|
||
|
if (!file_exists($this->uploadDir)) {
|
||
|
if (!mkdir($this->uploadDir, 0755, true)) {
|
||
|
throw new Exception('Impossible de créer le dossier d\'upload');
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (!is_writable($this->uploadDir)) {
|
||
|
throw new Exception('Le dossier d\'upload n\'est pas accessible en écriture');
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private function removeOldCover($chapterId) {
|
||
|
foreach (glob($this->uploadDir . $chapterId . '-cover.*') as $file) {
|
||
|
unlink($file);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private function getExtensionFromMimeType($mimeType) {
|
||
|
$map = [
|
||
|
'image/jpeg' => 'jpg',
|
||
|
'image/png' => 'png',
|
||
|
'image/gif' => 'gif',
|
||
|
'image/webp' => 'webp'
|
||
|
];
|
||
|
return $map[$mimeType] ?? 'jpg';
|
||
|
}
|
||
|
|
||
|
private function getUploadErrorMessage($error) {
|
||
|
$errors = [
|
||
|
UPLOAD_ERR_INI_SIZE => 'Le fichier dépasse la taille maximale autorisée par PHP',
|
||
|
UPLOAD_ERR_FORM_SIZE => 'Le fichier dépasse la taille maximale autorisée par le formulaire',
|
||
|
UPLOAD_ERR_PARTIAL => 'Le fichier n\'a été que partiellement uploadé',
|
||
|
UPLOAD_ERR_NO_FILE => 'Aucun fichier n\'a été uploadé',
|
||
|
UPLOAD_ERR_NO_TMP_DIR => 'Dossier temporaire manquant',
|
||
|
UPLOAD_ERR_CANT_WRITE => 'Échec de l\'écriture du fichier sur le disque',
|
||
|
UPLOAD_ERR_EXTENSION => 'Une extension PHP a arrêté l\'upload'
|
||
|
];
|
||
|
return $errors[$error] ?? 'Erreur inconnue lors de l\'upload';
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Vérification de l'authentification
|
||
|
if (!Auth::check()) {
|
||
|
http_response_code(401);
|
||
|
exit(json_encode(['success' => false, 'error' => 'Non autorisé']));
|
||
|
}
|
||
|
|
||
|
// Traitement de la requête
|
||
|
try {
|
||
|
// Récupérer les données selon la méthode
|
||
|
$input = json_decode(file_get_contents('php://input'), true);
|
||
|
if ($input) {
|
||
|
// Cas d'une requête JSON (suppression)
|
||
|
$storyId = $input['storyId'] ?? null;
|
||
|
$chapterId = $input['chapterId'] ?? null;
|
||
|
$isDelete = $input['delete'] ?? false;
|
||
|
} else {
|
||
|
// Cas d'un upload de fichier
|
||
|
$storyId = $_POST['storyId'] ?? null;
|
||
|
$chapterId = $_POST['chapterId'] ?? null;
|
||
|
$isDelete = false;
|
||
|
}
|
||
|
|
||
|
if (!$storyId || !$chapterId) {
|
||
|
throw new Exception('Paramètres manquants');
|
||
|
}
|
||
|
|
||
|
// Récupération du roman
|
||
|
$story = Stories::get($storyId);
|
||
|
if (!$story) {
|
||
|
throw new Exception('Roman non trouvé');
|
||
|
}
|
||
|
|
||
|
// Trouver le chapitre concerné
|
||
|
$chapterFound = false;
|
||
|
foreach ($story['chapters'] as &$chapter) {
|
||
|
if ($chapter['id'] === $chapterId) {
|
||
|
$chapterFound = true;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (!$chapterFound) {
|
||
|
throw new Exception('Chapitre non trouvé');
|
||
|
}
|
||
|
|
||
|
$handler = new ChapterCoverHandler($storyId);
|
||
|
|
||
|
// Traitement selon le type de requête
|
||
|
if ($isDelete) {
|
||
|
$handler->removeCover($chapterId);
|
||
|
$chapter['cover'] = null;
|
||
|
} else if (isset($_FILES['cover'])) {
|
||
|
$chapter['cover'] = $handler->handleUpload($_FILES['cover'], $chapterId);
|
||
|
}
|
||
|
|
||
|
// Sauvegarde des modifications
|
||
|
Stories::save($story);
|
||
|
|
||
|
echo json_encode(['success' => true]);
|
||
|
|
||
|
} catch (Exception $e) {
|
||
|
http_response_code(500);
|
||
|
echo json_encode([
|
||
|
'success' => false,
|
||
|
'error' => $e->getMessage()
|
||
|
]);
|
||
|
}
|