WebOrbiton
v1.0.0.9

Publisium

141 lines · 5.6 KB
  1. <?php
  2. ​
  3. declare(strict_types=1);
  4. ​
  5. require_once __DIR__ . '/../includes/slugger.php';
  6. ​
  7. $title = trim((string) ($_POST['title'] ?? ''));
  8. $categoryId = (int) ($_POST['category_id'] ?? 0) ?: null;
  9. $excerpt = trim((string) ($_POST['excerpt'] ?? ''));
  10. $accessType = ($_POST['access_type'] ?? 'free') === 'paid' ? 'paid' : 'free';
  11. $seoTitle = trim((string) ($_POST['seo_title'] ?? ''));
  12. $seoDescription = trim((string) ($_POST['seo_description'] ?? ''));
  13. $coverImage = trim((string) ($_POST['cover_image_path'] ?? ''));
  14. $blocksJson = BlockEditor::sanitize((string) ($_POST['blocks_json'] ?? '{"blocks":[]}'));
  15. $articleId = (int) ($_POST['article_id'] ?? 0) ?: null;
  16. $requestedAction = (string) ($_POST['publish_action'] ?? '');
  17. if (!in_array($requestedAction, ['save_draft', 'update', 'submit_for_review', 'publish_now', 'schedule'], true)) {
  18. // No button pressed (Ctrl+S or Enter): an existing article keeps its status, a new one starts as a draft.
  19. $requestedAction = $articleId !== null ? 'update' : 'save_draft';
  20. }
  21. $scheduledInput = trim((string) ($_POST['scheduled_at'] ?? ''));
  22. $scheduledTimestamp = $scheduledInput !== '' ? strtotime($scheduledInput) : false;
  23. $scheduledAt = $scheduledTimestamp !== false ? date('Y-m-d H:i:s', $scheduledTimestamp) : '';
  24. ​
  25. if ($title === '') {
  26. return ['Give your article a title first.', 'error', null];
  27. }
  28. ​
  29. $slug = Slugger::make(trim((string) ($_POST['slug'] ?? '')));
  30. if ($slug === '') {
  31. $slug = Slugger::make($title);
  32. }
  33. $slug = $slug . '-' . substr(bin2hex(random_bytes(3)), 0, 6);
  34. ​
  35. $db = Database::site();
  36. ​
  37. if ($articleId !== null) {
  38. $existingStatement = $db->prepare('SELECT * FROM articles WHERE id = :id LIMIT 1');
  39. $existingStatement->execute(['id' => $articleId]);
  40. $existing = $existingStatement->fetch();
  41. ​
  42. if (!$existing) {
  43. return ['We couldn’t find this article. It may have been deleted.', 'error', null];
  44. }
  45. ​
  46. if ($existing['deleted_at'] !== null) {
  47. return ['This article is in the trash. Restore it first, then save your changes.', 'error', null];
  48. }
  49. ​
  50. $isOwner = (int) $existing['author_id'] === (int) $currentUser['id'];
  51. $canEditAny = Auth::hasRoleAtLeast(Auth::ROLE_EDITOR_IN_CHIEF);
  52. $canProofread = Auth::role() === Auth::ROLE_PROOFREADER;
  53. ​
  54. if (!$isOwner && !$canEditAny && !$canProofread) {
  55. return ['Only the author and editors can edit this article.', 'error', null];
  56. }
  57. ​
  58. if ($canProofread && !$isOwner) {
  59. $update = $db->prepare(
  60. 'UPDATE articles SET content_blocks = :content, updated_at = NOW() WHERE id = :id'
  61. );
  62. $update->execute(['content' => $blocksJson, 'id' => $articleId]);
  63. return ['Your corrections are saved.', 'success', $articleId];
  64. }
  65. ​
  66. $canPublish = Auth::canPublishDirectly() || $canEditAny;
  67. $status = $existing['status'];
  68. ​
  69. if ($requestedAction === 'submit_for_review') {
  70. $status = 'pending_review';
  71. } elseif ($requestedAction === 'publish_now') {
  72. $status = $canPublish ? 'published' : 'pending_review';
  73. } elseif ($requestedAction === 'schedule' && $scheduledAt !== '') {
  74. $status = $canPublish ? 'scheduled' : 'pending_review';
  75. } elseif ($requestedAction === 'save_draft' || !$canPublish) {
  76. $status = 'draft';
  77. }
  78. ​
  79. $publishedAt = $status === 'published' ? ($existing['published_at'] ?? date('Y-m-d H:i:s')) : $existing['published_at'];
  80. ​
  81. $update = $db->prepare(
  82. 'UPDATE articles SET category_id = :category_id, title = :title, excerpt = :excerpt,
  83. content_blocks = :content, cover_image_path = :cover, access_type = :access_type,
  84. status = :status, seo_title = :seo_title, seo_description = :seo_description,
  85. scheduled_at = :scheduled_at, published_at = :published_at, updated_at = NOW()
  86. WHERE id = :id'
  87. );
  88. $update->execute([
  89. 'category_id' => $categoryId,
  90. 'title' => $title,
  91. 'excerpt' => $excerpt,
  92. 'content' => $blocksJson,
  93. 'cover' => $coverImage,
  94. 'access_type' => $accessType,
  95. 'status' => $status,
  96. 'seo_title' => $seoTitle,
  97. 'seo_description' => $seoDescription,
  98. 'scheduled_at' => $status === 'scheduled' ? ($scheduledAt !== '' ? $scheduledAt : $existing['scheduled_at']) : null,
  99. 'published_at' => $publishedAt,
  100. 'id' => $articleId,
  101. ]);
  102. ​
  103. return ['Changes saved.', 'success', $articleId];
  104. }
  105. ​
  106. $status = 'draft';
  107. if ($requestedAction === 'submit_for_review') {
  108. $status = 'pending_review';
  109. } elseif ($requestedAction === 'publish_now') {
  110. $status = Auth::canPublishDirectly() ? 'published' : 'pending_review';
  111. } elseif ($requestedAction === 'schedule' && $scheduledAt !== '') {
  112. $status = Auth::canPublishDirectly() ? 'scheduled' : 'pending_review';
  113. }
  114. ​
  115. $insert = $db->prepare(
  116. 'INSERT INTO articles (author_id, category_id, title, slug, excerpt, content_blocks, cover_image_path,
  117. access_type, status, seo_title, seo_description, scheduled_at, published_at)
  118. VALUES (:author_id, :category_id, :title, :slug, :excerpt, :content, :cover,
  119. :access_type, :status, :seo_title, :seo_description, :scheduled_at, :published_at)'
  120. );
  121. $insert->execute([
  122. 'author_id' => $currentUser['id'],
  123. 'category_id' => $categoryId,
  124. 'title' => $title,
  125. 'slug' => $slug,
  126. 'excerpt' => $excerpt,
  127. 'content' => $blocksJson,
  128. 'cover' => $coverImage,
  129. 'access_type' => $accessType,
  130. 'status' => $status,
  131. 'seo_title' => $seoTitle,
  132. 'seo_description' => $seoDescription,
  133. 'scheduled_at' => $status === 'scheduled' ? $scheduledAt : null,
  134. 'published_at' => $status === 'published' ? date('Y-m-d H:i:s') : null,
  135. ]);
  136. ​
  137. $newId = (int) $db->lastInsertId();
  138. CleanUrls::sync();
  139. ​
  140. return ['Article saved.', 'success', $newId];
  141. ​