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,13 +50,15 @@ if (!$operationType || empty($ticketIds)) {
exit;
}
// Validate ticket IDs are integers
foreach ($ticketIds as $ticketId) {
if (!is_numeric($ticketId)) {
echo json_encode(['success' => false, 'error' => 'Invalid ticket ID format']);
// Validate ticket IDs are positive integers
$ticketIds = array_values(array_filter(array_map(function($id) {
$int = (int)$id;
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;
}
}
// Use centralized database connection
$conn = Database::getConnection();
+14 -14
View File
@@ -475,10 +475,10 @@ function showDependencyError(message) {
const dependentsList = document.getElementById('dependentsList');
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) {
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="status-badge ${statusClass}">${lt.escHtml(dep.status)}</span>
</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>`;
});
@@ -521,7 +521,7 @@ function renderDependencies(dependencies) {
}
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;
@@ -532,7 +532,7 @@ function renderDependents(dependents) {
if (!container) return;
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;
}
@@ -546,7 +546,7 @@ function renderDependents(dependents) {
</a>
<span class="dependency-title">${lt.escHtml(dep.title)}</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>`;
});
@@ -756,11 +756,11 @@ function loadAttachments() {
if (data.success) {
renderAttachments(data.attachments || []);
} 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 => {
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 (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;
}
@@ -799,8 +799,8 @@ function renderAttachments(attachments) {
</div>
</div>
<div class="attachment-actions">
<a href="/api/download_attachment.php?id=${att.attachment_id}" class="btn btn-small" title="Download">⬇</a>
<button data-action="delete-attachment" data-attachment-id="${att.attachment_id}" class="btn btn-small btn-danger" title="Delete">✕</button>
<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="lt-btn lt-btn-sm lt-btn-danger" title="Delete">✕</button>
</div>
</div>`;
});
@@ -1123,8 +1123,8 @@ function editComment(commentId) {
Markdown
</label>
<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="btn btn-secondary btn-small" data-action="cancel-edit-comment" data-comment-id="${commentId}">CANCEL</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="lt-btn lt-btn-ghost lt-btn-sm" data-action="cancel-edit-comment" data-comment-id="${commentId}">CANCEL</button>
</div>
</div>
`;
@@ -1279,7 +1279,7 @@ function showReplyForm(commentId, userName) {
<span>Markdown</span>
</label>
<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>
+4 -4
View File
@@ -21,7 +21,7 @@ class AttachmentModel {
ORDER BY a.uploaded_at DESC";
$stmt = $this->conn->prepare($sql);
$stmt->bind_param("s", $ticketId);
$stmt->bind_param("i", $ticketId);
$stmt->execute();
$result = $stmt->get_result();
@@ -61,7 +61,7 @@ class AttachmentModel {
VALUES (?, ?, ?, ?, ?, ?)";
$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();
if ($result) {
@@ -97,7 +97,7 @@ class AttachmentModel {
WHERE ticket_id = ?";
$stmt = $this->conn->prepare($sql);
$stmt->bind_param("s", $ticketId);
$stmt->bind_param("i", $ticketId);
$stmt->execute();
$result = $stmt->get_result();
$row = $result->fetch_assoc();
@@ -113,7 +113,7 @@ class AttachmentModel {
$sql = "SELECT COUNT(*) as count FROM ticket_attachments WHERE ticket_id = ?";
$stmt = $this->conn->prepare($sql);
$stmt->bind_param("s", $ticketId);
$stmt->bind_param("i", $ticketId);
$stmt->execute();
$result = $stmt->get_result();
$row = $result->fetch_assoc();
+2 -2
View File
@@ -240,7 +240,7 @@ class CommentModel {
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'];
}
@@ -286,7 +286,7 @@ class CommentModel {
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'];
}