fix: C#5 compat - replace all ?. with helper methods, remove GetPluginDataDirectory

This commit is contained in:
2026-05-06 09:17:27 +10:00
parent 1988cbb86d
commit 56004c4e21
+93 -30
View File
@@ -13,14 +13,8 @@ namespace Disco.Plugins.ServiceTracker.WebHandler
{
private ServiceTrackerDataStore GetDataStore()
{
var dataPath = PluginConfigurationHandler.GetPluginDataDirectory(
HostController.HttpContext.Application["Disco.Plugins.ServiceTracker"] as Plugin
?? new ServiceTrackerPlugin());
if (string.IsNullOrEmpty(dataPath))
{
dataPath = System.IO.Path.Combine(
var dataPath = System.IO.Path.Combine(
AppDomain.CurrentDomain.BaseDirectory, "App_Data", "Plugins", "Disco.Plugins.ServiceTracker");
}
return new ServiceTrackerDataStore(dataPath);
}
@@ -77,7 +71,7 @@ namespace Disco.Plugins.ServiceTracker.WebHandler
DateTime etaParsed;
if (DateTime.TryParse(HostController.Request.Form["eta"], out etaParsed))
eta = etaParsed;
var currentUser = HostController.HttpContext.User?.Identity?.Name ?? "system";
var currentUser = GetCurrentUser();
service.UpdateTicket(jobId, priorityId, locationId, techId, eta, status, summary, currentUser);
return new RedirectResult("/Plugin/Disco.Plugins.ServiceTracker/Dashboard");
}
@@ -93,7 +87,7 @@ namespace Disco.Plugins.ServiceTracker.WebHandler
return new HttpStatusCodeResult(400);
var content = HostController.Request.Form["note"];
var noteType = HostController.Request.Form["noteType"] ?? "general";
var currentUser = HostController.HttpContext.User?.Identity?.Name ?? "system";
var currentUser = GetCurrentUser();
service.AddNote(jobId, currentUser, currentUser, content, noteType);
return new RedirectResult("/Plugin/Disco.Plugins.ServiceTracker/Detail?id=" + jobId);
}
@@ -127,19 +121,28 @@ namespace Disco.Plugins.ServiceTracker.WebHandler
sb.AppendLine("JobId,Device,User,Priority,Location,Status,AssignedTech,OpenedDate,ETA,SlaDeadline,SlaBreached,AgeDays,Summary,NoteCount");
foreach (var t in model.Tiles)
{
var etaStr = t.EstimatedCompletion.HasValue ? t.EstimatedCompletion.Value.ToString("yyyy-MM-dd") : "";
var slaStr = t.SlaDeadline.HasValue ? t.SlaDeadline.Value.ToString("yyyy-MM-dd HH:mm") : "";
sb.AppendLine(string.Join(",",
t.JobId, Csv(t.DeviceSerialNumber), Csv(t.UserDisplayName),
Csv(t.PriorityName), Csv(t.LocationName), Csv(t.StatusOverride),
Csv(t.AssignedTechName), t.OpenedDate.ToString("yyyy-MM-dd"),
t.EstimatedCompletion?.ToString("yyyy-MM-dd") ?? "",
t.SlaDeadline?.ToString("yyyy-MM-dd HH:mm") ?? "",
t.IsSlaBreached, t.AgeDays, Csv(t.Summary), t.NoteCount));
etaStr, slaStr, t.IsSlaBreached, t.AgeDays, Csv(t.Summary), t.NoteCount));
}
var fileName = "ServiceTracker_Export_" + DateTime.Now.ToString("yyyyMMdd_HHmmss") + ".csv";
HostController.Response.Headers.Add("Content-Disposition", "attachment; filename=\"" + fileName + "\"");
return new ContentResult { Content = sb.ToString(), ContentType = "text/csv", ContentEncoding = Encoding.UTF8 };
}
// --- Helpers ---
private string GetCurrentUser()
{
if (HostController.HttpContext.User != null && HostController.HttpContext.User.Identity != null)
return HostController.HttpContext.User.Identity.Name ?? "system";
return "system";
}
private ActionResult HtmlResult(string html)
{
return new ContentResult { Content = html, ContentType = "text/html", ContentEncoding = Encoding.UTF8 };
@@ -147,15 +150,50 @@ namespace Disco.Plugins.ServiceTracker.WebHandler
private string Csv(string v) { return "\"" + (v ?? "").Replace("\"", "\"\"") + "\""; }
private string H(string v) { return string.IsNullOrEmpty(v) ? "" : HttpUtility.HtmlEncode(v); }
// --- Safe accessors for job navigation properties (C#5 compatible) ---
private string SafeDeviceDomainId(Disco.Models.Repository.Job job)
{
return job.Device != null ? job.Device.DeviceDomainId : null;
}
private string SafeDeviceModelDesc(Disco.Models.Repository.Job job)
{
return (job.Device != null && job.Device.DeviceModel != null) ? job.Device.DeviceModel.Description : null;
}
private string SafeUserDisplay(Disco.Models.Repository.Job job)
{
return job.User != null ? job.User.DisplayName : job.UserId;
}
private string SafeJobTypeDesc(Disco.Models.Repository.Job job)
{
return job.JobType != null ? job.JobType.Description : job.JobTypeId;
}
private string SafeTechDisplay(Disco.Models.Repository.Job job)
{
return job.OpenedTechUser != null ? job.OpenedTechUser.DisplayName : job.OpenedTechUserId;
}
private string SafeTicketStr(ServiceTicket ticket, string field)
{
if (ticket == null) return null;
switch (field)
{
case "PriorityId": return ticket.PriorityId;
case "LocationId": return ticket.LocationId;
case "AssignedTechId": return ticket.AssignedTechId;
case "StatusOverride": return ticket.StatusOverride;
case "Summary": return ticket.Summary;
default: return null;
}
}
// --- HTML Builders ---
private string BuildDashboardPage(DashboardViewModel model)
{
var pluginUrl = "/Plugin/Disco.Plugins.ServiceTracker";
var sb = new StringBuilder();
sb.Append("<!DOCTYPE html><html><head><meta charset='utf-8'/>");
sb.Append("<title>Service Tracker Dashboard</title>");
sb.Append("<style>");
sb.Append(GetDashboardCSS());
sb.Append("</style></head><body>");
sb.Append("<style>"); sb.Append(GetDashboardCSS()); sb.Append("</style></head><body>");
// Header
sb.Append("<div class='header'><div class='header-left'>");
@@ -221,10 +259,14 @@ namespace Disco.Plugins.ServiceTracker.WebHandler
// Tile Grid
sb.Append("<div class='tile-grid'>");
if (model.Tiles.Count == 0)
{
sb.Append("<div class='empty-state'><div class='empty-icon'>&#x2705;</div><div class='empty-msg'>No open jobs found</div></div>");
}
else
{
foreach (var tile in model.Tiles)
sb.Append(BuildTileHtml(tile, pluginUrl));
}
sb.Append("</div>");
// Tech Workload
@@ -314,13 +356,14 @@ namespace Disco.Plugins.ServiceTracker.WebHandler
sb.Append("<h1>Job #" + job.Id + "</h1></div></div>");
sb.Append("<div class='detail-grid'>");
// Left column
// Left column - Job info
sb.Append("<div class='detail-left'><div class='detail-card'><h3>Job Details</h3><table class='detail-table'>");
sb.Append("<tr><th>Device</th><td>" + H(job.DeviceSerialNumber) + (job.Device?.DeviceDomainId != null ? " (" + H(job.Device.DeviceDomainId) + ")" : "") + "</td></tr>");
sb.Append("<tr><th>Model</th><td>" + H(job.Device?.DeviceModel?.Description) + "</td></tr>");
sb.Append("<tr><th>User</th><td>" + H(job.User?.DisplayName ?? job.UserId) + "</td></tr>");
sb.Append("<tr><th>Type</th><td>" + H(job.JobType?.Description ?? job.JobTypeId) + "</td></tr>");
sb.Append("<tr><th>Opened</th><td>" + job.OpenedDate.ToString("dd MMM yyyy HH:mm") + " by " + H(job.OpenedTechUser?.DisplayName ?? job.OpenedTechUserId) + "</td></tr>");
var domainId = SafeDeviceDomainId(job);
sb.Append("<tr><th>Device</th><td>" + H(job.DeviceSerialNumber) + (domainId != null ? " (" + H(domainId) + ")" : "") + "</td></tr>");
sb.Append("<tr><th>Model</th><td>" + H(SafeDeviceModelDesc(job)) + "</td></tr>");
sb.Append("<tr><th>User</th><td>" + H(SafeUserDisplay(job)) + "</td></tr>");
sb.Append("<tr><th>Type</th><td>" + H(SafeJobTypeDesc(job)) + "</td></tr>");
sb.Append("<tr><th>Opened</th><td>" + job.OpenedDate.ToString("dd MMM yyyy HH:mm") + " by " + H(SafeTechDisplay(job)) + "</td></tr>");
if (job.ExpectedClosedDate.HasValue)
sb.Append("<tr><th>Expected Close</th><td>" + job.ExpectedClosedDate.Value.ToString("dd MMM yyyy") + "</td></tr>");
if (job.DeviceHeld.HasValue)
@@ -328,33 +371,44 @@ namespace Disco.Plugins.ServiceTracker.WebHandler
sb.Append("</table></div>");
// Edit form
var ticketPriority = SafeTicketStr(ticket, "PriorityId") ?? config.DefaultPriorityId;
var ticketLocation = SafeTicketStr(ticket, "LocationId") ?? config.DefaultLocationId;
var ticketStatus = SafeTicketStr(ticket, "StatusOverride");
var ticketTech = SafeTicketStr(ticket, "AssignedTechId") ?? "";
var ticketSummary = SafeTicketStr(ticket, "Summary") ?? "";
var ticketEta = (ticket != null && ticket.EstimatedCompletion.HasValue) ? ticket.EstimatedCompletion.Value.ToString("yyyy-MM-dd") : "";
sb.Append("<div class='detail-card'><h3>Service Tracker Settings</h3>");
sb.Append("<form method='POST' action='" + pluginUrl + "/Update'>");
sb.Append("<input type='hidden' name='jobId' value='" + job.Id + "'/>");
sb.Append("<div class='form-group'><label>Priority</label><select name='priority' class='form-control'>");
foreach (var p in config.Priorities)
{
var sel = (ticket?.PriorityId ?? config.DefaultPriorityId) == p.Id ? " selected" : "";
var sel = ticketPriority == p.Id ? " selected" : "";
sb.Append("<option value='" + p.Id + "'" + sel + ">" + H(p.Name) + " (" + p.SlaHours + "h SLA)</option>");
}
sb.Append("</select></div>");
sb.Append("<div class='form-group'><label>Location</label><select name='location' class='form-control'>");
foreach (var l in config.Locations)
{
var sel = (ticket?.LocationId ?? config.DefaultLocationId) == l.Id ? " selected" : "";
var sel = ticketLocation == l.Id ? " selected" : "";
sb.Append("<option value='" + l.Id + "'" + sel + ">" + l.Icon + " " + H(l.Name) + "</option>");
}
sb.Append("</select></div>");
sb.Append("<div class='form-group'><label>Status</label><select name='status' class='form-control'><option value=''>— Use Disco Status —</option>");
foreach (var s in config.StatusOptions)
{
var sel = ticket?.StatusOverride == s ? " selected" : "";
var sel = ticketStatus == s ? " selected" : "";
sb.Append("<option value='" + H(s) + "'" + sel + ">" + H(s) + "</option>");
}
sb.Append("</select></div>");
sb.Append("<div class='form-group'><label>Assigned Tech (User ID)</label><input type='text' name='tech' class='form-control' value='" + H(ticket?.AssignedTechId) + "' placeholder='e.g. DOMAIN\\username'/></div>");
sb.Append("<div class='form-group'><label>ETA</label><input type='date' name='eta' class='form-control' value='" + (ticket?.EstimatedCompletion?.ToString("yyyy-MM-dd") ?? "") + "'/></div>");
sb.Append("<div class='form-group'><label>Summary</label><textarea name='summary' class='form-control' rows='3' placeholder='Brief description of the issue...'>" + H(ticket?.Summary) + "</textarea></div>");
sb.Append("<div class='form-group'><label>Assigned Tech (User ID)</label><input type='text' name='tech' class='form-control' value='" + H(ticketTech) + "' placeholder='e.g. DOMAIN\\username'/></div>");
sb.Append("<div class='form-group'><label>ETA</label><input type='date' name='eta' class='form-control' value='" + ticketEta + "'/></div>");
sb.Append("<div class='form-group'><label>Summary</label><textarea name='summary' class='form-control' rows='3' placeholder='Brief description of the issue...'>" + H(ticketSummary) + "</textarea></div>");
sb.Append("<button type='submit' class='btn btn-primary'>&#x2714; Save Changes</button>");
sb.Append("</form></div></div>");
@@ -367,15 +421,22 @@ namespace Disco.Plugins.ServiceTracker.WebHandler
sb.Append("<option value='general'>General</option><option value='update'>Update</option>");
sb.Append("<option value='escalation'>Escalation</option><option value='resolution'>Resolution</option>");
sb.Append("</select><button type='submit' class='btn btn-primary btn-sm'>Add Note</button></div></form>");
if (ticket?.Notes != null && ticket.Notes.Count > 0)
if (ticket != null && ticket.Notes != null && ticket.Notes.Count > 0)
{
sb.Append("<div class='timeline'>");
foreach (var note in ticket.Notes.OrderByDescending(n => n.Timestamp))
{
string tc = "#337AB7";
switch (note.NoteType) { case "escalation": tc = "#DC3545"; break; case "resolution": tc = "#28A745"; break; case "update": tc = "#FFC107"; break; }
switch (note.NoteType)
{
case "escalation": tc = "#DC3545"; break;
case "resolution": tc = "#28A745"; break;
case "update": tc = "#FFC107"; break;
}
var authorDisplay = note.AuthorName != null ? note.AuthorName : note.AuthorId;
sb.Append("<div class='timeline-item'><div class='timeline-dot' style='background:" + tc + ";'></div><div class='timeline-content'>");
sb.Append("<div class='timeline-header'><span class='timeline-author'>" + H(note.AuthorName ?? note.AuthorId) + "</span>");
sb.Append("<div class='timeline-header'><span class='timeline-author'>" + H(authorDisplay) + "</span>");
sb.Append("<span class='timeline-type' style='color:" + tc + ";'>" + H(note.NoteType) + "</span>");
sb.Append("<span class='timeline-date'>" + note.Timestamp.ToString("dd MMM HH:mm") + "</span></div>");
sb.Append("<div class='timeline-body'>" + H(note.Content) + "</div></div></div>");
@@ -383,7 +444,9 @@ namespace Disco.Plugins.ServiceTracker.WebHandler
sb.Append("</div>");
}
else
{
sb.Append("<p class='muted'>No notes yet.</p>");
}
sb.Append("</div></div></div>");
sb.Append("</body></html>");
return sb.ToString();