Wiki source code of ISMS Management Dashboard

Last modified by Lee Pedley on 2026/09/21 21:59

Hide last authors
Lee Pedley 1.1 1 {{velocity}}
2 #set ($settingsDoc = $xwiki.getDocument('ISMS.Settings'))
3 #set ($settings = $settingsDoc.getObject('ISMS.Code.SettingsClass'))
4 #set ($riskLowMax = 4)
5 #set ($riskMediumMax = 9)
6 #set ($riskHighMax = 14)
7 #set ($riskAppetite = 15)
8 #set ($reviewHorizon = 30)
9 #set ($evidenceHorizon = 30)
10 #set ($documentHorizon = 30)
11 #if ($settings)
12 #if ($settings.getProperty('riskLowMax').getValue()) #set ($riskLowMax = $settings.getProperty('riskLowMax').getValue()) #end
13 #if ($settings.getProperty('riskMediumMax').getValue()) #set ($riskMediumMax = $settings.getProperty('riskMediumMax').getValue()) #end
14 #if ($settings.getProperty('riskHighMax').getValue()) #set ($riskHighMax = $settings.getProperty('riskHighMax').getValue()) #end
15 #if ($settings.getProperty('riskAppetiteThreshold').getValue()) #set ($riskAppetite = $settings.getProperty('riskAppetiteThreshold').getValue()) #end
16 #if ($settings.getProperty('reviewHorizonDays').getValue()) #set ($reviewHorizon = $settings.getProperty('reviewHorizonDays').getValue()) #end
17 #if ($settings.getProperty('evidenceHorizonDays').getValue()) #set ($evidenceHorizon = $settings.getProperty('evidenceHorizonDays').getValue()) #end
18 #if ($settings.getProperty('documentHorizonDays').getValue()) #set ($documentHorizon = $settings.getProperty('documentHorizonDays').getValue()) #end
19 #end
20 #set ($riskDocs=$services.query.xwql("from doc.object('Risks.Code.RisksClass') as obj").setLimit(2000).execute())
21 #set ($treatmentDocs=$services.query.xwql("from doc.object('Risk Treatments.Code.Risk TreatmentsClass') as obj").setLimit(2000).execute())
22 #set ($controlDocs=$services.query.xwql("from doc.object('Controls and SoA.Code.Controls and SoAClass') as obj").setLimit(2000).execute())
23 #set ($evidenceDocs=$services.query.xwql("from doc.object('Evidence.Code.EvidenceClass') as obj").setLimit(2000).execute())
24 #set ($auditDocs=$services.query.xwql("from doc.object('Audits.Code.AuditsClass') as obj").setLimit(2000).execute())
25 #set ($findingDocs=$services.query.xwql("from doc.object('Findings.Code.FindingsClass') as obj").setLimit(2000).execute())
26 #set ($actionDocs=$services.query.xwql("from doc.object('Corrective Actions.Code.Corrective ActionsClass') as obj").setLimit(2000).execute())
27 #set ($controlledDocDocs=$services.query.xwql("from doc.object('Controlled Documents.Code.Controlled DocumentsClass') as obj").setLimit(2000).execute())
28 #set($openRisks=0)#set($aboveAppetite=0)#set($unassessedResidual=0)#set($openTreatments=0)#set($overdueTreatments=0)#set($controlsDue=0)#set($evidenceAttention=0)#set($activeAudits=0)#set($openFindings=0)#set($overdueFindings=0)#set($overdueActions=0)#set($documentsDue=0)#set($alerts=[])
29 #foreach($ref in $riskDocs)#set($d=$xwiki.getDocument($ref))#set($o=$d.getObject('Risks.Code.RisksClass'))#if($o)#set($st=$o.getProperty('status').getValue())#set($rs=$o.getProperty('residualScore').getValue())#if($st!='Closed')#set($openRisks=$openRisks+1)#if(!$rs)#set($unassessedResidual=$unassessedResidual+1)#elseif($rs >= $riskAppetite)#set($aboveAppetite=$aboveAppetite+1)#set($x=$alerts.add(['Residual risk >= appetite threshold',$ref,$d.displayTitle,$rs]))#end#end#end#end
30 #foreach($ref in $treatmentDocs)#set($d=$xwiki.getDocument($ref))#set($o=$d.getObject('Risk Treatments.Code.Risk TreatmentsClass'))#if($o)#set($st=$o.getProperty('status').getValue())#if($st!='Complete'&&$st!='Cancelled')#set($openTreatments=$openTreatments+1)#set($due=$o.getProperty('targetDate').getValue())#if($due&&$datetool.whenIs($due).days<0)#set($overdueTreatments=$overdueTreatments+1)#set($x=$alerts.add(['Overdue treatment',$ref,$d.displayTitle,$xwiki.formatDate($due)]))#end#end#end#end
31 #foreach($ref in $controlDocs)#set($d=$xwiki.getDocument($ref))#set($o=$d.getObject('Controls and SoA.Code.Controls and SoAClass'))#if($o)#set($due=$o.getProperty('nextReviewDate').getValue())#if($due&&$datetool.whenIs($due).days<=$reviewHorizon)#set($controlsDue=$controlsDue+1)#set($x=$alerts.add(['Control review',$ref,$d.displayTitle,$xwiki.formatDate($due)]))#end#end#end
32 #foreach($ref in $evidenceDocs)#set($d=$xwiki.getDocument($ref))#set($o=$d.getObject('Evidence.Code.EvidenceClass'))#if($o)#set($due=$o.getProperty('validUntil').getValue())#if($due&&$datetool.whenIs($due).days<=$evidenceHorizon)#set($evidenceAttention=$evidenceAttention+1)#set($x=$alerts.add(['Evidence review / expiry',$ref,$d.displayTitle,$xwiki.formatDate($due)]))#end#end#end
33 #foreach($ref in $auditDocs)#set($d=$xwiki.getDocument($ref))#set($o=$d.getObject('Audits.Code.AuditsClass'))#if($o)#set($st=$o.getProperty('status').getValue())#if($st!='Complete'&&$st!='Cancelled')#set($activeAudits=$activeAudits+1)#end#end#end
34 #foreach($ref in $findingDocs)#set($d=$xwiki.getDocument($ref))#set($o=$d.getObject('Findings.Code.FindingsClass'))#if($o)#set($st=$o.getProperty('status').getValue())#if($st!='Closed')#set($openFindings=$openFindings+1)#set($due=$o.getProperty('dueDate').getValue())#if($due&&$datetool.whenIs($due).days<0)#set($overdueFindings=$overdueFindings+1)#set($x=$alerts.add(['Overdue finding',$ref,$d.displayTitle,$xwiki.formatDate($due)]))#end#end#end#end
35 #foreach($ref in $actionDocs)#set($d=$xwiki.getDocument($ref))#set($o=$d.getObject('Corrective Actions.Code.Corrective ActionsClass'))#if($o)#set($st=$o.getProperty('status').getValue())#set($due=$o.getProperty('dueDate').getValue())#if($due&&$st!='Verified'&&$st!='Cancelled'&&$datetool.whenIs($due).days<0)#set($overdueActions=$overdueActions+1)#set($x=$alerts.add(['Overdue corrective action',$ref,$d.displayTitle,$xwiki.formatDate($due)]))#end#end#end
36 #foreach($ref in $controlledDocDocs)#set($d=$xwiki.getDocument($ref))#set($o=$d.getObject('Controlled Documents.Code.Controlled DocumentsClass'))#if($o)#set($due=$o.getProperty('nextReviewDate').getValue())#if($due&&$datetool.whenIs($due).days<=$documentHorizon)#set($documentsDue=$documentsDue+1)#set($x=$alerts.add(['Document review',$ref,$d.displayTitle,$xwiki.formatDate($due)]))#end#end#end
37 {{html wiki="true" clean="false"}}
38 <style>
39 .isms-dashboard .isms-dash-note{margin-bottom:16px}.isms-quick-actions{display:flex;flex-wrap:wrap;gap:7px;margin:12px 0 20px}.isms-quick-actions form{margin:0}.isms-kpis{display:grid;grid-template-columns:repeat(auto-fit,minmax(190px,1fr));gap:12px;margin:12px 0 24px}.isms-kpi{border:1px solid #d9dfe5;border-radius:8px;padding:14px;background:#fff;min-height:115px}.isms-kpi-title{font-weight:700;color:#263238;margin-bottom:6px}.isms-kpi-value{font-size:2em;font-weight:700;line-height:1.1}.isms-kpi-sub{color:#68737d;font-size:.9em;margin-top:7px;line-height:1.35}.isms-dash-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(240px,1fr));gap:10px}.isms-dash-link{border:1px solid #d9dfe5;border-radius:7px;padding:13px;display:block;text-decoration:none}.isms-alerts{width:100%;border-collapse:collapse}.isms-alerts th,.isms-alerts td{border-bottom:1px solid #e1e5e9;padding:8px;text-align:left}.isms-dash-section{margin-top:26px}
40 </style>
41 <div class="isms-dashboard">
42 <div class="box infomessage isms-dash-note"><strong>Management overview</strong><br/>This page highlights items requiring attention. Thresholds and reminder windows come from <a href="$xwiki.getURL('ISMS.Settings','view')">ISMS Settings</a>. Residual risk review threshold: <strong>$riskAppetite</strong>. Control review reminder: <strong>$reviewHorizon days</strong>.</div>
43 <h2>Quick actions</h2>
44 <div class="isms-quick-actions">
45 <form action="$xwiki.getURL('Risks.WebHome','create')" method="get"><input type="hidden" name="templateprovider" value="Risks.Code.RisksTemplateProvider"/><button class="btn btn-primary" type="submit">Add Risk</button></form>
46 <form action="$xwiki.getURL('Controls and SoA.WebHome','create')" method="get"><input type="hidden" name="templateprovider" value="Controls and SoA.Code.Controls and SoATemplateProvider"/><button class="btn btn-default" type="submit">Add Control / SoA Entry</button></form>
47 <form action="$xwiki.getURL('Risk Treatments.WebHome','create')" method="get"><input type="hidden" name="templateprovider" value="Risk Treatments.Code.Risk TreatmentsTemplateProvider"/><button class="btn btn-default" type="submit">Add Risk Treatment</button></form>
48 <form action="$xwiki.getURL('Evidence.WebHome','create')" method="get"><input type="hidden" name="templateprovider" value="Evidence.Code.EvidenceTemplateProvider"/><button class="btn btn-default" type="submit">Add Evidence</button></form>
49 <form action="$xwiki.getURL('Audits.WebHome','create')" method="get"><input type="hidden" name="templateprovider" value="Audits.Code.AuditsTemplateProvider"/><button class="btn btn-default" type="submit">Add Audit</button></form>
50 <a class="btn btn-default" href="$xwiki.getURL('ISMS.DataQuality','view')">Check Data Quality</a>
51 </div>
52 <h2>Current position</h2>
53 <div class="isms-kpis">
54 <div class="isms-kpi"><div class="isms-kpi-title">Open risks</div><div class="isms-kpi-value">$openRisks</div><div class="isms-kpi-sub"><a href="$xwiki.getURL('Risks.WebHome','view')">Open the risk register</a></div></div>
55 <div class="isms-kpi"><div class="isms-kpi-title">Residual risks requiring management review</div><div class="isms-kpi-value">$aboveAppetite</div><div class="isms-kpi-sub">Residual score at or above $riskAppetite. $unassessedResidual open risk(s) still need residual assessment.</div></div>
56 <div class="isms-kpi"><div class="isms-kpi-title">Open risk treatments</div><div class="isms-kpi-value">$openTreatments</div><div class="isms-kpi-sub">$overdueTreatments overdue. <a href="$xwiki.getURL('Risk Treatments.WebHome','view')">Open treatments</a></div></div>
57 <div class="isms-kpi"><div class="isms-kpi-title">Controls due for review</div><div class="isms-kpi-value">$controlsDue</div><div class="isms-kpi-sub">Due within $reviewHorizon days or overdue. <a href="$xwiki.getURL('Controls and SoA.WebHome','view')">Open Controls &amp; SoA</a></div></div>
58 <div class="isms-kpi"><div class="isms-kpi-title">Evidence requiring attention</div><div class="isms-kpi-value">$evidenceAttention</div><div class="isms-kpi-sub">Expiring within $evidenceHorizon days or expired. <a href="$xwiki.getURL('Evidence.WebHome','view')">Open evidence</a></div></div>
59 <div class="isms-kpi"><div class="isms-kpi-title">Open findings</div><div class="isms-kpi-value">$openFindings</div><div class="isms-kpi-sub">$overdueFindings overdue. <a href="$xwiki.getURL('Findings.WebHome','view')">Open findings</a></div></div>
60 <div class="isms-kpi"><div class="isms-kpi-title">Overdue corrective actions</div><div class="isms-kpi-value">$overdueActions</div><div class="isms-kpi-sub"><a href="$xwiki.getURL('Corrective Actions.WebHome','view')">Open corrective actions</a></div></div>
61 <div class="isms-kpi"><div class="isms-kpi-title">Controlled documents due for review</div><div class="isms-kpi-value">$documentsDue</div><div class="isms-kpi-sub">Due within $documentHorizon days or overdue. <a href="$xwiki.getURL('Controlled Documents.WebHome','view')">Open documents</a></div></div>
62 <div class="isms-kpi"><div class="isms-kpi-title">Active audits</div><div class="isms-kpi-value">$activeAudits</div><div class="isms-kpi-sub"><a href="$xwiki.getURL('Audits.WebHome','view')">Open the audit register</a></div></div>
63 </div>
64 <div class="isms-dash-section"><h2>Attention queue</h2>#if($alerts.isEmpty())<div class="box successmessage">No tracked overdue, due-soon or residual-risk-review items were detected.</div>#else<table class="isms-alerts"><thead><tr><th>Reason</th><th>Record</th><th>Date / score</th></tr></thead><tbody>#foreach($a in $alerts)<tr><td>$escapetool.xml($a.get(0))</td><td><a href="$xwiki.getURL($a.get(1),'view')">$escapetool.xml($a.get(2))</a></td><td>$escapetool.xml($a.get(3))</td></tr>#end</tbody></table>#end</div>
65 <div class="isms-dash-section"><h2>Reports and assurance</h2><div class="isms-dash-grid"><a class="isms-dash-link" href="$xwiki.getURL('ISMS.RiskReport','view')"><strong>Risk Report</strong><br/>Inherent and residual exposure, treatment and management-review threshold.</a><a class="isms-dash-link" href="$xwiki.getURL('ISMS.SoAReport','view')"><strong>Statement of Applicability Report</strong><br/>Control applicability, justification, implementation and evidence.</a><a class="isms-dash-link" href="$xwiki.getURL('ISMS.ManagementReview','view')"><strong>Management Review Pack</strong><br/>Management-level snapshot and review agenda.</a><a class="isms-dash-link" href="$xwiki.getURL('ISMS.DataQuality','view')"><strong>Data Quality Check</strong><br/>Find incomplete records before assurance reviews.</a></div></div>
66 <div class="isms-dash-section"><h2>Registers</h2><div class="isms-dash-grid"><a class="isms-dash-link" href="$xwiki.getURL('Controlled Documents.WebHome','view')"><strong>Controlled Documents</strong><br/>Policies, procedures, standards, forms and records.</a><a class="isms-dash-link" href="$xwiki.getURL('Controls and SoA.WebHome','view')"><strong>Controls &amp; SoA</strong><br/>Applicability, implementation, evidence and review.</a><a class="isms-dash-link" href="$xwiki.getURL('Risks.WebHome','view')"><strong>Risks</strong><br/>Risk assessment and treatment decisions.</a><a class="isms-dash-link" href="$xwiki.getURL('Risk Treatments.WebHome','view')"><strong>Risk Treatments</strong><br/>Actions, owners and target dates.</a><a class="isms-dash-link" href="$xwiki.getURL('Evidence.WebHome','view')"><strong>Evidence</strong><br/>Evidence records and validity dates.</a><a class="isms-dash-link" href="$xwiki.getURL('Audits.WebHome','view')"><strong>Audits</strong><br/>Assurance programme and audit activity.</a><a class="isms-dash-link" href="$xwiki.getURL('Findings.WebHome','view')"><strong>Findings</strong><br/>Audit findings, ownership and closure.</a><a class="isms-dash-link" href="$xwiki.getURL('Corrective Actions.WebHome','view')"><strong>Corrective Actions</strong><br/>Remediation and effectiveness review.</a></div></div>
67 </div>
68 {{/html}}
69 {{/velocity}}