Add orphan result filters
This commit is contained in:
@@ -17,6 +17,12 @@
|
|||||||
"Running": "Analysiere...",
|
"Running": "Analysiere...",
|
||||||
"Export": "Report exportieren"
|
"Export": "Report exportieren"
|
||||||
},
|
},
|
||||||
|
"Filter": {
|
||||||
|
"OrphansLabel": "Ausblenden:",
|
||||||
|
"OrphansModules": "modules/*",
|
||||||
|
"OrphansSystems": "systems/*",
|
||||||
|
"OrphansCorePublic": "canvas/cards/icons/sounds/toolclips/ui"
|
||||||
|
},
|
||||||
"Progress": {
|
"Progress": {
|
||||||
"Initialize": "Initialisiere Analyse",
|
"Initialize": "Initialisiere Analyse",
|
||||||
"Analyzing": "Analysiere",
|
"Analyzing": "Analysiere",
|
||||||
|
|||||||
@@ -17,6 +17,12 @@
|
|||||||
"Running": "Analyzing...",
|
"Running": "Analyzing...",
|
||||||
"Export": "Export Report"
|
"Export": "Export Report"
|
||||||
},
|
},
|
||||||
|
"Filter": {
|
||||||
|
"OrphansLabel": "Hide:",
|
||||||
|
"OrphansModules": "modules/*",
|
||||||
|
"OrphansSystems": "systems/*",
|
||||||
|
"OrphansCorePublic": "canvas/cards/icons/sounds/toolclips/ui"
|
||||||
|
},
|
||||||
"Progress": {
|
"Progress": {
|
||||||
"Initialize": "Initializing analysis",
|
"Initialize": "Initializing analysis",
|
||||||
"Analyzing": "Analyzing",
|
"Analyzing": "Analyzing",
|
||||||
|
|||||||
@@ -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.29",
|
"version": "0.0.30",
|
||||||
"compatibility": {
|
"compatibility": {
|
||||||
"minimum": "13",
|
"minimum": "13",
|
||||||
"verified": "13"
|
"verified": "13"
|
||||||
|
|||||||
@@ -21,6 +21,11 @@ export class StorageAuditReportApp extends foundry.applications.api.ApplicationV
|
|||||||
#analysis = null;
|
#analysis = null;
|
||||||
#loading = false;
|
#loading = false;
|
||||||
#progress = null;
|
#progress = null;
|
||||||
|
#orphanFilters = {
|
||||||
|
modules: true,
|
||||||
|
systems: true,
|
||||||
|
corePublic: true
|
||||||
|
};
|
||||||
|
|
||||||
constructor(options = {}) {
|
constructor(options = {}) {
|
||||||
super(options);
|
super(options);
|
||||||
@@ -28,7 +33,8 @@ export class StorageAuditReportApp extends foundry.applications.api.ApplicationV
|
|||||||
}
|
}
|
||||||
|
|
||||||
async _prepareContext() {
|
async _prepareContext() {
|
||||||
const findings = this.#analysis?.findings ?? [];
|
const visibleAnalysis = this.#getVisibleAnalysis();
|
||||||
|
const findings = visibleAnalysis?.findings ?? [];
|
||||||
const groupedFindings = await enrichGroupedFindings(groupFindings(findings));
|
const groupedFindings = await enrichGroupedFindings(groupFindings(findings));
|
||||||
return {
|
return {
|
||||||
loading: this.#loading,
|
loading: this.#loading,
|
||||||
@@ -36,8 +42,9 @@ export class StorageAuditReportApp extends foundry.applications.api.ApplicationV
|
|||||||
progress: this.#progress,
|
progress: this.#progress,
|
||||||
notices: this.#analysis?.notices ?? [],
|
notices: this.#analysis?.notices ?? [],
|
||||||
moduleVersion: game.modules.get("kosmos-storage-audit")?.version ?? null,
|
moduleVersion: game.modules.get("kosmos-storage-audit")?.version ?? null,
|
||||||
summary: this.#summarize(this.#analysis),
|
summary: this.#summarize(visibleAnalysis),
|
||||||
groupedFindings
|
groupedFindings,
|
||||||
|
orphanFilters: this.#orphanFilters
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -69,13 +76,23 @@ export class StorageAuditReportApp extends foundry.applications.api.ApplicationV
|
|||||||
${renderProgress(context.progress, context.loading)}
|
${renderProgress(context.progress, context.loading)}
|
||||||
${renderNotices(context.notices, context.loading)}
|
${renderNotices(context.notices, context.loading)}
|
||||||
${renderSummary(context.summary, context.loading)}
|
${renderSummary(context.summary, context.loading)}
|
||||||
${renderGroupedFindingList(context.groupedFindings, context.hasAnalysis, context.loading)}
|
${renderGroupedFindingList(context.groupedFindings, context.hasAnalysis, context.loading, context.orphanFilters)}
|
||||||
`;
|
`;
|
||||||
return container;
|
return container;
|
||||||
}
|
}
|
||||||
|
|
||||||
_replaceHTML(result, content) {
|
_replaceHTML(result, content) {
|
||||||
content.replaceChildren(result);
|
content.replaceChildren(result);
|
||||||
|
for (const input of content.querySelectorAll("[data-orphan-filter]")) {
|
||||||
|
input.addEventListener("change", event => {
|
||||||
|
const key = event.currentTarget.dataset.orphanFilter;
|
||||||
|
this.#orphanFilters = {
|
||||||
|
...this.#orphanFilters,
|
||||||
|
[key]: event.currentTarget.checked
|
||||||
|
};
|
||||||
|
this.render({ force: true });
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async runAnalysis() {
|
async runAnalysis() {
|
||||||
@@ -107,13 +124,15 @@ export class StorageAuditReportApp extends foundry.applications.api.ApplicationV
|
|||||||
}
|
}
|
||||||
|
|
||||||
exportReport() {
|
exportReport() {
|
||||||
if (!this.#analysis) return;
|
const visibleAnalysis = this.#getVisibleAnalysis();
|
||||||
|
if (!visibleAnalysis) return;
|
||||||
const payload = {
|
const payload = {
|
||||||
exportedAt: new Date().toISOString(),
|
exportedAt: new Date().toISOString(),
|
||||||
|
orphanFilters: this.#orphanFilters,
|
||||||
notices: this.#analysis.notices ?? [],
|
notices: this.#analysis.notices ?? [],
|
||||||
summary: this.#summarize(this.#analysis),
|
summary: this.#summarize(visibleAnalysis),
|
||||||
groupedFindings: groupFindings(this.#analysis.findings),
|
groupedFindings: groupFindings(visibleAnalysis.findings),
|
||||||
findings: this.#analysis.findings.map(serializeFinding)
|
findings: visibleAnalysis.findings.map(serializeFinding)
|
||||||
};
|
};
|
||||||
const blob = new Blob([JSON.stringify(payload, null, 2)], { type: "application/json" });
|
const blob = new Blob([JSON.stringify(payload, null, 2)], { type: "application/json" });
|
||||||
const url = URL.createObjectURL(blob);
|
const url = URL.createObjectURL(blob);
|
||||||
@@ -154,6 +173,15 @@ export class StorageAuditReportApp extends foundry.applications.api.ApplicationV
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#getVisibleAnalysis() {
|
||||||
|
if (!this.#analysis) return null;
|
||||||
|
const findings = this.#analysis.findings.filter(finding => !shouldHideOrphanFinding(finding, this.#orphanFilters));
|
||||||
|
return {
|
||||||
|
...this.#analysis,
|
||||||
|
findings
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
#updateProgress(update) {
|
#updateProgress(update) {
|
||||||
this.#progress = {
|
this.#progress = {
|
||||||
...(this.#progress ?? {}),
|
...(this.#progress ?? {}),
|
||||||
@@ -251,7 +279,7 @@ function renderSummary(summary, loading) {
|
|||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
function renderGroupedFindingList(groupedFindings, hasAnalysis, loading) {
|
function renderGroupedFindingList(groupedFindings, hasAnalysis, loading, orphanFilters) {
|
||||||
if (loading || !hasAnalysis) return "";
|
if (loading || !hasAnalysis) return "";
|
||||||
|
|
||||||
const sections = [];
|
const sections = [];
|
||||||
@@ -283,6 +311,7 @@ function renderGroupedFindingList(groupedFindings, hasAnalysis, loading) {
|
|||||||
sections.push(renderGroupedSection(
|
sections.push(renderGroupedSection(
|
||||||
localize("KSA.Section.OrphanCandidates"),
|
localize("KSA.Section.OrphanCandidates"),
|
||||||
localize("KSA.Section.OrphanCandidatesDesc"),
|
localize("KSA.Section.OrphanCandidatesDesc"),
|
||||||
|
renderOrphanFilters(orphanFilters),
|
||||||
renderGroupedTable(groupedFindings.orphans, {
|
renderGroupedTable(groupedFindings.orphans, {
|
||||||
includeOwner: false,
|
includeOwner: false,
|
||||||
includeReason: true,
|
includeReason: true,
|
||||||
@@ -305,18 +334,43 @@ function renderGroupedFindingList(groupedFindings, hasAnalysis, loading) {
|
|||||||
return `<section class="storage-audit__grouped">${sections.join("")}</section>`;
|
return `<section class="storage-audit__grouped">${sections.join("")}</section>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
function renderGroupedSection(title, description, content) {
|
function renderGroupedSection(title, description, prelude = "", content = "") {
|
||||||
|
if (!content) {
|
||||||
|
content = prelude;
|
||||||
|
prelude = "";
|
||||||
|
}
|
||||||
return `
|
return `
|
||||||
<section class="storage-audit__group">
|
<section class="storage-audit__group">
|
||||||
<div class="storage-audit__group-header">
|
<div class="storage-audit__group-header">
|
||||||
<h3>${title}</h3>
|
<h3>${title}</h3>
|
||||||
<p>${description}</p>
|
<p>${description}</p>
|
||||||
</div>
|
</div>
|
||||||
|
${prelude}
|
||||||
<div class="storage-audit__table-wrap">${content}</div>
|
<div class="storage-audit__table-wrap">${content}</div>
|
||||||
</section>
|
</section>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function renderOrphanFilters(filters) {
|
||||||
|
return `
|
||||||
|
<div class="storage-audit__orphan-filters">
|
||||||
|
<span class="storage-audit__orphan-filters-label">${localize("KSA.Filter.OrphansLabel")}</span>
|
||||||
|
${renderOrphanFilterToggle("modules", localize("KSA.Filter.OrphansModules"), filters.modules)}
|
||||||
|
${renderOrphanFilterToggle("systems", localize("KSA.Filter.OrphansSystems"), filters.systems)}
|
||||||
|
${renderOrphanFilterToggle("corePublic", localize("KSA.Filter.OrphansCorePublic"), filters.corePublic)}
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderOrphanFilterToggle(key, label, checked) {
|
||||||
|
return `
|
||||||
|
<label class="storage-audit__checkbox">
|
||||||
|
<input type="checkbox" data-orphan-filter="${escapeHtml(key)}" ${checked ? "checked" : ""}>
|
||||||
|
<span>${escapeHtml(label)}</span>
|
||||||
|
</label>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
function renderGroupedTable(groups, { includeOwner=false, includeReason=false, includeSources=false }={}) {
|
function renderGroupedTable(groups, { includeOwner=false, includeReason=false, includeSources=false }={}) {
|
||||||
const headers = [
|
const headers = [
|
||||||
`<th>${localize("KSA.Table.Severity")}</th>`,
|
`<th>${localize("KSA.Table.Severity")}</th>`,
|
||||||
@@ -523,6 +577,21 @@ async function enrichGroupedFindings(groupedFindings) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function shouldHideOrphanFinding(finding, orphanFilters) {
|
||||||
|
if (finding.kind !== "orphan-file") return false;
|
||||||
|
const target = finding.target?.locator ?? formatTarget(finding.target ?? {});
|
||||||
|
const [, path = ""] = String(target).split(":", 2);
|
||||||
|
|
||||||
|
if (orphanFilters.modules && path.startsWith("modules/")) return true;
|
||||||
|
if (orphanFilters.systems && path.startsWith("systems/")) return true;
|
||||||
|
if (
|
||||||
|
orphanFilters.corePublic &&
|
||||||
|
["canvas/", "cards/", "icons/", "sounds/", "toolclips/", "ui/"].some(prefix => path.startsWith(prefix))
|
||||||
|
) return true;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
async function enrichGroupedSources(groups) {
|
async function enrichGroupedSources(groups) {
|
||||||
const enriched = [];
|
const enriched = [];
|
||||||
for (const group of groups) {
|
for (const group of groups) {
|
||||||
|
|||||||
@@ -45,6 +45,28 @@
|
|||||||
overflow-x: auto;
|
overflow-x: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.storage-audit__orphan-filters {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 0.6rem 1rem;
|
||||||
|
align-items: center;
|
||||||
|
padding: 0.35rem 1.1rem 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.storage-audit__orphan-filters-label {
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
|
||||||
|
.storage-audit__checkbox {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.4rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.storage-audit__checkbox input {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
.storage-audit__progress {
|
.storage-audit__progress {
|
||||||
padding: 1rem 1.1rem;
|
padding: 1rem 1.1rem;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user