From 606a865757a762ed2206be76b4df77117258a321 Mon Sep 17 00:00:00 2001 From: jessikitty Date: Mon, 27 Apr 2026 10:19:15 +1000 Subject: [PATCH] Rewrite web handler with server-side rendering - no jQuery dependency --- WebHandler/ADCompareWebHandler.cs | 215 ++++++++++++++++++------------ 1 file changed, 131 insertions(+), 84 deletions(-) diff --git a/WebHandler/ADCompareWebHandler.cs b/WebHandler/ADCompareWebHandler.cs index b69596f..7a11cbc 100644 --- a/WebHandler/ADCompareWebHandler.cs +++ b/WebHandler/ADCompareWebHandler.cs @@ -1,6 +1,5 @@ using Disco.Plugins.ADCompare.Features; using Disco.Services.Plugins; -using Newtonsoft.Json; using System; using System.Linq; using System.Text; @@ -19,8 +18,8 @@ namespace Disco.Plugins.ADCompare.WebHandler case "": case "index": return Index(); - case "compare": - return Compare(); + case "run": + return Run(); case "export": return ExportCsv(); default: @@ -30,24 +29,14 @@ namespace Disco.Plugins.ADCompare.WebHandler private ActionResult Index() { - return new ContentResult - { - Content = BuildDashboardHtml(), - ContentType = "text/html", - ContentEncoding = Encoding.UTF8 - }; + return HtmlResult(BuildPage(null)); } - private ActionResult Compare() + private ActionResult Run() { var service = new DeviceCompareService(Database); var summary = service.CompareAllDevices(); - return new ContentResult - { - Content = JsonConvert.SerializeObject(summary, Formatting.Indented), - ContentType = "application/json", - ContentEncoding = Encoding.UTF8 - }; + return HtmlResult(BuildPage(summary)); } private ActionResult ExportCsv() @@ -59,14 +48,13 @@ namespace Disco.Plugins.ADCompare.WebHandler foreach (var r in summary.Results.Where(r => !r.IsMatch)) { sb.AppendLine(string.Join(",", - CsvEscape(r.SerialNumber), CsvEscape(r.ComputerName), - CsvEscape(r.DiscoAssignedUserId), CsvEscape(r.DiscoAssignedUserDisplayName), - CsvEscape(r.ADManagedByUserId), CsvEscape(r.ADManagedByDisplayName), - r.IsMatch.ToString(), CsvEscape(r.MismatchReason))); + CsvEsc(r.SerialNumber), CsvEsc(r.ComputerName), + CsvEsc(r.DiscoAssignedUserId), CsvEsc(r.DiscoAssignedUserDisplayName), + CsvEsc(r.ADManagedByUserId), CsvEsc(r.ADManagedByDisplayName), + r.IsMatch.ToString(), CsvEsc(r.MismatchReason))); } var fileName = "AD_ManagedBy_Compare_" + DateTime.Now.ToString("yyyyMMdd_HHmmss") + ".csv"; - HostController.Response.Headers.Add("Content-Disposition", - "attachment; filename=\"" + fileName + "\""); + HostController.Response.Headers.Add("Content-Disposition", "attachment; filename=\"" + fileName + "\""); return new ContentResult { Content = sb.ToString(), @@ -75,75 +63,134 @@ namespace Disco.Plugins.ADCompare.WebHandler }; } - private string CsvEscape(string v) + private ActionResult HtmlResult(string html) + { + return new ContentResult + { + Content = html, + ContentType = "text/html", + ContentEncoding = Encoding.UTF8 + }; + } + + private string CsvEsc(string v) { if (v == null) v = ""; return "\"" + v.Replace("\"", "\"\"") + "\""; } - private string BuildDashboardHtml() + private string H(string v) { - // Build the plugin URL manually (T4MVC's MVC.API class isn't available outside the Disco solution) - var pluginUrl = "/Plugin/" + HttpUtility.UrlEncode(Manifest.Id); + if (string.IsNullOrEmpty(v)) return ""; + return HttpUtility.HtmlEncode(v); + } - return @" -
-

AD Compare — Device Managed By

-

Compares the AD computer Managed By field against the Disco Assigned User.

-
- - - -
- -
- - -
- - - - - - - -
-"; + private string BuildPage(Models.DeviceComparisonSummary summary) + { + var pluginUrl = "/Plugin/Disco.Plugins.ADCompare"; + var sb = new StringBuilder(); + + sb.Append(""); + sb.Append("AD Compare - Device Managed By"); + sb.Append(""); + + sb.Append("

AD Compare — Device Managed By

"); + sb.Append("

Compares the AD computer Managed By field against the Disco Assigned User.

"); + + sb.Append("
"); + sb.Append("↻ Run Comparison"); + if (summary != null) + { + sb.Append("⬇ Export Mismatches CSV"); + } + sb.Append("
"); + + if (summary != null) + { + sb.Append("
"); + sb.Append("
" + summary.TotalDevices + "
Total Devices
"); + sb.Append("
" + summary.DevicesMatched + "
Matched
"); + sb.Append("
" + summary.DevicesMismatched + "
Mismatched
"); + sb.Append("
" + summary.DevicesNotInAD + "
Not in AD
"); + sb.Append("
" + summary.DevicesNoManagedBy + "
No ManagedBy
"); + sb.Append("
" + summary.DevicesNoAssignment + "
No Assignment
"); + sb.Append("
"); + + var mismatches = summary.Results.Where(r => !r.IsMatch).ToList(); + + if (mismatches.Count == 0) + { + sb.Append("
"); + sb.Append("✓ All devices match! Every device's Disco assigned user matches the AD Managed By field."); + sb.Append("
"); + } + else + { + sb.Append("

Mismatches (" + mismatches.Count + ")

"); + sb.Append(""); + sb.Append(""); + sb.Append(""); + sb.Append(""); + + foreach (var r in mismatches) + { + string rowClass = r.FoundInAD ? "warn-row" : "danger-row"; + string badge = r.FoundInAD + ? "Mismatch" + : "Not in AD"; + + string discoUser = !string.IsNullOrEmpty(r.DiscoAssignedUserId) + ? H(r.DiscoAssignedUserId) + (r.DiscoAssignedUserDisplayName != null + ? "
" + H(r.DiscoAssignedUserDisplayName) + "" : "") + : "Not assigned"; + + string adUser; + if (!string.IsNullOrEmpty(r.ADManagedByUserId)) + { + adUser = H(r.ADManagedByUserId); + if (r.ADManagedByDisplayName != null) + adUser = adUser + "
" + H(r.ADManagedByDisplayName) + ""; + } + else if (r.FoundInAD) + { + adUser = "Empty"; + } + else + { + adUser = "N/A"; + } + + sb.Append(""); + sb.Append(""); + sb.Append(""); + sb.Append(""); + sb.Append(""); + sb.Append(""); + sb.Append(""); + sb.Append(""); + } + + sb.Append("
StatusSerial NumberComputer NameDisco Assigned UserAD Managed ByReason
" + badge + "" + H(r.SerialNumber) + "" + H(r.ComputerName) + "" + discoUser + "" + adUser + "" + H(r.MismatchReason) + "
"); + } + } + + sb.Append(""); + return sb.ToString(); } } }