comments.php 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306
  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="users.php" class="nav-link"><?php echo t('manage_users'); ?></a>
  107. <a href="comments.php" class="nav-link active"><?php echo t('admin_nav_comments'); ?></a>
  108. <?php if (LDAP_ENABLED): ?>
  109. <a href="ldap-users.php" class="nav-link"><?php echo t('admin_nav_ldap_users'); ?></a>
  110. <?php endif; ?>
  111. <a href="logout.php" class="nav-link"><?php echo t('admin_nav_logout'); ?></a>
  112. </nav>
  113. <div class="user-info">
  114. <?php echo t('welcome'); ?>, <?php echo htmlspecialchars($user['username']); ?>
  115. </div>
  116. <?php echo Translation::getLanguageSwitcher('comments.php'); ?>
  117. </div>
  118. </header>
  119. <main class="admin-main">
  120. <div class="page-header">
  121. <h2><?php echo t('manage_comments'); ?></h2>
  122. <div class="header-stats">
  123. <span class="stat-item">
  124. <?php echo t('pending_comments'); ?>: <strong><?php echo $comment->getPendingCount(); ?></strong>
  125. </span>
  126. <span class="stat-item">
  127. <?php echo t('total_comments'); ?>: <strong><?php echo $comment->getCountByStatus(); ?></strong>
  128. </span>
  129. </div>
  130. </div>
  131. <?php if ($message): ?>
  132. <div class="alert alert-<?php echo strpos($message, 'Error') === false ? 'success' : 'error'; ?>">
  133. <?php echo $message; ?>
  134. </div>
  135. <?php endif; ?>
  136. <!-- Status Filter -->
  137. <div class="filter-section">
  138. <form method="get" class="filter-form">
  139. <label for="status"><?php echo t('filter_by_status'); ?>:</label>
  140. <select name="status" id="status" onchange="this.form.submit()">
  141. <option value=""><?php echo t('all_comments'); ?></option>
  142. <option value="pending" <?php echo $status === 'pending' ? 'selected' : ''; ?>><?php echo t('pending'); ?></option>
  143. <option value="approved" <?php echo $status === 'approved' ? 'selected' : ''; ?>><?php echo t('approved'); ?></option>
  144. <option value="rejected" <?php echo $status === 'rejected' ? 'selected' : ''; ?>><?php echo t('rejected'); ?></option>
  145. </select>
  146. </form>
  147. </div>
  148. <!-- Comments List -->
  149. <div class="comments-admin-list">
  150. <?php if (empty($comments)): ?>
  151. <p class="no-comments"><?php echo t('no_comments_found'); ?></p>
  152. <?php else: ?>
  153. <?php foreach ($comments as $comment_item): ?>
  154. <div class="comment-admin-item <?php echo $comment_item['status']; ?>">
  155. <div class="comment-header">
  156. <div class="comment-meta">
  157. <span class="comment-author">
  158. <strong><?php echo htmlspecialchars($comment_item['name']); ?></strong>
  159. <?php if ($comment_item['email']): ?>
  160. <small>(<?php echo htmlspecialchars($comment_item['email']); ?>)</small>
  161. <?php endif; ?>
  162. </span>
  163. <span class="comment-date">
  164. <?php echo date('M j, Y g:i A', strtotime($comment_item['created_at'])); ?>
  165. </span>
  166. <span class="comment-status status-<?php echo $comment_item['status']; ?>">
  167. <?php echo t($comment_item['status']); ?>
  168. </span>
  169. </div>
  170. <div class="comment-actions">
  171. <?php if ($comment_item['status'] === 'pending'): ?>
  172. <a href="comments.php?action=approve&id=<?php echo $comment_item['id']; ?>"
  173. class="btn btn-sm btn-success"><?php echo t('approve'); ?></a>
  174. <?php endif; ?>
  175. <?php if ($comment_item['status'] !== 'rejected'): ?>
  176. <a href="comments.php?action=reject&id=<?php echo $comment_item['id']; ?>"
  177. class="btn btn-sm btn-warning"><?php echo t('reject'); ?></a>
  178. <?php endif; ?>
  179. <button type="button" class="btn btn-sm btn-primary reply-btn"
  180. data-comment-id="<?php echo $comment_item['id']; ?>">
  181. <?php echo t('reply'); ?>
  182. </button>
  183. <a href="comments.php?action=delete&id=<?php echo $comment_item['id']; ?>"
  184. class="btn btn-sm btn-danger"
  185. onclick="return confirm('<?php echo t('admin_delete_comment_confirm'); ?>')">
  186. <?php echo t('delete'); ?>
  187. </a>
  188. </div>
  189. </div>
  190. <div class="comment-content">
  191. <p><?php echo nl2br(htmlspecialchars($comment_item['content'])); ?></p>
  192. </div>
  193. <div class="comment-publication">
  194. <small>
  195. <?php echo t('on_publication'); ?>:
  196. <a href="../public/publication.php?id=<?php echo $comment_item['publication_id']; ?>" target="_blank">
  197. <?php echo htmlspecialchars($comment_item['publication_title']); ?>
  198. </a>
  199. </small>
  200. </div>
  201. <!-- Reply Form (hidden by default) -->
  202. <div class="reply-form-container" id="reply-form-<?php echo $comment_item['id']; ?>" style="display: none;">
  203. <form method="post" class="reply-form">
  204. <input type="hidden" name="comment_id" value="<?php echo $comment_item['id']; ?>">
  205. <div class="form-group">
  206. <label><?php echo t('admin_reply'); ?>:</label>
  207. <textarea name="reply_content" rows="3" required placeholder="<?php echo t('enter_reply'); ?>"></textarea>
  208. </div>
  209. <div class="form-actions">
  210. <button type="submit" class="btn btn-primary"><?php echo t('submit_reply'); ?></button>
  211. <button type="button" class="btn btn-secondary cancel-reply"><?php echo t('cancel'); ?></button>
  212. </div>
  213. </form>
  214. </div>
  215. <!-- Show replies -->
  216. <?php if ($comment_item['parent_id']): ?>
  217. <div class="reply-indicator">
  218. <small><?php echo t('reply_to_comment'); ?></small>
  219. </div>
  220. <?php endif; ?>
  221. </div>
  222. <?php endforeach; ?>
  223. <?php endif; ?>
  224. </div>
  225. <!-- Pagination -->
  226. <?php if ($totalPages > 1): ?>
  227. <div class="pagination">
  228. <?php if ($page > 1): ?>
  229. <a href="?page=<?php echo $page - 1; ?><?php echo $status ? '&status=' . $status : ''; ?>"
  230. class="btn btn-secondary"><?php echo t('previous'); ?></a>
  231. <?php endif; ?>
  232. <span class="page-info">
  233. <?php echo t('page'); ?> <?php echo $page; ?> <?php echo t('of'); ?> <?php echo $totalPages; ?>
  234. </span>
  235. <?php if ($page < $totalPages): ?>
  236. <a href="?page=<?php echo $page + 1; ?><?php echo $status ? '&status=' . $status : ''; ?>"
  237. class="btn btn-secondary"><?php echo t('next'); ?></a>
  238. <?php endif; ?>
  239. </div>
  240. <?php endif; ?>
  241. </main>
  242. </div>
  243. <script>
  244. // Admin Comments JavaScript
  245. document.addEventListener('DOMContentLoaded', function() {
  246. // Handle reply buttons
  247. document.querySelectorAll('.reply-btn').forEach(button => {
  248. button.addEventListener('click', function() {
  249. const commentId = this.dataset.commentId;
  250. const replyForm = document.getElementById('reply-form-' + commentId);
  251. // Hide all other reply forms
  252. document.querySelectorAll('.reply-form-container').forEach(form => {
  253. if (form.id !== 'reply-form-' + commentId) {
  254. form.style.display = 'none';
  255. }
  256. });
  257. // Toggle this reply form
  258. replyForm.style.display = replyForm.style.display === 'none' ? 'block' : 'none';
  259. // Focus on textarea if showing
  260. if (replyForm.style.display === 'block') {
  261. replyForm.querySelector('textarea').focus();
  262. }
  263. });
  264. });
  265. // Handle cancel reply buttons
  266. document.querySelectorAll('.cancel-reply').forEach(button => {
  267. button.addEventListener('click', function() {
  268. this.closest('.reply-form-container').style.display = 'none';
  269. });
  270. });
  271. });
  272. </script>
  273. </body>
  274. </html>