comments.php 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307
  1. <?php
  2. // Start session for language preference
  3. if (session_status() === PHP_SESSION_NONE) {
  4. session_start();
  5. }
  6. require_once '../includes/config.php';
  7. require_once '../includes/database.php';
  8. require_once '../includes/auth.php';
  9. require_once '../includes/comment.php';
  10. require_once '../includes/translation.php';
  11. // Translation system is auto-initialized when translation.php is included
  12. $auth = new Auth();
  13. $auth->requireAuth();
  14. $comment = new Comment();
  15. $user = $auth->getUser();
  16. // Handle actions
  17. $action = $_GET['action'] ?? '';
  18. $message = '';
  19. if ($action === 'approve' && isset($_GET['id'])) {
  20. $id = (int)$_GET['id'];
  21. if ($comment->updateStatus($id, 'approved')) {
  22. $message = t('admin_comment_approved_success');
  23. } else {
  24. $message = t('admin_comment_approve_error');
  25. }
  26. header('Location: comments.php?message=' . urlencode($message));
  27. exit;
  28. }
  29. if ($action === 'reject' && isset($_GET['id'])) {
  30. $id = (int)$_GET['id'];
  31. if ($comment->updateStatus($id, 'rejected')) {
  32. $message = t('admin_comment_rejected_success');
  33. } else {
  34. $message = t('admin_comment_reject_error');
  35. }
  36. header('Location: comments.php?message=' . urlencode($message));
  37. exit;
  38. }
  39. if ($action === 'delete' && isset($_GET['id'])) {
  40. $id = (int)$_GET['id'];
  41. if ($comment->delete($id)) {
  42. $message = t('admin_comment_deleted_success');
  43. } else {
  44. $message = t('admin_comment_delete_error');
  45. }
  46. header('Location: comments.php?message=' . urlencode($message));
  47. exit;
  48. }
  49. if ($action === 'reply' && isset($_POST['comment_id']) && isset($_POST['reply_content'])) {
  50. $commentId = (int)$_POST['comment_id'];
  51. $replyContent = trim($_POST['reply_content']);
  52. // Get original comment to get publication ID
  53. $originalComment = $comment->getById($commentId);
  54. if ($originalComment && !empty($replyContent)) {
  55. $replyData = [
  56. 'publication_id' => $originalComment['publication_id'],
  57. 'parent_id' => $commentId,
  58. 'name' => $user['username'],
  59. 'content' => $replyContent,
  60. 'replied_by' => $user['id']
  61. ];
  62. if ($comment->createAdminReply($replyData)) {
  63. $message = t('admin_reply_added_success');
  64. } else {
  65. $message = t('admin_reply_add_error');
  66. }
  67. } else {
  68. $message = t('admin_reply_invalid_data');
  69. }
  70. header('Location: comments.php?message=' . urlencode($message));
  71. exit;
  72. }
  73. // Get pagination parameters
  74. $page = max(1, (int)($_GET['page'] ?? 1));
  75. $limit = 20;
  76. $offset = ($page - 1) * $limit;
  77. // Get status filter
  78. $status = $_GET['status'] ?? '';
  79. // Get comments
  80. $comments = $comment->getAll($status ?: null, $limit, $offset);
  81. $totalComments = $comment->getCountByStatus($status ?: null);
  82. $totalPages = ceil($totalComments / $limit);
  83. // Handle message from redirect
  84. if (isset($_GET['message'])) {
  85. $message = htmlspecialchars($_GET['message']);
  86. }
  87. ?>
  88. <!DOCTYPE html>
  89. <html lang="<?php echo Translation::getCurrentLang(); ?>">
  90. <head>
  91. <meta charset="UTF-8">
  92. <meta name="viewport" content="width=device-width, initial-scale=1.0">
  93. <title><?php echo t('manage_comments'); ?> - <?php echo SITE_TITLE; ?></title>
  94. <link rel="stylesheet" href="../css/style.css">
  95. <link rel="stylesheet" href="../css/admin-comments.css">
  96. </head>
  97. <body>
  98. <div class="admin-layout">
  99. <header class="admin-header">
  100. <div class="header-content">
  101. <h1><a href="/index.php"><?php echo SITE_TITLE; ?></a></h1>
  102. <nav class="admin-nav">
  103. <a href="index.php" class="nav-link"><?php echo t('admin_nav_dashboard'); ?></a>
  104. <a href="publications.php" class="nav-link"><?php echo t('admin_nav_publications'); ?></a>
  105. <a href="categories.php" class="nav-link"><?php echo t('admin_nav_categories'); ?></a>
  106. <a href="comments.php" class="nav-link active"><?php echo t('admin_nav_comments'); ?></a>
  107. <a href="users.php" class="nav-link"><?php echo t('manage_users'); ?></a>
  108. <a href="wordpress_import.php" class="nav-link"><?php echo t('wordpress_import'); ?></a>
  109. <?php if (LDAP_ENABLED): ?>
  110. <a href="ldap-users.php" class="nav-link"><?php echo t('admin_nav_ldap_users'); ?></a>
  111. <?php endif; ?>
  112. <a href="logout.php" class="nav-link"><?php echo t('admin_nav_logout'); ?></a>
  113. </nav>
  114. <div class="user-info">
  115. <?php echo t('welcome'); ?>, <?php echo htmlspecialchars($user['username']); ?>
  116. </div>
  117. <?php echo Translation::getLanguageSwitcher('comments.php'); ?>
  118. </div>
  119. </header>
  120. <main class="admin-main">
  121. <div class="page-header">
  122. <h2><?php echo t('manage_comments'); ?></h2>
  123. <div class="header-stats">
  124. <span class="stat-item">
  125. <?php echo t('pending_comments'); ?>: <strong><?php echo $comment->getPendingCount(); ?></strong>
  126. </span>
  127. <span class="stat-item">
  128. <?php echo t('total_comments'); ?>: <strong><?php echo $comment->getCountByStatus(); ?></strong>
  129. </span>
  130. </div>
  131. </div>
  132. <?php if ($message): ?>
  133. <div class="alert alert-<?php echo strpos($message, 'Error') === false ? 'success' : 'error'; ?>">
  134. <?php echo $message; ?>
  135. </div>
  136. <?php endif; ?>
  137. <!-- Status Filter -->
  138. <div class="filter-section">
  139. <form method="get" class="filter-form">
  140. <label for="status"><?php echo t('filter_by_status'); ?>:</label>
  141. <select name="status" id="status" onchange="this.form.submit()">
  142. <option value=""><?php echo t('all_comments'); ?></option>
  143. <option value="pending" <?php echo $status === 'pending' ? 'selected' : ''; ?>><?php echo t('pending'); ?></option>
  144. <option value="approved" <?php echo $status === 'approved' ? 'selected' : ''; ?>><?php echo t('approved'); ?></option>
  145. <option value="rejected" <?php echo $status === 'rejected' ? 'selected' : ''; ?>><?php echo t('rejected'); ?></option>
  146. </select>
  147. </form>
  148. </div>
  149. <!-- Comments List -->
  150. <div class="comments-admin-list">
  151. <?php if (empty($comments)): ?>
  152. <p class="no-comments"><?php echo t('no_comments_found'); ?></p>
  153. <?php else: ?>
  154. <?php foreach ($comments as $comment_item): ?>
  155. <div class="comment-admin-item <?php echo $comment_item['status']; ?>">
  156. <div class="comment-header">
  157. <div class="comment-meta">
  158. <span class="comment-author">
  159. <strong><?php echo htmlspecialchars($comment_item['name']); ?></strong>
  160. <?php if ($comment_item['email']): ?>
  161. <small>(<?php echo htmlspecialchars($comment_item['email']); ?>)</small>
  162. <?php endif; ?>
  163. </span>
  164. <span class="comment-date">
  165. <?php echo date('M j, Y g:i A', strtotime($comment_item['created_at'])); ?>
  166. </span>
  167. <span class="comment-status status-<?php echo $comment_item['status']; ?>">
  168. <?php echo t($comment_item['status']); ?>
  169. </span>
  170. </div>
  171. <div class="comment-actions">
  172. <?php if ($comment_item['status'] === 'pending'): ?>
  173. <a href="comments.php?action=approve&id=<?php echo $comment_item['id']; ?>"
  174. class="btn btn-sm btn-success"><?php echo t('approve'); ?></a>
  175. <?php endif; ?>
  176. <?php if ($comment_item['status'] !== 'rejected'): ?>
  177. <a href="comments.php?action=reject&id=<?php echo $comment_item['id']; ?>"
  178. class="btn btn-sm btn-warning"><?php echo t('reject'); ?></a>
  179. <?php endif; ?>
  180. <button type="button" class="btn btn-sm btn-primary reply-btn"
  181. data-comment-id="<?php echo $comment_item['id']; ?>">
  182. <?php echo t('reply'); ?>
  183. </button>
  184. <a href="comments.php?action=delete&id=<?php echo $comment_item['id']; ?>"
  185. class="btn btn-sm btn-danger"
  186. onclick="return confirm('<?php echo t('admin_delete_comment_confirm'); ?>')">
  187. <?php echo t('delete'); ?>
  188. </a>
  189. </div>
  190. </div>
  191. <div class="comment-content">
  192. <p><?php echo nl2br(htmlspecialchars($comment_item['content'])); ?></p>
  193. </div>
  194. <div class="comment-publication">
  195. <small>
  196. <?php echo t('on_publication'); ?>:
  197. <a href="../public/publication.php?id=<?php echo $comment_item['publication_id']; ?>" target="_blank">
  198. <?php echo htmlspecialchars($comment_item['publication_title']); ?>
  199. </a>
  200. </small>
  201. </div>
  202. <!-- Reply Form (hidden by default) -->
  203. <div class="reply-form-container" id="reply-form-<?php echo $comment_item['id']; ?>" style="display: none;">
  204. <form method="post" class="reply-form">
  205. <input type="hidden" name="comment_id" value="<?php echo $comment_item['id']; ?>">
  206. <div class="form-group">
  207. <label><?php echo t('admin_reply'); ?>:</label>
  208. <textarea name="reply_content" rows="3" required placeholder="<?php echo t('enter_reply'); ?>"></textarea>
  209. </div>
  210. <div class="form-actions">
  211. <button type="submit" class="btn btn-primary"><?php echo t('submit_reply'); ?></button>
  212. <button type="button" class="btn btn-secondary cancel-reply"><?php echo t('cancel'); ?></button>
  213. </div>
  214. </form>
  215. </div>
  216. <!-- Show replies -->
  217. <?php if ($comment_item['parent_id']): ?>
  218. <div class="reply-indicator">
  219. <small><?php echo t('reply_to_comment'); ?></small>
  220. </div>
  221. <?php endif; ?>
  222. </div>
  223. <?php endforeach; ?>
  224. <?php endif; ?>
  225. </div>
  226. <!-- Pagination -->
  227. <?php if ($totalPages > 1): ?>
  228. <div class="pagination">
  229. <?php if ($page > 1): ?>
  230. <a href="?page=<?php echo $page - 1; ?><?php echo $status ? '&status=' . $status : ''; ?>"
  231. class="btn btn-secondary"><?php echo t('previous'); ?></a>
  232. <?php endif; ?>
  233. <span class="page-info">
  234. <?php echo t('page'); ?> <?php echo $page; ?> <?php echo t('of'); ?> <?php echo $totalPages; ?>
  235. </span>
  236. <?php if ($page < $totalPages): ?>
  237. <a href="?page=<?php echo $page + 1; ?><?php echo $status ? '&status=' . $status : ''; ?>"
  238. class="btn btn-secondary"><?php echo t('next'); ?></a>
  239. <?php endif; ?>
  240. </div>
  241. <?php endif; ?>
  242. </main>
  243. </div>
  244. <script>
  245. // Admin Comments JavaScript
  246. document.addEventListener('DOMContentLoaded', function() {
  247. // Handle reply buttons
  248. document.querySelectorAll('.reply-btn').forEach(button => {
  249. button.addEventListener('click', function() {
  250. const commentId = this.dataset.commentId;
  251. const replyForm = document.getElementById('reply-form-' + commentId);
  252. // Hide all other reply forms
  253. document.querySelectorAll('.reply-form-container').forEach(form => {
  254. if (form.id !== 'reply-form-' + commentId) {
  255. form.style.display = 'none';
  256. }
  257. });
  258. // Toggle this reply form
  259. replyForm.style.display = replyForm.style.display === 'none' ? 'block' : 'none';
  260. // Focus on textarea if showing
  261. if (replyForm.style.display === 'block') {
  262. replyForm.querySelector('textarea').focus();
  263. }
  264. });
  265. });
  266. // Handle cancel reply buttons
  267. document.querySelectorAll('.cancel-reply').forEach(button => {
  268. button.addEventListener('click', function() {
  269. this.closest('.reply-form-container').style.display = 'none';
  270. });
  271. });
  272. });
  273. </script>
  274. </body>
  275. </html>