2025-05-16 20:02:49 -04:00
|
|
|
<?php
|
|
|
|
|
class CommentModel {
|
|
|
|
|
private $conn;
|
Implement comprehensive improvement plan (Phases 1-6)
Security (Phase 1-2):
- Add SecurityHeadersMiddleware with CSP, X-Frame-Options, etc.
- Add RateLimitMiddleware for API rate limiting
- Add security event logging to AuditLogModel
- Add ResponseHelper for standardized API responses
- Update config.php with security constants
Database (Phase 3):
- Add migration 014 for additional indexes
- Add migration 015 for ticket dependencies
- Add migration 016 for ticket attachments
- Add migration 017 for recurring tickets
- Add migration 018 for custom fields
Features (Phase 4-5):
- Add ticket dependencies with DependencyModel and API
- Add duplicate detection with check_duplicates API
- Add file attachments with AttachmentModel and upload/download APIs
- Add @mentions with autocomplete and highlighting
- Add quick actions on dashboard rows
Collaboration (Phase 5):
- Add mention extraction in CommentModel
- Add mention autocomplete dropdown in ticket.js
- Add mention highlighting CSS styles
Admin & Export (Phase 6):
- Add StatsModel for dashboard widgets
- Add dashboard stats cards (open, critical, unassigned, etc.)
- Add CSV/JSON export via export_tickets API
- Add rich text editor toolbar in markdown.js
- Add RecurringTicketModel with cron job
- Add CustomFieldModel for per-category fields
- Add admin views: RecurringTickets, CustomFields, Workflow,
Templates, AuditLog, UserActivity
- Add admin APIs: manage_workflows, manage_templates,
manage_recurring, custom_fields, get_users
- Add admin routes in index.php
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-20 09:55:01 -05:00
|
|
|
|
2025-05-16 20:02:49 -04:00
|
|
|
public function __construct($conn) {
|
|
|
|
|
$this->conn = $conn;
|
|
|
|
|
}
|
Implement comprehensive improvement plan (Phases 1-6)
Security (Phase 1-2):
- Add SecurityHeadersMiddleware with CSP, X-Frame-Options, etc.
- Add RateLimitMiddleware for API rate limiting
- Add security event logging to AuditLogModel
- Add ResponseHelper for standardized API responses
- Update config.php with security constants
Database (Phase 3):
- Add migration 014 for additional indexes
- Add migration 015 for ticket dependencies
- Add migration 016 for ticket attachments
- Add migration 017 for recurring tickets
- Add migration 018 for custom fields
Features (Phase 4-5):
- Add ticket dependencies with DependencyModel and API
- Add duplicate detection with check_duplicates API
- Add file attachments with AttachmentModel and upload/download APIs
- Add @mentions with autocomplete and highlighting
- Add quick actions on dashboard rows
Collaboration (Phase 5):
- Add mention extraction in CommentModel
- Add mention autocomplete dropdown in ticket.js
- Add mention highlighting CSS styles
Admin & Export (Phase 6):
- Add StatsModel for dashboard widgets
- Add dashboard stats cards (open, critical, unassigned, etc.)
- Add CSV/JSON export via export_tickets API
- Add rich text editor toolbar in markdown.js
- Add RecurringTicketModel with cron job
- Add CustomFieldModel for per-category fields
- Add admin views: RecurringTickets, CustomFields, Workflow,
Templates, AuditLog, UserActivity
- Add admin APIs: manage_workflows, manage_templates,
manage_recurring, custom_fields, get_users
- Add admin routes in index.php
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-20 09:55:01 -05:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Extract @mentions from comment text
|
|
|
|
|
*
|
|
|
|
|
* @param string $text Comment text
|
|
|
|
|
* @return array Array of mentioned usernames
|
|
|
|
|
*/
|
|
|
|
|
public function extractMentions($text) {
|
|
|
|
|
$mentions = [];
|
|
|
|
|
// Match @username patterns (alphanumeric, underscores, hyphens)
|
|
|
|
|
if (preg_match_all('/@([a-zA-Z0-9_-]+)/', $text, $matches)) {
|
|
|
|
|
$mentions = array_unique($matches[1]);
|
|
|
|
|
}
|
|
|
|
|
return $mentions;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Get user IDs for mentioned usernames
|
|
|
|
|
*
|
|
|
|
|
* @param array $usernames Array of usernames
|
|
|
|
|
* @return array Array of user records with user_id, username, display_name
|
|
|
|
|
*/
|
|
|
|
|
public function getMentionedUsers($usernames) {
|
|
|
|
|
if (empty($usernames)) {
|
|
|
|
|
return [];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$placeholders = str_repeat('?,', count($usernames) - 1) . '?';
|
|
|
|
|
$sql = "SELECT user_id, username, display_name FROM users WHERE username IN ($placeholders)";
|
|
|
|
|
$stmt = $this->conn->prepare($sql);
|
|
|
|
|
|
|
|
|
|
$types = str_repeat('s', count($usernames));
|
|
|
|
|
$stmt->bind_param($types, ...$usernames);
|
|
|
|
|
$stmt->execute();
|
|
|
|
|
$result = $stmt->get_result();
|
|
|
|
|
|
|
|
|
|
$users = [];
|
|
|
|
|
while ($row = $result->fetch_assoc()) {
|
|
|
|
|
$users[] = $row;
|
|
|
|
|
}
|
|
|
|
|
$stmt->close();
|
|
|
|
|
|
|
|
|
|
return $users;
|
|
|
|
|
}
|
2025-05-16 20:02:49 -04:00
|
|
|
|
|
|
|
|
public function getCommentsByTicketId($ticketId) {
|
2026-01-01 15:40:32 -05:00
|
|
|
$sql = "SELECT tc.*, u.display_name, u.username
|
|
|
|
|
FROM ticket_comments tc
|
|
|
|
|
LEFT JOIN users u ON tc.user_id = u.user_id
|
|
|
|
|
WHERE tc.ticket_id = ?
|
|
|
|
|
ORDER BY tc.created_at DESC";
|
2025-05-16 20:02:49 -04:00
|
|
|
$stmt = $this->conn->prepare($sql);
|
2025-09-05 11:08:56 -04:00
|
|
|
$stmt->bind_param("s", $ticketId); // Changed to string since ticket_id is varchar
|
2025-05-16 20:02:49 -04:00
|
|
|
$stmt->execute();
|
|
|
|
|
$result = $stmt->get_result();
|
2026-01-01 15:40:32 -05:00
|
|
|
|
2025-05-16 20:02:49 -04:00
|
|
|
$comments = [];
|
|
|
|
|
while ($row = $result->fetch_assoc()) {
|
2026-01-01 15:40:32 -05:00
|
|
|
// Use display_name from users table if available, fallback to user_name field
|
|
|
|
|
if (!empty($row['display_name'])) {
|
|
|
|
|
$row['display_name_formatted'] = $row['display_name'];
|
|
|
|
|
} else {
|
|
|
|
|
$row['display_name_formatted'] = $row['user_name'] ?? 'Unknown User';
|
|
|
|
|
}
|
2025-05-16 20:02:49 -04:00
|
|
|
$comments[] = $row;
|
|
|
|
|
}
|
2026-01-01 15:40:32 -05:00
|
|
|
|
2025-05-16 20:02:49 -04:00
|
|
|
return $comments;
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-01 15:40:32 -05:00
|
|
|
public function addComment($ticketId, $commentData, $userId = null) {
|
|
|
|
|
$sql = "INSERT INTO ticket_comments (ticket_id, user_id, user_name, comment_text, markdown_enabled)
|
|
|
|
|
VALUES (?, ?, ?, ?, ?)";
|
|
|
|
|
|
2025-05-16 20:02:49 -04:00
|
|
|
$stmt = $this->conn->prepare($sql);
|
2026-01-01 15:40:32 -05:00
|
|
|
|
|
|
|
|
// Set default username (kept for backward compatibility)
|
2025-05-16 20:02:49 -04:00
|
|
|
$username = $commentData['user_name'] ?? 'User';
|
|
|
|
|
$markdownEnabled = isset($commentData['markdown_enabled']) && $commentData['markdown_enabled'] ? 1 : 0;
|
2026-01-01 15:40:32 -05:00
|
|
|
|
2025-09-05 11:08:56 -04:00
|
|
|
// Preserve line breaks in the comment text
|
|
|
|
|
$commentText = $commentData['comment_text'];
|
2026-01-01 15:40:32 -05:00
|
|
|
|
2025-05-16 20:02:49 -04:00
|
|
|
$stmt->bind_param(
|
2026-01-01 15:40:32 -05:00
|
|
|
"sissi",
|
2025-05-16 20:02:49 -04:00
|
|
|
$ticketId,
|
2026-01-01 15:40:32 -05:00
|
|
|
$userId,
|
2025-05-16 20:02:49 -04:00
|
|
|
$username,
|
2025-09-05 11:08:56 -04:00
|
|
|
$commentText,
|
2025-05-16 20:02:49 -04:00
|
|
|
$markdownEnabled
|
|
|
|
|
);
|
2026-01-01 15:40:32 -05:00
|
|
|
|
2025-05-16 20:02:49 -04:00
|
|
|
if ($stmt->execute()) {
|
2026-01-01 15:40:32 -05:00
|
|
|
$commentId = $this->conn->insert_id;
|
|
|
|
|
|
2025-05-16 20:02:49 -04:00
|
|
|
return [
|
|
|
|
|
'success' => true,
|
2026-01-01 15:40:32 -05:00
|
|
|
'comment_id' => $commentId,
|
2025-05-16 20:02:49 -04:00
|
|
|
'user_name' => $username,
|
|
|
|
|
'created_at' => date('M d, Y H:i'),
|
2025-09-05 11:08:56 -04:00
|
|
|
'markdown_enabled' => $markdownEnabled,
|
|
|
|
|
'comment_text' => $commentText
|
2025-05-16 20:02:49 -04:00
|
|
|
];
|
|
|
|
|
} else {
|
|
|
|
|
return [
|
|
|
|
|
'success' => false,
|
|
|
|
|
'error' => $this->conn->error
|
|
|
|
|
];
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-09-05 11:08:56 -04:00
|
|
|
}
|
|
|
|
|
?>
|