Add comment threading and fix fetch authentication
- Add comment threading/reply functionality with nested display - Database migration for parent_comment_id and thread_depth columns - Recursive comment rendering with depth-based indentation - Reply form with inline UI and smooth animations - Thread collapse/expand capability - Max thread depth of 3 levels - Fix 401 authentication errors on API calls - Add credentials: 'same-origin' to all fetch calls - Affects settings.js, ticket.js, dashboard.js, advanced-search.js - Ensures session cookies are sent with requests - Enhanced comment styling - Thread connector lines for visual hierarchy - Reply button on comments (up to depth 3) - Quote block styling for replies Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -331,42 +331,76 @@ $nonce = SecurityHeadersMiddleware::getNonce();
|
||||
$currentUserId = $GLOBALS['currentUser']['user_id'] ?? null;
|
||||
$isAdmin = $GLOBALS['currentUser']['is_admin'] ?? false;
|
||||
|
||||
foreach ($comments as $comment) {
|
||||
// Use display_name_formatted which falls back appropriately
|
||||
// Recursive function to render threaded comments
|
||||
function renderComment($comment, $currentUserId, $isAdmin, $depth = 0) {
|
||||
$displayName = $comment['display_name_formatted'] ?? $comment['user_name'] ?? 'Unknown User';
|
||||
$commentId = $comment['comment_id'];
|
||||
$isOwner = ($comment['user_id'] == $currentUserId);
|
||||
$canModify = $isOwner || $isAdmin;
|
||||
$markdownEnabled = $comment['markdown_enabled'] ? 1 : 0;
|
||||
$threadDepth = $comment['thread_depth'] ?? $depth;
|
||||
$parentId = $comment['parent_comment_id'] ?? null;
|
||||
|
||||
echo "<div class='comment' data-comment-id='{$commentId}' data-markdown-enabled='{$markdownEnabled}'>";
|
||||
$depthClass = 'thread-depth-' . min($threadDepth, 3);
|
||||
$threadClass = $parentId ? 'comment-reply' : 'comment-root';
|
||||
|
||||
echo "<div class='comment {$depthClass} {$threadClass}' data-comment-id='{$commentId}' data-markdown-enabled='{$markdownEnabled}' data-thread-depth='{$threadDepth}' data-parent-id='" . ($parentId ?? '') . "'>";
|
||||
|
||||
// Thread connector line for replies
|
||||
if ($parentId) {
|
||||
echo "<div class='thread-line'></div>";
|
||||
}
|
||||
|
||||
echo "<div class='comment-content'>";
|
||||
echo "<div class='comment-header'>";
|
||||
echo "<span class='comment-user'>" . htmlspecialchars($displayName) . "</span>";
|
||||
$dateStr = date('M d, Y H:i', strtotime($comment['created_at']));
|
||||
$editedIndicator = !empty($comment['updated_at']) ? ' <span class="comment-edited">(edited)</span>' : '';
|
||||
echo "<span class='comment-date'>{$dateStr}{$editedIndicator}</span>";
|
||||
|
||||
// Action buttons
|
||||
echo "<div class='comment-actions'>";
|
||||
// Reply button (max depth of 3)
|
||||
if ($threadDepth < 3) {
|
||||
echo "<button type='button' class='comment-action-btn reply-btn' data-action='reply-comment' data-comment-id='{$commentId}' data-user='" . htmlspecialchars($displayName) . "' title='Reply'>↩</button>";
|
||||
}
|
||||
// Edit/Delete buttons for owner or admin
|
||||
if ($canModify) {
|
||||
echo "<div class='comment-actions'>";
|
||||
echo "<button type='button' class='comment-action-btn edit-btn' data-action='edit-comment' data-comment-id='{$commentId}' title='Edit comment'>✏️</button>";
|
||||
echo "<button type='button' class='comment-action-btn delete-btn' data-action='delete-comment' data-comment-id='{$commentId}' title='Delete comment'>🗑️</button>";
|
||||
echo "</div>";
|
||||
echo "<button type='button' class='comment-action-btn edit-btn' data-action='edit-comment' data-comment-id='{$commentId}' title='Edit'>✏️</button>";
|
||||
echo "<button type='button' class='comment-action-btn delete-btn' data-action='delete-comment' data-comment-id='{$commentId}' title='Delete'>🗑️</button>";
|
||||
}
|
||||
|
||||
echo "</div>";
|
||||
|
||||
echo "</div>"; // .comment-header
|
||||
|
||||
echo "<div class='comment-text' id='comment-text-{$commentId}' " . ($comment['markdown_enabled'] ? "data-markdown" : "") . ">";
|
||||
if ($comment['markdown_enabled']) {
|
||||
// Markdown will be rendered by JavaScript
|
||||
echo htmlspecialchars($comment['comment_text']);
|
||||
} else {
|
||||
// For non-markdown comments, convert line breaks to <br> and escape HTML
|
||||
echo nl2br(htmlspecialchars($comment['comment_text']));
|
||||
}
|
||||
echo "</div>";
|
||||
|
||||
// Hidden raw text for editing
|
||||
echo "<textarea class='comment-edit-raw' id='comment-raw-{$commentId}' style='display:none;'>" . htmlspecialchars($comment['comment_text']) . "</textarea>";
|
||||
echo "</div>";
|
||||
|
||||
echo "</div>"; // .comment-content
|
||||
|
||||
// Render replies recursively
|
||||
if (!empty($comment['replies'])) {
|
||||
echo "<div class='comment-replies'>";
|
||||
foreach ($comment['replies'] as $reply) {
|
||||
renderComment($reply, $currentUserId, $isAdmin, $threadDepth + 1);
|
||||
}
|
||||
echo "</div>";
|
||||
}
|
||||
|
||||
echo "</div>"; // .comment
|
||||
}
|
||||
|
||||
// Render all comments
|
||||
foreach ($comments as $comment) {
|
||||
renderComment($comment, $currentUserId, $isAdmin);
|
||||
}
|
||||
?>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user