WebOrbiton
v1.0.0.6

Publisium

233 lines · 9.7 KB
  1. <?php
  2. ​
  3. declare(strict_types=1);
  4. ​
  5. require_once __DIR__ . '/includes/config.php';
  6. require_once __DIR__ . '/includes/database.php';
  7. require_once __DIR__ . '/includes/auth.php';
  8. require_once __DIR__ . '/includes/csrf.php';
  9. require_once __DIR__ . '/includes/block-editor.php';
  10. require_once __DIR__ . '/includes/site-front.php';
  11. require_once __DIR__ . '/includes/activity-log.php';
  12. require_once __DIR__ . '/includes/indexnow.php';
  13. require_once __DIR__ . '/includes/author-profile.php';
  14. require_once __DIR__ . '/includes/avatar.php';
  15. require_once __DIR__ . '/includes/article-versions.php';
  16. require_once __DIR__ . '/includes/tags.php';
  17. require_once __DIR__ . '/includes/login-throttle.php';
  18. require_once __DIR__ . '/includes/two-factor.php';
  19. require_once __DIR__ . '/includes/read-aloud.php';
  20. ​
  21. Auth::boot();
  22. Auth::requireLogin();
  23. ​
  24. $currentUser = Auth::user();
  25. $currentRole = Auth::role();
  26. SiteFront::settings();
  27. $view = $_GET['view'] ?? 'overview';
  28. $allowedViews = ['overview', 'articles', 'article-edit', 'categories', 'pages', 'stats', 'profile', 'profile-reviews', 'security', 'trash'];
  29. if (!in_array($view, $allowedViews, true)) {
  30. $view = 'overview';
  31. }
  32. ​
  33. $flashMessage = null;
  34. $flashType = 'success';
  35. ​
  36. if ($_SERVER['REQUEST_METHOD'] === 'POST') {
  37. if (!Csrf::verify($_POST['csrf_token'] ?? null)) {
  38. $flashMessage = 'Your session expired. Please try again.';
  39. $flashType = 'error';
  40. } else {
  41. $action = $_POST['action'] ?? '';
  42. ​
  43. if ($action === 'save_article') {
  44. [$flashMessage, $flashType, $redirectId] = require __DIR__ . '/dashboard-actions/save-article.php';
  45. if ($redirectId !== null) {
  46. ActivityLog::record('article.save', 'article', (int) $redirectId, trim((string) ($_POST['title'] ?? '')) . ' [' . (string) ($_POST['publish_action'] ?? '') . ']');
  47. ArticleVersions::snapshot((int) $redirectId, (int) $currentUser['id'], (string) $currentUser['display_name']);
  48. ​
  49. if (ArticleTags::isEnabled() && isset($_POST['tags'])) {
  50. $taggedArticle = Database::site()->prepare('SELECT author_id FROM articles WHERE id = :id');
  51. $taggedArticle->execute(['id' => (int) $redirectId]);
  52. $taggedAuthor = $taggedArticle->fetchColumn();
  53. ​
  54. if ($taggedAuthor !== false && ((int) $taggedAuthor === (int) $currentUser['id'] || Auth::hasRoleAtLeast(Auth::ROLE_EDITOR_IN_CHIEF))) {
  55. ArticleTags::sync((int) $redirectId, ArticleTags::parse((string) $_POST['tags']));
  56. }
  57. }
  58. ​
  59. ReadAloud::saveArticleFields((int) $redirectId, $_POST, $currentUser);
  60. ReadAloud::queueGeneration((int) $redirectId);
  61. IndexNow::notifyArticle((int) $redirectId);
  62. header('Location: dashboard.php?view=article-edit&id=' . $redirectId . '&saved=1');
  63. exit;
  64. }
  65. }
  66. ​
  67. if ($action === 'restore_version') {
  68. [$flashMessage, $flashType, $restoredId] = require __DIR__ . '/dashboard-actions/restore-version.php';
  69. if ($restoredId !== null) {
  70. IndexNow::notifyArticle((int) $restoredId);
  71. header('Location: dashboard.php?view=article-edit&id=' . $restoredId . '&saved=1');
  72. exit;
  73. }
  74. }
  75. ​
  76. if (in_array($action, ['restore_article', 'purge_article', 'empty_trash'], true)) {
  77. $trashedId = require __DIR__ . '/dashboard-actions/trash-article.php';
  78. if ($action === 'restore_article' && $trashedId > 0) {
  79. IndexNow::notifyArticle($trashedId);
  80. ReadAloud::queueGeneration($trashedId);
  81. }
  82. header('Location: dashboard.php?view=' . ($action === 'restore_article' && $trashedId > 0 ? 'articles' : 'trash') . '&saved=1');
  83. exit;
  84. }
  85. ​
  86. if (in_array($action, ['totp_begin', 'totp_cancel', 'totp_confirm', 'totp_disable', 'totp_regenerate'], true)) {
  87. [$flashMessage, $flashType, $securityDone] = require __DIR__ . '/dashboard-actions/security.php';
  88. if ($securityDone) {
  89. header('Location: dashboard.php?view=security');
  90. exit;
  91. }
  92. }
  93. ​
  94. if ($action === 'delete_article') {
  95. $deletedArticle = Database::site()->prepare('SELECT title, slug, status FROM articles WHERE id = :id');
  96. $deletedArticle->execute(['id' => (int) ($_POST['article_id'] ?? 0)]);
  97. $deletedArticle = $deletedArticle->fetch() ?: [];
  98. require __DIR__ . '/dashboard-actions/delete-article.php';
  99. ActivityLog::record('article.trash', 'article', (int) ($_POST['article_id'] ?? 0), (string) ($deletedArticle['title'] ?? ''));
  100. if (($deletedArticle['status'] ?? '') === 'published') {
  101. IndexNow::notifyRemoved((string) $deletedArticle['slug'], (int) ($_POST['article_id'] ?? 0));
  102. }
  103. header('Location: dashboard.php?view=articles&deleted=1');
  104. exit;
  105. }
  106. ​
  107. if ($action === 'moderate_article') {
  108. require __DIR__ . '/dashboard-actions/moderate-article.php';
  109. ActivityLog::record('article.moderate', 'article', (int) ($_POST['article_id'] ?? 0), (string) ($_POST['decision'] ?? ''));
  110. ReadAloud::queueGeneration((int) ($_POST['article_id'] ?? 0));
  111. IndexNow::notifyArticle((int) ($_POST['article_id'] ?? 0));
  112. header('Location: dashboard.php?view=articles&moderated=1');
  113. exit;
  114. }
  115. ​
  116. if ($action === 'save_profile') {
  117. [$flashMessage, $flashType, $profileSaved] = require __DIR__ . '/dashboard-actions/save-profile.php';
  118. if ($profileSaved) {
  119. header('Location: dashboard.php?view=profile&saved=1');
  120. exit;
  121. }
  122. }
  123. ​
  124. if ($action === 'review_profile') {
  125. [$flashMessage, $flashType, $profileReviewed] = require __DIR__ . '/dashboard-actions/review-profile.php';
  126. if ($profileReviewed) {
  127. header('Location: dashboard.php?view=profile-reviews&moderated=1');
  128. exit;
  129. }
  130. }
  131. ​
  132. if ($action === 'save_category') {
  133. require __DIR__ . '/dashboard-actions/save-category.php';
  134. ActivityLog::record('category.save', 'category', (int) ($_POST['category_id'] ?? 0) ?: null, trim((string) ($_POST['name'] ?? '')));
  135. header('Location: dashboard.php?view=categories&saved=1');
  136. exit;
  137. }
  138. ​
  139. if ($action === 'delete_category') {
  140. require __DIR__ . '/dashboard-actions/delete-category.php';
  141. ActivityLog::record('category.delete', 'category', (int) ($_POST['category_id'] ?? 0));
  142. header('Location: dashboard.php?view=categories&deleted=1');
  143. exit;
  144. }
  145. ​
  146. if ($action === 'toggle_category_menu') {
  147. require __DIR__ . '/dashboard-actions/toggle-category-menu.php';
  148. header('Location: dashboard.php?view=categories');
  149. exit;
  150. }
  151. ​
  152. if ($action === 'save_page') {
  153. require __DIR__ . '/dashboard-actions/save-page.php';
  154. ActivityLog::record('page.save', 'page', (int) ($_POST['page_id'] ?? 0) ?: null, trim((string) ($_POST['title'] ?? '')));
  155. header('Location: dashboard.php?view=pages&saved=1');
  156. exit;
  157. }
  158. ​
  159. if ($action === 'save_homepage') {
  160. require __DIR__ . '/dashboard-actions/save-homepage.php';
  161. ActivityLog::record('page.save', 'page', null, 'Home page');
  162. header('Location: dashboard.php?view=pages&saved=1');
  163. exit;
  164. }
  165. ​
  166. if ($action === 'delete_page') {
  167. require __DIR__ . '/dashboard-actions/delete-page.php';
  168. ActivityLog::record('page.delete', 'page', (int) ($_POST['page_id'] ?? 0));
  169. header('Location: dashboard.php?view=pages&deleted=1');
  170. exit;
  171. }
  172. }
  173. }
  174. ​
  175. $dashView = $view;
  176. $dashPageTitle = 'Dashboard';
  177. $dashLoadBlockEditor = $view === 'article-edit';
  178. ​
  179. require __DIR__ . '/includes/dash-header.php';
  180. ​
  181. ?>
  182. ​
  183. <?php if ($flashMessage !== null): ?>
  184. <div class="dash-flash dash-flash-<?= htmlspecialchars($flashType) ?>"><?= Icons::icon($flashType === 'error' ? 'x' : 'check', 'icon icon-sm') ?><?= htmlspecialchars($flashMessage) ?></div>
  185. <?php endif; ?>
  186. ​
  187. <?php if (isset($_GET['saved'])): ?>
  188. <div class="dash-flash dash-flash-success"><?= Icons::icon('check', 'icon icon-sm') ?>Changes saved.</div>
  189. <?php endif; ?>
  190. <?php if (isset($_GET['deleted'])): ?>
  191. <div class="dash-flash dash-flash-success"><?= Icons::icon('check', 'icon icon-sm') ?>Deleted.</div>
  192. <?php endif; ?>
  193. <?php if (isset($_GET['moderated'])): ?>
  194. <div class="dash-flash dash-flash-success"><?= Icons::icon('check', 'icon icon-sm') ?>Done. The article has been updated.</div>
  195. <?php endif; ?>
  196. ​
  197. <?php
  198. switch ($view) {
  199. case 'overview':
  200. require __DIR__ . '/dashboard-views/overview.php';
  201. break;
  202. case 'articles':
  203. require __DIR__ . '/dashboard-views/articles.php';
  204. break;
  205. case 'article-edit':
  206. require __DIR__ . '/dashboard-views/article-edit.php';
  207. break;
  208. case 'categories':
  209. require __DIR__ . '/dashboard-views/categories.php';
  210. break;
  211. case 'pages':
  212. require __DIR__ . '/dashboard-views/pages.php';
  213. break;
  214. case 'stats':
  215. require __DIR__ . '/dashboard-views/stats.php';
  216. break;
  217. case 'profile':
  218. require __DIR__ . '/dashboard-views/profile.php';
  219. break;
  220. case 'profile-reviews':
  221. require __DIR__ . '/dashboard-views/profile-reviews.php';
  222. break;
  223. case 'security':
  224. require __DIR__ . '/dashboard-views/security.php';
  225. break;
  226. case 'trash':
  227. require __DIR__ . '/dashboard-views/trash.php';
  228. break;
  229. }
  230. ?>
  231. ​
  232. <?php require __DIR__ . '/includes/dash-footer.php'; ?>
  233. ​