From a6c450109a745ce363c56223ac43691dfe5e440f Mon Sep 17 00:00:00 2001 From: "DESKTOP-T0O5CDB\\DESK-555BD" Date: Tue, 7 Jan 2025 08:43:25 -0700 Subject: [PATCH 1/4] added API method that generates a calendar of reminders for the user. --- Controllers/APIController.cs | 13 ++++++++++ Helper/StaticHelper.cs | 48 ++++++++++++++++++++++++++++++++++++ 2 files changed, 61 insertions(+) diff --git a/Controllers/APIController.cs b/Controllers/APIController.cs index 41a68b6..131a2cc 100644 --- a/Controllers/APIController.cs +++ b/Controllers/APIController.cs @@ -1166,6 +1166,19 @@ namespace CarCareTracker.Controllers return Json(results); } } + [HttpGet] + [Route("/api/calendar")] + public IActionResult CalendarICS() + { + var vehiclesStored = _dataAccess.GetVehicles(); + if (!User.IsInRole(nameof(UserData.IsRootUser))) + { + vehiclesStored = _userLogic.FilterUserVehicles(vehiclesStored, GetUserID()); + } + var reminders = _vehicleLogic.GetReminders(vehiclesStored, true); + var calendarContent = StaticHelper.RemindersToCalendar(reminders); + return File(calendarContent, "text/calendar"); + } [Authorize(Roles = nameof(UserData.IsRootUser))] [HttpGet] [Route("/api/vehicle/reminders/send")] diff --git a/Helper/StaticHelper.cs b/Helper/StaticHelper.cs index 84c96bb..5749e7a 100644 --- a/Helper/StaticHelper.cs +++ b/Helper/StaticHelper.cs @@ -1,6 +1,7 @@ using CarCareTracker.Models; using CsvHelper; using System.Globalization; +using System.Text; using System.Text.Json; namespace CarCareTracker.Helper @@ -669,5 +670,52 @@ namespace CarCareTracker.Helper _csv.NextRecord(); } } + public static byte[] RemindersToCalendar(List reminders) + { + //converts reminders to iCal file + StringBuilder sb = new StringBuilder(); + //start the calendar item + sb.AppendLine("BEGIN:VCALENDAR"); + sb.AppendLine("VERSION:2.0"); + sb.AppendLine("PRODID:lubelogger.com"); + sb.AppendLine("CALSCALE:GREGORIAN"); + sb.AppendLine("METHOD:PUBLISH"); + + //create events. + foreach(ReminderRecordViewModel reminder in reminders) + { + var dtStart = reminder.Date.Date.ToString("yyyyMMddTHHmm00"); + var dtEnd = reminder.Date.Date.AddDays(1).AddMilliseconds(-1).ToString("yyyyMMddTHHmm00"); + + sb.AppendLine("BEGIN:VEVENT"); + sb.AppendLine("DTSTAMP:" + DateTime.Now.ToString("yyyyMMddTHHmm00")); + sb.AppendLine("UID:" + Guid.NewGuid()); + sb.AppendLine("DTSTART:" + dtStart); + sb.AppendLine("DTEND:" + dtEnd); + sb.AppendLine($"SUMMARY:{reminder.Description}"); + sb.AppendLine($"DESCRIPTION:{reminder.Description}"); + switch (reminder.Urgency) + { + case ReminderUrgency.NotUrgent: + sb.AppendLine("PRIORITY:3"); + break; + case ReminderUrgency.Urgent: + sb.AppendLine("PRIORITY:2"); + break; + case ReminderUrgency.VeryUrgent: + sb.AppendLine("PRIORITY:1"); + break; + case ReminderUrgency.PastDue: + sb.AppendLine("PRIORITY:1"); + break; + } + sb.AppendLine("END:VEVENT"); + } + + //end calendar item + sb.AppendLine("END:VCALENDAR"); + string calendarContent = sb.ToString(); + return Encoding.UTF8.GetBytes(calendarContent); + } } } From c82e0c8b9bb2f0d834cdd0f73404faaa48b5da17 Mon Sep 17 00:00:00 2001 From: "DESKTOP-T0O5CDB\\DESK-555BD" Date: Wed, 8 Jan 2025 10:49:26 -0700 Subject: [PATCH 2/4] Use a MD5 hash to get exactly 16 bytes so the GUID is always valid and identical for the calendar event with same date and description. --- Helper/StaticHelper.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Helper/StaticHelper.cs b/Helper/StaticHelper.cs index 8041553..f540ed7 100644 --- a/Helper/StaticHelper.cs +++ b/Helper/StaticHelper.cs @@ -1,6 +1,7 @@ using CarCareTracker.Models; using CsvHelper; using System.Globalization; +using System.Security.Cryptography; using System.Text; using System.Text.Json; @@ -771,10 +772,10 @@ namespace CarCareTracker.Helper { var dtStart = reminder.Date.Date.ToString("yyyyMMddTHHmm00"); var dtEnd = reminder.Date.Date.AddDays(1).AddMilliseconds(-1).ToString("yyyyMMddTHHmm00"); - + var calendarUID = new Guid(MD5.HashData(Encoding.UTF8.GetBytes($"{dtStart}_{reminder.Description}"))); sb.AppendLine("BEGIN:VEVENT"); sb.AppendLine("DTSTAMP:" + DateTime.Now.ToString("yyyyMMddTHHmm00")); - sb.AppendLine("UID:" + Guid.NewGuid()); + sb.AppendLine("UID:" + calendarUID); sb.AppendLine("DTSTART:" + dtStart); sb.AppendLine("DTEND:" + dtEnd); sb.AppendLine($"SUMMARY:{reminder.Description}"); From f48e7cd0d4fff4509157ddf46400adde94ab8694 Mon Sep 17 00:00:00 2001 From: "DESKTOP-T0O5CDB\\DESK-555BD" Date: Wed, 8 Jan 2025 11:11:29 -0700 Subject: [PATCH 3/4] update Controller Action Name, --- Controllers/APIController.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Controllers/APIController.cs b/Controllers/APIController.cs index 131a2cc..eba424d 100644 --- a/Controllers/APIController.cs +++ b/Controllers/APIController.cs @@ -1168,7 +1168,7 @@ namespace CarCareTracker.Controllers } [HttpGet] [Route("/api/calendar")] - public IActionResult CalendarICS() + public IActionResult Calendar() { var vehiclesStored = _dataAccess.GetVehicles(); if (!User.IsInRole(nameof(UserData.IsRootUser))) From f2f55f81182acfb7f31de70f8d5ed5a7661818a2 Mon Sep 17 00:00:00 2001 From: "DESKTOP-T0O5CDB\\DESK-555BD" Date: Wed, 8 Jan 2025 12:38:27 -0700 Subject: [PATCH 4/4] Restore favicon, previously was served up automatically by browser but now we want to link it explicitly. also brought back the security features for accessing static files. --- Program.cs | 38 ++++++++++++++++++++++++++++--------- Views/Shared/_Layout.cshtml | 1 + 2 files changed, 30 insertions(+), 9 deletions(-) diff --git a/Program.cs b/Program.cs index ce9346e..bcb25da 100644 --- a/Program.cs +++ b/Program.cs @@ -106,11 +106,15 @@ var app = builder.Build(); // Configure the HTTP request pipeline. app.UseExceptionHandler("/Home/Error"); +app.UseStaticFiles(); app.UseStaticFiles(new StaticFileOptions { + FileProvider = new PhysicalFileProvider( + Path.Combine(builder.Environment.ContentRootPath, "data", "images")), + RequestPath = "/images", OnPrepareResponse = ctx => { - if (ctx.Context.Request.Path.StartsWithSegments("/images") || ctx.Context.Request.Path.StartsWithSegments("/documents")) + if (ctx.Context.Request.Path.StartsWithSegments("/images")) { ctx.Context.Response.Headers.Add("Cache-Control", "no-store"); if (!ctx.Context.User.Identity.IsAuthenticated) @@ -121,16 +125,21 @@ app.UseStaticFiles(new StaticFileOptions } }); app.UseStaticFiles(new StaticFileOptions -{ - FileProvider = new PhysicalFileProvider( - Path.Combine(builder.Environment.ContentRootPath, "data", "images")), - RequestPath = "/images" -}); -app.UseStaticFiles(new StaticFileOptions { FileProvider = new PhysicalFileProvider( Path.Combine(builder.Environment.ContentRootPath, "data", "documents")), - RequestPath = "/documents" + RequestPath = "/documents", + OnPrepareResponse = ctx => + { + if (ctx.Context.Request.Path.StartsWithSegments("/documents")) + { + ctx.Context.Response.Headers.Add("Cache-Control", "no-store"); + if (!ctx.Context.User.Identity.IsAuthenticated) + { + ctx.Context.Response.Redirect("/Login"); + } + } + } }); app.UseStaticFiles(new StaticFileOptions { @@ -142,7 +151,18 @@ app.UseStaticFiles(new StaticFileOptions { FileProvider = new PhysicalFileProvider( Path.Combine(builder.Environment.ContentRootPath, "data", "temp")), - RequestPath = "/temp" + RequestPath = "/temp", + OnPrepareResponse = ctx => + { + if (ctx.Context.Request.Path.StartsWithSegments("/temp")) + { + ctx.Context.Response.Headers.Add("Cache-Control", "no-store"); + if (!ctx.Context.User.Identity.IsAuthenticated) + { + ctx.Context.Response.Redirect("/Login"); + } + } + } }); app.UseRouting(); diff --git a/Views/Shared/_Layout.cshtml b/Views/Shared/_Layout.cshtml index 43c2773..25cbfec 100644 --- a/Views/Shared/_Layout.cshtml +++ b/Views/Shared/_Layout.cshtml @@ -34,6 +34,7 @@ @ViewData["Title"] - LubeLogger +