@@ -506,7 +506,7 @@ function handleDocuments($method, $id, $db) {
|
|||||||
$typeLabels = ['deployment' => 'Deployment', 'attack' => 'Attack', 'incident-report' => 'Incident Report', 'remediation' => 'Remediation', 'exercise' => 'Exercise'];
|
$typeLabels = ['deployment' => 'Deployment', 'attack' => 'Attack', 'incident-report' => 'Incident Report', 'remediation' => 'Remediation', 'exercise' => 'Exercise'];
|
||||||
$typeLabel = $typeLabels[$docType] ?? ucfirst($docType);
|
$typeLabel = $typeLabels[$docType] ?? ucfirst($docType);
|
||||||
$eventTitle = $typeLabel . ': ' . $data['title'];
|
$eventTitle = $typeLabel . ': ' . $data['title'];
|
||||||
$eventDesc = $username . ' created document "' . $data['title'] . '" (' . $typeLabel . ')';
|
$eventDesc = $username . ' created [doc:' . $docId . ']' . $data['title'] . '[/doc] (' . $typeLabel . ')';
|
||||||
$stmt2 = $db->prepare("
|
$stmt2 = $db->prepare("
|
||||||
INSERT INTO events (team_id, title, description, severity, event_type, occurred_at)
|
INSERT INTO events (team_id, title, description, severity, event_type, occurred_at)
|
||||||
VALUES (?, ?, ?, 'info', 'document', ?)
|
VALUES (?, ?, ?, 'info', 'document', ?)
|
||||||
@@ -538,7 +538,7 @@ function handleDocuments($method, $id, $db) {
|
|||||||
$typeLabels = ['deployment' => 'Deployment', 'attack' => 'Attack', 'incident-report' => 'Incident Report', 'remediation' => 'Remediation', 'exercise' => 'Exercise'];
|
$typeLabels = ['deployment' => 'Deployment', 'attack' => 'Attack', 'incident-report' => 'Incident Report', 'remediation' => 'Remediation', 'exercise' => 'Exercise'];
|
||||||
$typeLabel = $typeLabels[$docType] ?? ucfirst($docType);
|
$typeLabel = $typeLabels[$docType] ?? ucfirst($docType);
|
||||||
$eventTitle = 'Updated ' . $typeLabel . ': ' . $docTitle;
|
$eventTitle = 'Updated ' . $typeLabel . ': ' . $docTitle;
|
||||||
$eventDesc = $username . ' updated document "' . $docTitle . '" (' . $typeLabel . ')';
|
$eventDesc = $username . ' updated [doc:' . $id . ']' . $docTitle . '[/doc] (' . $typeLabel . ')';
|
||||||
$stmt2 = $db->prepare("
|
$stmt2 = $db->prepare("
|
||||||
INSERT INTO events (team_id, title, description, severity, event_type, occurred_at)
|
INSERT INTO events (team_id, title, description, severity, event_type, occurred_at)
|
||||||
VALUES (?, ?, ?, 'info', 'document', ?)
|
VALUES (?, ?, ?, 'info', 'document', ?)
|
||||||
|
|||||||
@@ -268,7 +268,7 @@ function renderTimeline() {
|
|||||||
<small class="event-meta">${date}</small>
|
<small class="event-meta">${date}</small>
|
||||||
</div>
|
</div>
|
||||||
<h6 class="event-title mt-1 mb-1">${esc(e.title)}</h6>
|
<h6 class="event-title mt-1 mb-1">${esc(e.title)}</h6>
|
||||||
${e.description ? '<p class="mb-1 small text-secondary">' + esc(e.description) + '</p>' : ''}
|
${e.description ? '<p class="mb-1 small text-secondary">' + renderDocLinks(e.description) + '</p>' : ''}
|
||||||
<div class="text-end">
|
<div class="text-end">
|
||||||
<button class="btn btn-outline-danger btn-sm py-0 px-1" onclick="deleteEvent(${e.id}, this)" title="Delete event" style="font-size:.7rem;"><i class="fas fa-trash"></i></button>
|
<button class="btn btn-outline-danger btn-sm py-0 px-1" onclick="deleteEvent(${e.id}, this)" title="Delete event" style="font-size:.7rem;"><i class="fas fa-trash"></i></button>
|
||||||
</div>
|
</div>
|
||||||
@@ -286,6 +286,58 @@ function renderTimeline() {
|
|||||||
}).join('');
|
}).join('');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function renderDocLinks(text) {
|
||||||
|
const parts = text.split(/(\[doc:\d+\].*?\[\/doc\])/g);
|
||||||
|
return parts.map(p => {
|
||||||
|
const m = p.match(/\[doc:(\d+)\](.*?)\[\/doc\]/);
|
||||||
|
if (m) {
|
||||||
|
return `<a href="#" class="doc-link" onclick="event.preventDefault();openDocument(${m[1]})" style="color:var(--neptune-accent);text-decoration:underline;cursor:pointer;">${esc(m[2])}</a>`;
|
||||||
|
}
|
||||||
|
return esc(p);
|
||||||
|
}).join('');
|
||||||
|
}
|
||||||
|
|
||||||
|
function openDocument(id) {
|
||||||
|
const tab = document.getElementById('documents-tab');
|
||||||
|
if (tab) tab.click();
|
||||||
|
setTimeout(() => {
|
||||||
|
const container = document.getElementById('documentContainer');
|
||||||
|
if (!container) return;
|
||||||
|
const cards = container.querySelectorAll('.doc-card');
|
||||||
|
cards.forEach(c => {
|
||||||
|
c.style.transition = 'box-shadow .3s, border-color .3s';
|
||||||
|
c.style.boxShadow = '';
|
||||||
|
c.style.borderColor = '';
|
||||||
|
});
|
||||||
|
const target = container.querySelector(`[data-doc-id="${id}"]`);
|
||||||
|
if (target) {
|
||||||
|
target.scrollIntoView({ behavior: 'smooth', block: 'center' });
|
||||||
|
target.style.boxShadow = '0 0 20px rgba(59,130,246,.5)';
|
||||||
|
target.style.borderColor = 'var(--neptune-accent)';
|
||||||
|
setTimeout(() => {
|
||||||
|
target.style.boxShadow = '';
|
||||||
|
target.style.borderColor = '';
|
||||||
|
}, 3000);
|
||||||
|
} else {
|
||||||
|
loadDocuments().then(() => {
|
||||||
|
setTimeout(() => {
|
||||||
|
const el = document.getElementById('documentContainer').querySelector(`[data-doc-id="${id}"]`);
|
||||||
|
if (el) {
|
||||||
|
el.scrollIntoView({ behavior: 'smooth', block: 'center' });
|
||||||
|
el.style.boxShadow = '0 0 20px rgba(59,130,246,.5)';
|
||||||
|
el.style.borderColor = 'var(--neptune-accent)';
|
||||||
|
setTimeout(() => { el.style.boxShadow = ''; el.style.borderColor = ''; }, 3000);
|
||||||
|
}
|
||||||
|
}, 100);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
const filter = document.getElementById('searchDocs');
|
||||||
|
if (filter) filter.value = '';
|
||||||
|
document.getElementById('docTeamFilter').value = '';
|
||||||
|
document.getElementById('docTypeFilter').value = '';
|
||||||
|
}, 300);
|
||||||
|
}
|
||||||
|
|
||||||
function renderComments(event) {
|
function renderComments(event) {
|
||||||
if (!event.comments || !event.comments.length) return '<div class="text-secondary small py-1"><i class="fas fa-comment me-1"></i>No comments yet</div>';
|
if (!event.comments || !event.comments.length) return '<div class="text-secondary small py-1"><i class="fas fa-comment me-1"></i>No comments yet</div>';
|
||||||
return event.comments.map(c => `
|
return event.comments.map(c => `
|
||||||
@@ -1030,7 +1082,7 @@ function renderDocuments() {
|
|||||||
const contentPreview = d.content ? d.content.substring(0, 150) + (d.content.length > 150 ? '...' : '') : '';
|
const contentPreview = d.content ? d.content.substring(0, 150) + (d.content.length > 150 ? '...' : '') : '';
|
||||||
return `
|
return `
|
||||||
<div class="col-md-6 col-lg-4 mb-3">
|
<div class="col-md-6 col-lg-4 mb-3">
|
||||||
<div class="card bg-dark border-secondary h-100 doc-card">
|
<div class="card bg-dark border-secondary h-100 doc-card" data-doc-id="${d.id}">
|
||||||
<div class="card-header border-secondary py-2 d-flex justify-content-between align-items-center">
|
<div class="card-header border-secondary py-2 d-flex justify-content-between align-items-center">
|
||||||
<div>
|
<div>
|
||||||
<span class="badge me-1" style="background:${meta.color}20;color:${meta.color};border:1px solid ${meta.color}40;">
|
<span class="badge me-1" style="background:${meta.color}20;color:${meta.color};border:1px solid ${meta.color}40;">
|
||||||
|
|||||||
Reference in New Issue
Block a user