v2.0.0.1
Publisium
- <?php
-
- declare(strict_types=1);
-
- require_once __DIR__ . '/../includes/slugger.php';
-
- $title = trim((string) ($_POST['title'] ?? ''));
- $categoryId = (int) ($_POST['category_id'] ?? 0) ?: null;
- $excerpt = trim((string) ($_POST['excerpt'] ?? ''));
- $accessType = ($_POST['access_type'] ?? 'free') === 'paid' ? 'paid' : 'free';
- $seoTitle = trim((string) ($_POST['seo_title'] ?? ''));
- $seoDescription = trim((string) ($_POST['seo_description'] ?? ''));
- $noindex = isset($_POST['noindex']) ? 1 : 0;
- $coverImage = trim((string) ($_POST['cover_image_path'] ?? ''));
- $blocksJson = BlockEditor::sanitize((string) ($_POST['blocks_json'] ?? '{"blocks":[]}'));
- $articleId = (int) ($_POST['article_id'] ?? 0) ?: null;
- $requestedAction = (string) ($_POST['publish_action'] ?? '');
- if (!in_array($requestedAction, ['save_draft', 'update', 'submit_for_review', 'publish_now', 'schedule'], true)) {
- // No button pressed (Ctrl+S or Enter): an existing article keeps its status, a new one starts as a draft.
- $requestedAction = $articleId !== null ? 'update' : 'save_draft';
- }
- $scheduledInput = trim((string) ($_POST['scheduled_at'] ?? ''));
- $scheduledTimestamp = $scheduledInput !== '' ? strtotime($scheduledInput) : false;
- $scheduledAt = $scheduledTimestamp !== false ? date('Y-m-d H:i:s', $scheduledTimestamp) : '';
-
- if ($title === '') {
- return ['Give your article a title first.', 'error', null];
- }
-
- $slug = Slugger::make(trim((string) ($_POST['slug'] ?? '')));
- if ($slug === '') {
- $slug = Slugger::make($title);
- }
- $slug = $slug . '-' . substr(bin2hex(random_bytes(3)), 0, 6);
-
- $db = Database::site();
-
- if ($articleId !== null) {
- $existingStatement = $db->prepare('SELECT * FROM articles WHERE id = :id LIMIT 1');
- $existingStatement->execute(['id' => $articleId]);
- $existing = $existingStatement->fetch();
-
- if (!$existing) {
- return ['We couldn’t find this article. It may have been deleted.', 'error', null];
- }
-
- if ($existing['deleted_at'] !== null) {
- return ['This article is in the trash. Restore it first, then save your changes.', 'error', null];
- }
-
- $isOwner = (int) $existing['author_id'] === (int) $currentUser['id'];
- $canEditAny = Auth::hasRoleAtLeast(Auth::ROLE_EDITOR_IN_CHIEF);
- $canProofread = Auth::role() === Auth::ROLE_PROOFREADER;
-
- if (!$isOwner && !$canEditAny && !$canProofread) {
- return ['Only the author and editors can edit this article.', 'error', null];
- }
-
- if ($canProofread && !$isOwner) {
- $update = $db->prepare(
- 'UPDATE articles SET content_blocks = :content, updated_at = NOW() WHERE id = :id'
- );
- $update->execute(['content' => $blocksJson, 'id' => $articleId]);
- return ['Your corrections are saved.', 'success', $articleId];
- }
-
- $canPublish = Auth::canPublishDirectly() || $canEditAny;
- $status = $existing['status'];
-
- if ($requestedAction === 'submit_for_review') {
- $status = 'pending_review';
- } elseif ($requestedAction === 'publish_now') {
- $status = $canPublish ? 'published' : 'pending_review';
- } elseif ($requestedAction === 'schedule' && $scheduledAt !== '') {
- $status = $canPublish ? 'scheduled' : 'pending_review';
- } elseif ($requestedAction === 'save_draft' || !$canPublish) {
- $status = 'draft';
- }
-
- $publishedAt = $status === 'published' ? ($existing['published_at'] ?? date('Y-m-d H:i:s')) : $existing['published_at'];
-
- $update = $db->prepare(
- 'UPDATE articles SET category_id = :category_id, title = :title, excerpt = :excerpt,
- content_blocks = :content, cover_image_path = :cover, access_type = :access_type,
- status = :status, seo_title = :seo_title, seo_description = :seo_description, noindex = :noindex,
- scheduled_at = :scheduled_at, published_at = :published_at, updated_at = NOW()
- WHERE id = :id'
- );
- $update->execute([
- 'category_id' => $categoryId,
- 'title' => $title,
- 'excerpt' => $excerpt,
- 'content' => $blocksJson,
- 'cover' => $coverImage,
- 'access_type' => $accessType,
- 'status' => $status,
- 'seo_title' => $seoTitle,
- 'seo_description' => $seoDescription,
- 'noindex' => $noindex,
- 'scheduled_at' => $status === 'scheduled' ? ($scheduledAt !== '' ? $scheduledAt : $existing['scheduled_at']) : null,
- 'published_at' => $publishedAt,
- 'id' => $articleId,
- ]);
-
- return ['Changes saved.', 'success', $articleId];
- }
-
- $status = 'draft';
- if ($requestedAction === 'submit_for_review') {
- $status = 'pending_review';
- } elseif ($requestedAction === 'publish_now') {
- $status = Auth::canPublishDirectly() ? 'published' : 'pending_review';
- } elseif ($requestedAction === 'schedule' && $scheduledAt !== '') {
- $status = Auth::canPublishDirectly() ? 'scheduled' : 'pending_review';
- }
-
- $insert = $db->prepare(
- 'INSERT INTO articles (author_id, category_id, title, slug, excerpt, content_blocks, cover_image_path,
- access_type, status, seo_title, seo_description, noindex, scheduled_at, published_at)
- VALUES (:author_id, :category_id, :title, :slug, :excerpt, :content, :cover,
- :access_type, :status, :seo_title, :seo_description, :noindex, :scheduled_at, :published_at)'
- );
- $insert->execute([
- 'author_id' => $currentUser['id'],
- 'category_id' => $categoryId,
- 'title' => $title,
- 'slug' => $slug,
- 'excerpt' => $excerpt,
- 'content' => $blocksJson,
- 'cover' => $coverImage,
- 'access_type' => $accessType,
- 'status' => $status,
- 'seo_title' => $seoTitle,
- 'seo_description' => $seoDescription,
- 'noindex' => $noindex,
- 'scheduled_at' => $status === 'scheduled' ? $scheduledAt : null,
- 'published_at' => $status === 'published' ? date('Y-m-d H:i:s') : null,
- ]);
-
- $newId = (int) $db->lastInsertId();
- CleanUrls::sync();
-
- return ['Article saved.', 'success', $newId];
-