Release 0.0.4
This commit is contained in:
@@ -34,7 +34,8 @@
|
|||||||
},
|
},
|
||||||
"Notify": {
|
"Notify": {
|
||||||
"Completed": "Kosmos Storage Audit abgeschlossen: {count} Findings.",
|
"Completed": "Kosmos Storage Audit abgeschlossen: {count} Findings.",
|
||||||
"Failed": "Kosmos Storage Audit fehlgeschlagen: {message}"
|
"Failed": "Kosmos Storage Audit fehlgeschlagen: {message}",
|
||||||
|
"OpenSourceFailed": "Die Quelle konnte nicht geöffnet werden: {uuid}"
|
||||||
},
|
},
|
||||||
"Summary": {
|
"Summary": {
|
||||||
"NoAnalysis": "Noch keine Analyse ausgeführt.",
|
"NoAnalysis": "Noch keine Analyse ausgeführt.",
|
||||||
|
|||||||
@@ -34,7 +34,8 @@
|
|||||||
},
|
},
|
||||||
"Notify": {
|
"Notify": {
|
||||||
"Completed": "Kosmos Storage Audit completed: {count} findings.",
|
"Completed": "Kosmos Storage Audit completed: {count} findings.",
|
||||||
"Failed": "Kosmos Storage Audit failed: {message}"
|
"Failed": "Kosmos Storage Audit failed: {message}",
|
||||||
|
"OpenSourceFailed": "Could not open source: {uuid}"
|
||||||
},
|
},
|
||||||
"Summary": {
|
"Summary": {
|
||||||
"NoAnalysis": "No analysis has been run yet.",
|
"NoAnalysis": "No analysis has been run yet.",
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
"id": "kosmos-storage-audit",
|
"id": "kosmos-storage-audit",
|
||||||
"title": "Kosmos Storage Audit",
|
"title": "Kosmos Storage Audit",
|
||||||
"description": "Analyzes media references and risky storage locations across Foundry data and public roots.",
|
"description": "Analyzes media references and risky storage locations across Foundry data and public roots.",
|
||||||
"version": "0.0.3",
|
"version": "0.0.4",
|
||||||
"compatibility": {
|
"compatibility": {
|
||||||
"minimum": "13",
|
"minimum": "13",
|
||||||
"verified": "13"
|
"verified": "13"
|
||||||
|
|||||||
@@ -37,6 +37,8 @@ function worldCollectionEntries() {
|
|||||||
subtype: doc.documentName?.toLowerCase() ?? collection.documentName?.toLowerCase() ?? "document"
|
subtype: doc.documentName?.toLowerCase() ?? collection.documentName?.toLowerCase() ?? "document"
|
||||||
},
|
},
|
||||||
sourceLabel: `${doc.documentName ?? "Document"} ${doc.id}`,
|
sourceLabel: `${doc.documentName ?? "Document"} ${doc.id}`,
|
||||||
|
sourceName: doc.name ?? null,
|
||||||
|
sourceUuid: doc.uuid ?? null,
|
||||||
value: doc.toObject ? doc.toObject() : doc
|
value: doc.toObject ? doc.toObject() : doc
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -89,6 +91,8 @@ async function* packagePackEntries(onProgress = null) {
|
|||||||
subtype: `pack:${pack.collection}`
|
subtype: `pack:${pack.collection}`
|
||||||
},
|
},
|
||||||
sourceLabel: `${pack.collection} ${document.id}`,
|
sourceLabel: `${pack.collection} ${document.id}`,
|
||||||
|
sourceName: document.name ?? null,
|
||||||
|
sourceUuid: document.uuid ?? null,
|
||||||
value: document.toObject ? document.toObject() : document
|
value: document.toObject ? document.toObject() : document
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,7 +7,8 @@ export class StorageAuditReportApp extends foundry.applications.api.ApplicationV
|
|||||||
runAnalysis: StorageAuditReportApp.#onRunAnalysis,
|
runAnalysis: StorageAuditReportApp.#onRunAnalysis,
|
||||||
toggleShowAll: StorageAuditReportApp.#onToggleShowAll,
|
toggleShowAll: StorageAuditReportApp.#onToggleShowAll,
|
||||||
toggleRaw: StorageAuditReportApp.#onToggleRaw,
|
toggleRaw: StorageAuditReportApp.#onToggleRaw,
|
||||||
exportReport: StorageAuditReportApp.#onExportReport
|
exportReport: StorageAuditReportApp.#onExportReport,
|
||||||
|
openSource: StorageAuditReportApp.#onOpenSource
|
||||||
},
|
},
|
||||||
window: {
|
window: {
|
||||||
title: "Kosmos Storage Audit",
|
title: "Kosmos Storage Audit",
|
||||||
@@ -43,7 +44,7 @@ export class StorageAuditReportApp extends foundry.applications.api.ApplicationV
|
|||||||
progress: this.#progress,
|
progress: this.#progress,
|
||||||
summary: this.#summarize(this.#analysis),
|
summary: this.#summarize(this.#analysis),
|
||||||
groupedFindings,
|
groupedFindings,
|
||||||
findings: visibleFindings.slice(0, 50)
|
findings: visibleFindings
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -197,6 +198,21 @@ export class StorageAuditReportApp extends foundry.applications.api.ApplicationV
|
|||||||
static #onExportReport(_event, _button) {
|
static #onExportReport(_event, _button) {
|
||||||
return this.exportReport();
|
return this.exportReport();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static async #onOpenSource(_event, button) {
|
||||||
|
const uuid = button?.dataset?.uuid;
|
||||||
|
if (!uuid) return;
|
||||||
|
const document = await fromUuid(uuid);
|
||||||
|
if (document?.sheet) {
|
||||||
|
document.sheet.render(true, { focus: true });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (document?.parent?.sheet) {
|
||||||
|
document.parent.sheet.render(true, { focus: true });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ui.notifications.warn(format("KSA.Notify.OpenSourceFailed", { uuid }));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function renderProgress(progress, loading) {
|
function renderProgress(progress, loading) {
|
||||||
@@ -340,7 +356,7 @@ function renderGroupedFindingList(groupedFindings, hasAnalysis, loading, showAll
|
|||||||
}
|
}
|
||||||
|
|
||||||
function renderGroupedSection(title, description, groups, renderGroup) {
|
function renderGroupedSection(title, description, groups, renderGroup) {
|
||||||
const items = groups.slice(0, 12).map(renderGroup).join("");
|
const items = groups.map(renderGroup).join("");
|
||||||
return `
|
return `
|
||||||
<section class="storage-audit__group">
|
<section class="storage-audit__group">
|
||||||
<div class="storage-audit__group-header">
|
<div class="storage-audit__group-header">
|
||||||
@@ -375,7 +391,7 @@ function renderFindingList(findings, hasAnalysis, loading, showAll, showRaw) {
|
|||||||
<p>${escapeHtml(finding.reason)}</p>
|
<p>${escapeHtml(finding.reason)}</p>
|
||||||
<dl>
|
<dl>
|
||||||
<div><dt>${localize("KSA.Field.Target")}</dt><dd><code>${escapeHtml(finding.target.locator ?? `${finding.target.storage}:${finding.target.path}`)}</code></dd></div>
|
<div><dt>${localize("KSA.Field.Target")}</dt><dd><code>${escapeHtml(finding.target.locator ?? `${finding.target.storage}:${finding.target.path}`)}</code></dd></div>
|
||||||
${finding.source ? `<div><dt>${localize("KSA.Field.Source")}</dt><dd>${escapeHtml(finding.source.sourceLabel)}</dd></div>` : ""}
|
${finding.source ? `<div><dt>${localize("KSA.Field.Source")}</dt><dd>${renderSourceLink(finding.source)}</dd></div>` : ""}
|
||||||
</dl>
|
</dl>
|
||||||
<p class="storage-audit__recommendation">${escapeHtml(finding.recommendation)}</p>
|
<p class="storage-audit__recommendation">${escapeHtml(finding.recommendation)}</p>
|
||||||
</article>
|
</article>
|
||||||
@@ -391,7 +407,7 @@ function renderFindingList(findings, hasAnalysis, loading, showAll, showRaw) {
|
|||||||
|
|
||||||
function renderSampleSources(sources) {
|
function renderSampleSources(sources) {
|
||||||
if (!sources.length) return "";
|
if (!sources.length) return "";
|
||||||
const rows = sources.map(source => `<li>${escapeHtml(source)}</li>`).join("");
|
const rows = sources.map(source => `<li>${renderSourceLink(source)}</li>`).join("");
|
||||||
return `<div class="storage-audit__samples"><span>${localize("KSA.Section.Samples")}</span><ul>${rows}</ul></div>`;
|
return `<div class="storage-audit__samples"><span>${localize("KSA.Section.Samples")}</span><ul>${rows}</ul></div>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -428,15 +444,22 @@ function groupByTarget(findings) {
|
|||||||
reason: finding.reason,
|
reason: finding.reason,
|
||||||
recommendation: finding.recommendation,
|
recommendation: finding.recommendation,
|
||||||
targetKind: finding.target.targetKind ?? "local-file",
|
targetKind: finding.target.targetKind ?? "local-file",
|
||||||
sources: new Set()
|
sources: new Map()
|
||||||
};
|
};
|
||||||
current.count += 1;
|
current.count += 1;
|
||||||
if (finding.severity === "high") current.severity = "high";
|
if (finding.severity === "high") current.severity = "high";
|
||||||
if (finding.source?.sourceLabel) current.sources.add(finding.source.sourceLabel);
|
if (finding.source?.sourceLabel) {
|
||||||
|
const key = finding.source.sourceUuid ?? finding.source.sourceLabel;
|
||||||
|
current.sources.set(key, {
|
||||||
|
sourceLabel: finding.source.sourceLabel,
|
||||||
|
sourceName: finding.source.sourceName ?? null,
|
||||||
|
sourceUuid: finding.source.sourceUuid ?? null
|
||||||
|
});
|
||||||
|
}
|
||||||
grouped.set(target, current);
|
grouped.set(target, current);
|
||||||
}
|
}
|
||||||
return [...grouped.values()]
|
return [...grouped.values()]
|
||||||
.map(group => ({ ...group, sources: [...group.sources].slice(0, 5) }))
|
.map(group => ({ ...group, sources: [...group.sources.values()] }))
|
||||||
.sort((a, b) => compareSeverity(a.severity, b.severity) || (b.count - a.count) || a.target.localeCompare(b.target));
|
.sort((a, b) => compareSeverity(a.severity, b.severity) || (b.count - a.count) || a.target.localeCompare(b.target));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -488,6 +511,8 @@ function serializeFinding(finding) {
|
|||||||
? {
|
? {
|
||||||
sourceType: finding.source.sourceType,
|
sourceType: finding.source.sourceType,
|
||||||
sourceLabel: finding.source.sourceLabel,
|
sourceLabel: finding.source.sourceLabel,
|
||||||
|
sourceName: finding.source.sourceName,
|
||||||
|
sourceUuid: finding.source.sourceUuid,
|
||||||
sourceScope: finding.source.sourceScope,
|
sourceScope: finding.source.sourceScope,
|
||||||
rawValue: finding.source.rawValue,
|
rawValue: finding.source.rawValue,
|
||||||
normalized: finding.source.normalized
|
normalized: finding.source.normalized
|
||||||
@@ -529,3 +554,9 @@ function renderLocalizedCodeText(key, data, codeValues) {
|
|||||||
}
|
}
|
||||||
return escapeHtml(text).replace(/@@CODE:([^@]+)@@/g, "<code>$1</code>");
|
return escapeHtml(text).replace(/@@CODE:([^@]+)@@/g, "<code>$1</code>");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function renderSourceLink(source) {
|
||||||
|
const label = source.sourceName ? `${source.sourceLabel} (${source.sourceName})` : source.sourceLabel;
|
||||||
|
if (!source.sourceUuid) return escapeHtml(label);
|
||||||
|
return `<a href="#" data-action="openSource" data-uuid="${escapeHtml(source.sourceUuid)}"><code>${escapeHtml(source.sourceUuid)}</code></a>${label ? ` <span>${escapeHtml(label)}</span>` : ""}`;
|
||||||
|
}
|
||||||
|
|||||||
@@ -28,6 +28,8 @@ export function extractReferencesFromValue(value, source) {
|
|||||||
sourceType: source.sourceType,
|
sourceType: source.sourceType,
|
||||||
sourceScope: source.sourceScope,
|
sourceScope: source.sourceScope,
|
||||||
sourceLabel: source.sourceLabel,
|
sourceLabel: source.sourceLabel,
|
||||||
|
sourceName: source.sourceName,
|
||||||
|
sourceUuid: source.sourceUuid,
|
||||||
rawValue: candidate,
|
rawValue: candidate,
|
||||||
normalized: {
|
normalized: {
|
||||||
...direct,
|
...direct,
|
||||||
@@ -46,6 +48,8 @@ export function extractReferencesFromValue(value, source) {
|
|||||||
sourceType: source.sourceType,
|
sourceType: source.sourceType,
|
||||||
sourceScope: source.sourceScope,
|
sourceScope: source.sourceScope,
|
||||||
sourceLabel: source.sourceLabel,
|
sourceLabel: source.sourceLabel,
|
||||||
|
sourceName: source.sourceName,
|
||||||
|
sourceUuid: source.sourceUuid,
|
||||||
rawValue: match[1],
|
rawValue: match[1],
|
||||||
normalized: {
|
normalized: {
|
||||||
...nested,
|
...nested,
|
||||||
|
|||||||
Reference in New Issue
Block a user