web_writer/book_edit.php

350 lines
16 KiB
PHP
Executable File
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
require_once 'config/config.php';
require_login();
// Подключаем функции для обложек
$user_id = $_SESSION['user_id'];
$bookModel = new Book($pdo);
// Проверяем, редактируем ли существующую книгу
$book_id = $_GET['id'] ?? null;
$book = null;
$is_edit = false;
if ($book_id) {
$book = $bookModel->findById($book_id);
if (!$book || $book['user_id'] != $user_id) {
$_SESSION['error'] = "Книга не найдена или у вас нет доступа";
redirect('books.php');
}
$is_edit = true;
}
// Обработка формы
$cover_error = '';
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
if (!verify_csrf_token($_POST['csrf_token'] ?? '')) {
$_SESSION['error'] = "Ошибка безопасности";
redirect($is_edit ? "book_edit.php?id=$book_id" : 'book_edit.php');
}
$title = trim($_POST['title'] ?? '');
$description = trim($_POST['description'] ?? '');
$genre = trim($_POST['genre'] ?? '');
if (empty($title)) {
$_SESSION['error'] = "Название книги обязательно";
} else {
$series_id = !empty($_POST['series_id']) ? (int)$_POST['series_id'] : null;
$sort_order_in_series = !empty($_POST['sort_order_in_series']) ? (int)$_POST['sort_order_in_series'] : null;
// Если серия указана, но порядок нет - генерируем автоматически
if ($series_id && !$sort_order_in_series) {
$seriesModel = new Series($pdo);
$sort_order_in_series = $seriesModel->getNextSortOrder($series_id);
}
$data = [
'title' => $title,
'description' => $description,
'genre' => $genre,
'user_id' => $user_id,
'series_id' => $series_id,
'sort_order_in_series' => $sort_order_in_series
];
$data['published'] = isset($_POST['published']) ? 1 : 0;
// Обработка загрузки обложки
if (isset($_FILES['cover_image']) && $_FILES['cover_image']['error'] === UPLOAD_ERR_OK) {
$cover_result = handleCoverUpload($_FILES['cover_image'], $book_id);
if ($cover_result['success']) {
$bookModel->updateCover($book_id, $cover_result['filename']);
// Обновляем данные книги
$book = $bookModel->findById($book_id);
} else {
$cover_error = $cover_result['error'];
}
}
// Обработка удаления обложки
if (isset($_POST['delete_cover']) && $_POST['delete_cover'] == '1') {
$bookModel->deleteCover($book_id);
$book = $bookModel->findById($book_id);
}
if ($is_edit) {
$success = $bookModel->update($book_id, $data);
$message = $success ? "Книга успешно обновлена" : "Ошибка при обновлении книги";
} else {
$success = $bookModel->create($data);
$message = $success ? "Книга успешно создана" : "Ошибка при создании книги";
if ($success) {
$new_book_id = $pdo->lastInsertId();
redirect("book_edit.php?id=$new_book_id");
}
}
if ($success) {
$_SESSION['success'] = $message;
redirect('books.php');
} else {
$_SESSION['error'] = $message;
}
}
}
$page_title = $is_edit ? "Редактирование книги" : "Создание новой книги";
include 'views/header.php';
?>
<!-- Остальная часть формы остается той же, но добавляем поле обложки -->
<form method="post" enctype="multipart/form-data">
<input type="hidden" name="csrf_token" value="<?= generate_csrf_token() ?>">
<div style="max-width: 100%; margin-bottom: 0.5rem;">
<label for="title" style="display: block; margin-bottom: 0.5rem; font-weight: bold;">
Название книги *
</label>
<input type="text" id="title" name="title"
value="<?= e($book['title'] ?? $_POST['title'] ?? '') ?>"
placeholder="Введите название книги"
style="width: 100%; margin-bottom: 1.5rem;"
required>
<label for="genre" style="display: block; margin-bottom: 0.5rem; font-weight: bold;">
Жанр
</label>
<input type="text" id="genre" name="genre"
value="<?= e($book['genre'] ?? $_POST['genre'] ?? '') ?>"
placeholder="Например: Фантастика, Роман, Детектив..."
style="width: 100%; margin-bottom: 1.5rem;">
<label for="series_id" style="display: block; margin-bottom: 0.5rem; font-weight: bold;">
Серия
</label>
<select id="series_id" name="series_id" style="width: 100%; margin-bottom: 1rem;">
<option value="">-- Без серии --</option>
<?php
$seriesModel = new Series($pdo);
$user_series = $seriesModel->findByUser($user_id, false);
foreach ($user_series as $ser):
$selected = ($ser['id'] == ($book['series_id'] ?? 0)) ? 'selected' : '';
?>
<option value="<?= $ser['id'] ?>" <?= $selected ?>>
<?= e($ser['title']) ?>
</option>
<?php endforeach; ?>
</select>
<label for="sort_order_in_series" style="display: block; margin-bottom: 0.5rem; font-weight: bold;">
Порядок в серии
</label>
<input type="number" id="sort_order_in_series" name="sort_order_in_series"
value="<?= e($book['sort_order_in_series'] ?? '') ?>"
placeholder="Номер по порядку в серии"
min="1"
style="width: 100%; margin-bottom: 1.5rem;">
<!-- ПОЛЕ ДЛЯ ОБЛОЖКИ -->
<div style="margin-bottom: 1.5rem;">
<label for="cover_image" style="display: block; margin-bottom: 0.5rem; font-weight: bold;">
Обложка книги
</label>
<?php if (!empty($book['cover_image'])): ?>
<div style="margin-bottom: 1rem;">
<p><strong>Текущая обложка:</strong></p>
<img src="<?= COVERS_URL . e($book['cover_image']) ?>"
alt="Обложка"
style="max-width: 200px; height: auto; border-radius: 4px; border: 1px solid #ddd;">
<div style="margin-top: 0.5rem;">
<label style="display: inline-flex; align-items: center; gap: 0.5rem;">
<input type="checkbox" name="delete_cover" value="1">
Удалить обложку
</label>
</div>
</div>
<?php endif; ?>
<input type="file" id="cover_image" name="cover_image"
accept="image/jpeg, image/png, image/gif, image/webp"
style="height: 2.6rem;">
<small style="color: #666;">
Разрешены: JPG, PNG, GIF, WebP. Максимальный размер: 5MB.
Рекомендуемый размер: 300×450 пикселей.
</small>
<?php if (!empty($cover_error)): ?>
<div style="color: #d32f2f; margin-top: 0.5rem;">
❌ <?= e($cover_error) ?>
</div>
<?php endif; ?>
</div>
<label for="description" style="display: block; margin-bottom: 0.5rem; font-weight: bold;">
Описание книги
</label>
<textarea id="description" name="description"
placeholder="Краткое описание сюжета или аннотация..."
rows="6"
style="width: 100%;"><?= e($book['description'] ?? $_POST['description'] ?? '') ?></textarea>
<div>
<label for="published">
<input type="checkbox" id="published" name="published" value="1"
<?= !empty($book['published']) || (!empty($_POST['published']) && $_POST['published']) ? 'checked' : '' ?>>
Опубликовать книгу (показывать на публичной странице автора)
</label>
</div>
</div>
<div style="display: flex; gap: 5px; flex-wrap: wrap; align-items: center;">
<button type="submit" class="contrast compact-button">
<?= $is_edit ? '💾 Сохранить изменения' : '📖 Создать книгу' ?>
</button>
</div>
</form>
<?php if ($is_edit): ?>
<form method="post" action="book_delete.php" style="display: inline;" onsubmit="return confirm('Вы уверены, что хотите удалить книгу «<?= e($book['title']) ?>»? Все главы также будут удалены.');">
<input type="hidden" name="book_id" value="<?= $book['id'] ?>">
<input type="hidden" name="csrf_token" value="<?= generate_csrf_token() ?>">
<button type="submit" class="compact-button secondary" style="background: #ff4444; border-color: #ff4444; color: white;" title="Удалить книгу">
🗑️
</button>
</form>
<?php endif ?>
<?php if ($is_edit): ?>
<div style="margin-top: 2rem; padding: 1rem; background: #f8f9fa; border-radius: 5px;">
<h3>Публичная ссылка для чтения</h3>
<p style="margin-bottom: 0.5rem;">Отправьте эту ссылку читателям для просмотра опубликованных глав:</p>
<div style="display: flex; gap: 5px; align-items: center; flex-wrap: wrap;">
<input type="text"
id="share-link"
value="<?= e(SITE_URL . '/view_book.php?share_token=' . $book['share_token']) ?>"
readonly
style="flex: 1; padding: 8px; border: 1px solid #ddd; border-radius: 4px; background: white;">
<button type="button" onclick="copyShareLink()" class="compact-button secondary">
📋 Копировать
</button>
<form method="post" action="book_regenerate_token.php" style="display: inline;">
<input type="hidden" name="book_id" value="<?= $book_id ?>">
<input type="hidden" name="csrf_token" value="<?= generate_csrf_token() ?>">
<button type="submit" class="compact-button secondary" onclick="return confirm('Создать новую ссылку? Старая ссылка перестанет работать.')">
🔄 Обновить
</button>
</form>
</div>
<p style="margin-top: 0.5rem; font-size: 0.9em; color: #666;">
<strong>Примечание:</strong> В публичном просмотре отображаются только главы со статусом "Опубликована"
</p>
</div>
<script>
function copyShareLink() {
const shareLink = document.getElementById('share-link');
shareLink.select();
shareLink.setSelectionRange(0, 99999);
document.execCommand('copy');
// Показать уведомление
const button = event.target;
const originalText = button.innerHTML;
button.innerHTML = '✅ Скопировано';
setTimeout(() => {
button.innerHTML = originalText;
}, 2000);
}
</script>
<?php endif; ?>
<?php if ($is_edit): ?>
<div style="margin-top: 2rem; padding: 1rem; background: #f8f9fa; border-radius: 5px;">
<h3>Экспорт книги</h3>
<p style="margin-bottom: 0.5rem;">Экспортируйте книгу в различные форматы:</p>
<div style="display: flex; gap: 5px; flex-wrap: wrap;">
<a href="export_book.php?book_id=<?= $book_id ?>&format=pdf" class="adaptive-button secondary" target="_blank">
📄 PDF
</a>
<a href="export_book.php?book_id=<?= $book_id ?>&format=docx" class="adaptive-button secondary" target="_blank">
📝 DOCX
</a>
<a href="export_book.php?book_id=<?= $book_id ?>&format=html" class="adaptive-button secondary" target="_blank">
🌐 HTML
</a>
<a href="export_book.php?book_id=<?= $book_id ?>&format=txt" class="adaptive-button secondary" target="_blank">
📄 TXT
</a>
</div>
<p style="margin-top: 0.5rem; font-size: 0.9em; color: #666;">
<strong>Примечание:</strong> Экспортируются все главы книги (включая черновики)
</p>
</div>
<?php endif; ?>
<?php if ($is_edit): ?>
<div style="margin-top: 3rem;">
<h2>Главы этой книги</h2>
<a href="chapters.php?book_id=<?= $book_id ?>" class="compact-button secondary">
📑 Все главы
</a>
&nbsp;
<a href="chapter_edit.php?book_id=<?= $book_id ?>" role="button" class="compact-button secondary">
✏️ Добавить главу
</a>
<?php
// Получаем главы книги
$stmt = $pdo->prepare("SELECT * FROM chapters WHERE book_id = ? ORDER BY sort_order, created_at");
$stmt->execute([$book_id]);
$chapters = $stmt->fetchAll();
if ($chapters): ?>
<div style="overflow-x: auto;">
<table style="width: 100%;">
<thead>
<tr>
<th style="text-align: left; padding: 12px 8px;">Название</th>
<th style="text-align: left; padding: 12px 8px;">Статус</th>
<th style="text-align: left; padding: 12px 8px;">Слов</th>
<th style="text-align: left; padding: 12px 8px;">Действия</th>
</tr>
</thead>
<tbody>
<?php foreach ($chapters as $chapter): ?>
<tr style="border-bottom: 1px solid #eee;">
<td style="padding: 12px 8px;"><?= e($chapter['title']) ?></td>
<td style="padding: 12px 8px;">
<span style="color: <?= $chapter['status'] == 'published' ? 'green' : 'orange' ?>">
<?= $chapter['status'] == 'published' ? 'Опубликована' : 'Черновик' ?>
</span>
</td>
<td style="padding: 12px 8px;"><?= $chapter['word_count'] ?></td>
<td style="padding: 12px 8px;">
<a href="chapter_edit.php?id=<?= $chapter['id'] ?>" role="button" class="compact-button secondary" style="text-decoration: none;">
Редактировать
</a>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
<?php else: ?>
<div style="text-align: center; padding: 2rem; background: #f9f9f9; border-radius: 5px;">
<p style="margin-bottom: 1rem;">В этой книге пока нет глав.</p>
<a href="chapter_edit.php?book_id=<?= $book_id ?>" role="button" class="compact-button secondary" >
✏️ Добавить первую главу
</a>
</div>
<?php endif; ?>
</div>
<?php endif; ?>
<?php include 'views/footer.php'; ?>