v1.0.0.9
Publisium
- <?php
-
- declare(strict_types=1);
-
- require_once __DIR__ . '/includes/config.php';
- require_once __DIR__ . '/includes/database.php';
- require_once __DIR__ . '/includes/auth.php';
- require_once __DIR__ . '/includes/csrf.php';
- require_once __DIR__ . '/includes/activity-log.php';
- require_once __DIR__ . '/includes/site-front.php';
- require_once __DIR__ . '/includes/site-comments.php';
-
- Auth::boot();
- Auth::requireRoleAtLeast(Auth::ROLE_MANAGING_EDITOR);
-
- $currentUser = Auth::user();
- $currentRole = Auth::role();
- $db = Database::site();
-
- $flashMessage = null;
- $flashType = 'success';
-
- if ($_SERVER['REQUEST_METHOD'] === 'POST') {
- if (!Csrf::verify($_POST['csrf_token'] ?? null)) {
- $flashMessage = 'Your session expired. Please try again.';
- $flashType = 'error';
- } else {
- require __DIR__ . '/dashboard-actions/manage-comments.php';
- }
- }
-
- $statusFilters = ['pending' => 'Waiting', 'approved' => 'Published', 'all' => 'All'];
- $statusFilter = array_key_exists((string) ($_GET['status'] ?? ''), $statusFilters) ? (string) $_GET['status'] : 'pending';
- $perPage = 50;
- $currentPage = max(1, (int) ($_GET['page'] ?? 1));
-
- $counts = ['pending' => 0, 'approved' => 0, 'all' => 0];
- foreach ($db->query('SELECT status, COUNT(*) AS total FROM article_comments GROUP BY status') as $countRow) {
- if (isset($counts[$countRow['status']])) {
- $counts[$countRow['status']] = (int) $countRow['total'];
- }
- $counts['all'] += (int) $countRow['total'];
- }
-
- $totalPages = max(1, (int) ceil($counts[$statusFilter] / $perPage));
- $currentPage = min($currentPage, $totalPages);
- $where = $statusFilter === 'all' ? '' : 'WHERE c.status = :status';
- $statement = $db->prepare(
- 'SELECT c.*, a.title AS article_title, a.slug AS article_slug, a.status AS article_status
- FROM article_comments c
- LEFT JOIN articles a ON a.id = c.article_id
- ' . $where . '
- ORDER BY c.created_at DESC, c.id DESC
- LIMIT ' . $perPage . ' OFFSET ' . (($currentPage - 1) * $perPage)
- );
- $statement->execute($statusFilter === 'all' ? [] : ['status' => $statusFilter]);
- $comments = $statement->fetchAll();
-
- $dashActivePage = 'comments';
- $dashPageTitle = 'Comments';
-
- require __DIR__ . '/includes/dash-header.php';
-
- ?>
-
- <?php if ($flashMessage !== null): ?>
- <div class="dash-flash dash-flash-<?= htmlspecialchars($flashType) ?>"><?= Icons::icon($flashType === 'error' ? 'x' : 'check', 'icon icon-sm') ?><?= htmlspecialchars($flashMessage) ?></div>
- <?php endif; ?>
-
- <div class="dash-header-row">
- <h1 class="dash-title"><?= Icons::icon('message', 'icon icon-lg') ?>Comments</h1>
- <?php if ($statusFilter === 'pending' && $counts['pending'] > 0): ?>
- <form method="post" onsubmit="return confirm('Delete all <?= (int) $counts['pending'] ?> waiting comments? This can’t be undone.');">
- <?= Csrf::field() ?>
- <input type="hidden" name="action" value="delete_pending_comments">
- <button type="submit" class="dash-btn dash-btn-danger"><?= Icons::icon('trash', 'icon icon-sm') ?>Delete all waiting</button>
- </form>
- <?php endif; ?>
- </div>
-
- <?php if (!SiteComments::isActive()): ?>
- <p class="comments-note">Publisium comments are not the selected comment service right now, so nothing new comes in and published comments aren’t shown on the site. You can switch in Settings → Comments.</p>
- <?php endif; ?>
-
- <nav class="comments-filter">
- <?php foreach ($statusFilters as $filterKey => $filterLabel): ?>
- <a href="comments.php?status=<?= $filterKey ?>" class="dash-btn<?= $statusFilter === $filterKey ? ' dash-btn-primary' : '' ?>"><?= htmlspecialchars($filterLabel) ?> (<?= $counts[$filterKey] ?>)</a>
- <?php endforeach; ?>
- </nav>
-
- <table class="dash-table comments-table">
- <thead>
- <tr>
- <th><?= Icons::icon('message', 'icon icon-sm') ?>Comment</th>
- <th><?= Icons::icon('user', 'icon icon-sm') ?>Author</th>
- <th><?= Icons::icon('articles', 'icon icon-sm') ?>Article</th>
- <th><?= Icons::icon('calendar', 'icon icon-sm') ?>Date</th>
- <th><?= Icons::icon('eye', 'icon icon-sm') ?>Status</th>
- <th></th>
- </tr>
- </thead>
- <tbody>
- <?php foreach ($comments as $comment): ?>
- <?php $commentId = (int) $comment['id']; ?>
- <tr>
- <td class="comments-table-body">
- <?php if ($comment['parent_id'] !== null): ?><span class="status-pill status-draft">Reply</span><?php endif; ?>
- <?= SiteComments::formatBody((string) $comment['body']) ?>
- </td>
- <td>
- <?= htmlspecialchars((string) $comment['author_name']) ?>
- <span class="status-pill status-draft"><?= $comment['user_account_id'] !== null ? 'Reader' : 'Guest' ?></span>
- <?php if (!empty($comment['author_email'])): ?>
- <div class="comments-email"><a href="mailto:<?= htmlspecialchars((string) $comment['author_email'], ENT_QUOTES) ?>"><?= htmlspecialchars((string) $comment['author_email']) ?></a></div>
- <?php endif; ?>
- </td>
- <td>
- <?php if ($comment['article_title'] === null): ?>
- <em>Deleted article</em>
- <?php elseif ($comment['article_status'] === 'published'): ?>
- <a href="<?= htmlspecialchars(SiteFront::articleUrl((string) $comment['article_slug'], '') . '#comment-' . $commentId, ENT_QUOTES) ?>" target="_blank" rel="noopener"><?= htmlspecialchars((string) $comment['article_title']) ?></a>
- <?php else: ?>
- <?= htmlspecialchars((string) $comment['article_title']) ?>
- <?php endif; ?>
- </td>
- <td><?= htmlspecialchars(date('M j, Y H:i', strtotime((string) $comment['created_at']) ?: time())) ?></td>
- <td>
- <?php if ($comment['status'] === 'approved'): ?>
- <span class="status-pill status-published">Published</span>
- <?php else: ?>
- <span class="status-pill status-pending_review">Waiting</span>
- <?php endif; ?>
- </td>
- <td class="dash-table-actions">
- <form method="post" style="display:inline;">
- <?= Csrf::field() ?>
- <input type="hidden" name="comment_id" value="<?= $commentId ?>">
- <?php if ($comment['status'] === 'approved'): ?>
- <button type="submit" name="action" value="hide_comment" class="dash-btn-small"><?= Icons::icon('eye', 'icon icon-sm') ?>Hide</button>
- <?php else: ?>
- <button type="submit" name="action" value="approve_comment" class="dash-btn-small"><?= Icons::icon('check', 'icon icon-sm') ?>Approve</button>
- <?php endif; ?>
- </form>
- <form method="post" style="display:inline;" onsubmit="return confirm('Delete this comment? Replies to it are deleted too.');">
- <?= Csrf::field() ?>
- <input type="hidden" name="action" value="delete_comment">
- <input type="hidden" name="comment_id" value="<?= $commentId ?>">
- <button type="submit" class="dash-btn-small dash-btn-danger"><?= Icons::icon('trash', 'icon icon-sm') ?>Delete</button>
- </form>
- </td>
- </tr>
- <?php endforeach; ?>
- <?php if (empty($comments)): ?>
- <tr>
- <td colspan="6"><?= $statusFilter === 'pending' ? 'Nothing is waiting for approval.' : 'No comments here yet.' ?></td>
- </tr>
- <?php endif; ?>
- </tbody>
- </table>
-
- <?php if ($totalPages > 1): ?>
- <nav class="comments-filter">
- <?php for ($pageNumber = 1; $pageNumber <= $totalPages; $pageNumber++): ?>
- <a href="comments.php?status=<?= $statusFilter ?>&page=<?= $pageNumber ?>" class="dash-btn<?= $pageNumber === $currentPage ? ' dash-btn-primary' : '' ?>"><?= $pageNumber ?></a>
- <?php endfor; ?>
- </nav>
- <?php endif; ?>
-
- <style>
- .comments-filter { display: flex; flex-wrap: wrap; gap: 8px; margin: 0 0 16px; }
- .comments-note { color: var(--muted); font-size: 13px; }
- .comments-table-body { max-width: 420px; overflow-wrap: anywhere; white-space: normal; }
- .comments-table-body .status-pill { margin-right: 6px; }
- .comments-email { margin-top: 4px; font-size: 12px; overflow-wrap: anywhere; }
- </style>
-
- <?php require __DIR__ . '/includes/dash-footer.php'; ?>
-