Fix type safety and TDS class naming issues

- bulk_operation.php: replace is_numeric() with strict int cast+equality to reject scientific notation
- AttachmentModel.php: fix bind_param type strings (s→i for integer ticket IDs)
- CommentModel.php: use strict !== comparison with (int) cast for user_id ownership checks
- ticket.js: replace all non-TDS class names (text-amber→lt-text-amber, btn→lt-btn variants, etc.)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-28 22:29:28 -04:00
parent d8e6dcf7fa
commit 5242d42fa7
4 changed files with 28 additions and 26 deletions
+7 -5
View File
@@ -50,12 +50,14 @@ if (!$operationType || empty($ticketIds)) {
exit; exit;
} }
// Validate ticket IDs are integers // Validate ticket IDs are positive integers
foreach ($ticketIds as $ticketId) { $ticketIds = array_values(array_filter(array_map(function($id) {
if (!is_numeric($ticketId)) { $int = (int)$id;
echo json_encode(['success' => false, 'error' => 'Invalid ticket ID format']); return ($int > 0 && (string)$int === (string)$id) ? $int : null;
}, $ticketIds)));
if (empty($ticketIds)) {
echo json_encode(['success' => false, 'error' => 'No valid ticket IDs provided']);
exit; exit;
}
} }
// Use centralized database connection // Use centralized database connection
+14 -14
View File
@@ -475,10 +475,10 @@ function showDependencyError(message) {
const dependentsList = document.getElementById('dependentsList'); const dependentsList = document.getElementById('dependentsList');
if (dependenciesList) { if (dependenciesList) {
dependenciesList.innerHTML = `<p class="text-amber">${lt.escHtml(message)}</p>`; dependenciesList.innerHTML = `<p class="lt-text-amber">${lt.escHtml(message)}</p>`;
} }
if (dependentsList) { if (dependentsList) {
dependentsList.innerHTML = `<p class="text-amber">${lt.escHtml(message)}</p>`; dependentsList.innerHTML = `<p class="lt-text-amber">${lt.escHtml(message)}</p>`;
} }
} }
@@ -512,7 +512,7 @@ function renderDependencies(dependencies) {
<span class="dependency-title">${lt.escHtml(dep.title)}</span> <span class="dependency-title">${lt.escHtml(dep.title)}</span>
<span class="status-badge ${statusClass}">${lt.escHtml(dep.status)}</span> <span class="status-badge ${statusClass}">${lt.escHtml(dep.status)}</span>
</div> </div>
<button data-action="remove-dependency" data-dependency-id="${dep.dependency_id}" class="btn btn-small">REMOVE</button> <button data-action="remove-dependency" data-dependency-id="${dep.dependency_id}" class="lt-btn lt-btn-sm">REMOVE</button>
</div>`; </div>`;
}); });
@@ -521,7 +521,7 @@ function renderDependencies(dependencies) {
} }
if (!hasAny) { if (!hasAny) {
html = '<p class="text-muted-green">No dependencies configured.</p>'; html = '<p class="lt-text-muted">No dependencies configured.</p>';
} }
container.innerHTML = html; container.innerHTML = html;
@@ -532,7 +532,7 @@ function renderDependents(dependents) {
if (!container) return; if (!container) return;
if (dependents.length === 0) { if (dependents.length === 0) {
container.innerHTML = '<p class="text-muted-green">No tickets depend on this one.</p>'; container.innerHTML = '<p class="lt-text-muted">No tickets depend on this one.</p>';
return; return;
} }
@@ -546,7 +546,7 @@ function renderDependents(dependents) {
</a> </a>
<span class="dependency-title">${lt.escHtml(dep.title)}</span> <span class="dependency-title">${lt.escHtml(dep.title)}</span>
<span class="status-badge ${statusClass}">${lt.escHtml(dep.status)}</span> <span class="status-badge ${statusClass}">${lt.escHtml(dep.status)}</span>
<span class="dependency-title text-amber">(${lt.escHtml(dep.dependency_type)})</span> <span class="dependency-title lt-text-amber">(${lt.escHtml(dep.dependency_type)})</span>
</div> </div>
</div>`; </div>`;
}); });
@@ -756,11 +756,11 @@ function loadAttachments() {
if (data.success) { if (data.success) {
renderAttachments(data.attachments || []); renderAttachments(data.attachments || []);
} else { } else {
container.innerHTML = '<p class="text-muted-green">Error loading attachments.</p>'; container.innerHTML = '<p class="lt-text-muted">Error loading attachments.</p>';
} }
}) })
.catch(error => { .catch(error => {
container.innerHTML = '<p class="text-muted-green">Error loading attachments.</p>'; container.innerHTML = '<p class="lt-text-muted">Error loading attachments.</p>';
}); });
} }
@@ -769,7 +769,7 @@ function renderAttachments(attachments) {
if (!container) return; if (!container) return;
if (attachments.length === 0) { if (attachments.length === 0) {
container.innerHTML = '<p class="text-muted-green">No files attached to this ticket.</p>'; container.innerHTML = '<p class="lt-text-muted">No files attached to this ticket.</p>';
return; return;
} }
@@ -799,8 +799,8 @@ function renderAttachments(attachments) {
</div> </div>
</div> </div>
<div class="attachment-actions"> <div class="attachment-actions">
<a href="/api/download_attachment.php?id=${att.attachment_id}" class="btn btn-small" title="Download">⬇</a> <a href="/api/download_attachment.php?id=${att.attachment_id}" class="lt-btn lt-btn-sm" title="Download">⬇</a>
<button data-action="delete-attachment" data-attachment-id="${att.attachment_id}" class="btn btn-small btn-danger" title="Delete">✕</button> <button data-action="delete-attachment" data-attachment-id="${att.attachment_id}" class="lt-btn lt-btn-sm lt-btn-danger" title="Delete">✕</button>
</div> </div>
</div>`; </div>`;
}); });
@@ -1123,8 +1123,8 @@ function editComment(commentId) {
Markdown Markdown
</label> </label>
<div class="comment-edit-buttons"> <div class="comment-edit-buttons">
<button type="button" class="btn btn-small" data-action="save-edit-comment" data-comment-id="${commentId}">SAVE</button> <button type="button" class="lt-btn lt-btn-sm" data-action="save-edit-comment" data-comment-id="${commentId}">SAVE</button>
<button type="button" class="btn btn-secondary btn-small" data-action="cancel-edit-comment" data-comment-id="${commentId}">CANCEL</button> <button type="button" class="lt-btn lt-btn-ghost lt-btn-sm" data-action="cancel-edit-comment" data-comment-id="${commentId}">CANCEL</button>
</div> </div>
</div> </div>
`; `;
@@ -1279,7 +1279,7 @@ function showReplyForm(commentId, userName) {
<span>Markdown</span> <span>Markdown</span>
</label> </label>
<div class="reply-buttons"> <div class="reply-buttons">
<button type="button" class="btn btn-small" data-action="submit-reply" data-parent-id="${commentId}">REPLY</button> <button type="button" class="lt-btn lt-btn-sm" data-action="submit-reply" data-parent-id="${commentId}">REPLY</button>
</div> </div>
</div> </div>
</div> </div>
+4 -4
View File
@@ -21,7 +21,7 @@ class AttachmentModel {
ORDER BY a.uploaded_at DESC"; ORDER BY a.uploaded_at DESC";
$stmt = $this->conn->prepare($sql); $stmt = $this->conn->prepare($sql);
$stmt->bind_param("s", $ticketId); $stmt->bind_param("i", $ticketId);
$stmt->execute(); $stmt->execute();
$result = $stmt->get_result(); $result = $stmt->get_result();
@@ -61,7 +61,7 @@ class AttachmentModel {
VALUES (?, ?, ?, ?, ?, ?)"; VALUES (?, ?, ?, ?, ?, ?)";
$stmt = $this->conn->prepare($sql); $stmt = $this->conn->prepare($sql);
$stmt->bind_param("sssisi", $ticketId, $filename, $originalFilename, $fileSize, $mimeType, $uploadedBy); $stmt->bind_param("issisi", $ticketId, $filename, $originalFilename, $fileSize, $mimeType, $uploadedBy);
$result = $stmt->execute(); $result = $stmt->execute();
if ($result) { if ($result) {
@@ -97,7 +97,7 @@ class AttachmentModel {
WHERE ticket_id = ?"; WHERE ticket_id = ?";
$stmt = $this->conn->prepare($sql); $stmt = $this->conn->prepare($sql);
$stmt->bind_param("s", $ticketId); $stmt->bind_param("i", $ticketId);
$stmt->execute(); $stmt->execute();
$result = $stmt->get_result(); $result = $stmt->get_result();
$row = $result->fetch_assoc(); $row = $result->fetch_assoc();
@@ -113,7 +113,7 @@ class AttachmentModel {
$sql = "SELECT COUNT(*) as count FROM ticket_attachments WHERE ticket_id = ?"; $sql = "SELECT COUNT(*) as count FROM ticket_attachments WHERE ticket_id = ?";
$stmt = $this->conn->prepare($sql); $stmt = $this->conn->prepare($sql);
$stmt->bind_param("s", $ticketId); $stmt->bind_param("i", $ticketId);
$stmt->execute(); $stmt->execute();
$result = $stmt->get_result(); $result = $stmt->get_result();
$row = $result->fetch_assoc(); $row = $result->fetch_assoc();
+2 -2
View File
@@ -240,7 +240,7 @@ class CommentModel {
return ['success' => false, 'error' => 'Comment not found']; return ['success' => false, 'error' => 'Comment not found'];
} }
if ($comment['user_id'] != $userId && !$isAdmin) { if ($comment['user_id'] !== (int)$userId && !$isAdmin) {
return ['success' => false, 'error' => 'You do not have permission to edit this comment']; return ['success' => false, 'error' => 'You do not have permission to edit this comment'];
} }
@@ -286,7 +286,7 @@ class CommentModel {
return ['success' => false, 'error' => 'Comment not found']; return ['success' => false, 'error' => 'Comment not found'];
} }
if ($comment['user_id'] != $userId && !$isAdmin) { if ($comment['user_id'] !== (int)$userId && !$isAdmin) {
return ['success' => false, 'error' => 'You do not have permission to delete this comment']; return ['success' => false, 'error' => 'You do not have permission to delete this comment'];
} }