From 6af5682548636c0ab9b2e154a3b3e36a60fd2531 Mon Sep 17 00:00:00 2001 From: Shaun Walker Date: Wed, 5 Jan 2022 14:28:42 -0500 Subject: [PATCH] increment copyright date to 2022, allow scheduled jobs to support weekly interval, improve dynamic image generation, add defensive logic to router --- LICENSE | 2 +- Oqtane.Client/Modules/Admin/Jobs/Edit.razor | 1 + Oqtane.Client/Modules/Admin/Jobs/Index.razor | 3 + .../Modules/Admin/UserProfile/Index.razor | 2 +- Oqtane.Client/Modules/ModuleBase.cs | 13 ++-- .../Resources/Modules/Admin/Jobs/Edit.resx | 3 + .../Resources/Modules/Admin/Jobs/Index.resx | 11 ++-- Oqtane.Client/Themes/ThemeBase.cs | 13 ++-- Oqtane.Client/UI/SiteRouter.razor | 15 +++-- Oqtane.Server/Controllers/FileController.cs | 64 ++++++++++--------- .../Infrastructure/Jobs/HostedServiceBase.cs | 8 +++ .../SiteTemplates/DefaultSiteTemplate.cs | 2 +- Oqtane.Shared/Shared/Utilities.cs | 9 ++- 13 files changed, 93 insertions(+), 53 deletions(-) diff --git a/LICENSE b/LICENSE index 6b68cd26..81e8cb41 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2018-2021 .NET Foundation +Copyright (c) 2018-2022 .NET Foundation Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/Oqtane.Client/Modules/Admin/Jobs/Edit.razor b/Oqtane.Client/Modules/Admin/Jobs/Edit.razor index 72e56897..3d7af836 100644 --- a/Oqtane.Client/Modules/Admin/Jobs/Edit.razor +++ b/Oqtane.Client/Modules/Admin/Jobs/Edit.razor @@ -36,6 +36,7 @@ + diff --git a/Oqtane.Client/Modules/Admin/Jobs/Index.razor b/Oqtane.Client/Modules/Admin/Jobs/Index.razor index 2ff36730..a71b6d68 100644 --- a/Oqtane.Client/Modules/Admin/Jobs/Index.razor +++ b/Oqtane.Client/Modules/Admin/Jobs/Index.razor @@ -95,6 +95,9 @@ else case "d": result += Localizer["Day"]; break; + case "w": + result += Localizer["Week"]; + break; case "M": result += Localizer["Month"]; break; diff --git a/Oqtane.Client/Modules/Admin/UserProfile/Index.razor b/Oqtane.Client/Modules/Admin/UserProfile/Index.razor index 5644482d..9f3d19ae 100644 --- a/Oqtane.Client/Modules/Admin/UserProfile/Index.razor +++ b/Oqtane.Client/Modules/Admin/UserProfile/Index.razor @@ -12,7 +12,7 @@ @if (PageState.User != null && photo != null) { - @displayname + @displayname } else { diff --git a/Oqtane.Client/Modules/ModuleBase.cs b/Oqtane.Client/Modules/ModuleBase.cs index 30adc2a1..f3f06aee 100644 --- a/Oqtane.Client/Modules/ModuleBase.cs +++ b/Oqtane.Client/Modules/ModuleBase.cs @@ -134,14 +134,19 @@ namespace Oqtane.Modules return Utilities.ContentUrl(PageState.Alias, fileid, asAttachment); } - public string ImageUrl(int fileid, int width, int height, string mode) + public string ImageUrl(int fileid, int width, int height) { - return ImageUrl(fileid, width, height, mode, 0); + return ImageUrl(fileid, width, height, ""); } - public string ImageUrl(int fileid, int width, int height, string mode, int rotate) + public string ImageUrl(int fileid, int width, int height, string mode) { - return Utilities.ImageUrl(PageState.Alias, fileid, width, height, mode, rotate); + return ImageUrl(fileid, width, height, mode, "", "", 0, false); + } + + public string ImageUrl(int fileid, int width, int height, string mode, string position, string background, int rotate, bool recreate) + { + return Utilities.ImageUrl(PageState.Alias, fileid, width, height, mode, position, background, rotate, recreate); } public virtual Dictionary GetUrlParameters(string parametersTemplate = "") diff --git a/Oqtane.Client/Resources/Modules/Admin/Jobs/Edit.resx b/Oqtane.Client/Resources/Modules/Admin/Jobs/Edit.resx index eae2f962..569f2a71 100644 --- a/Oqtane.Client/Resources/Modules/Admin/Jobs/Edit.resx +++ b/Oqtane.Client/Resources/Modules/Admin/Jobs/Edit.resx @@ -186,4 +186,7 @@ Next Execution: + + Week(s) + \ No newline at end of file diff --git a/Oqtane.Client/Resources/Modules/Admin/Jobs/Index.resx b/Oqtane.Client/Resources/Modules/Admin/Jobs/Index.resx index 1ca4d846..a5906e03 100644 --- a/Oqtane.Client/Resources/Modules/Admin/Jobs/Index.resx +++ b/Oqtane.Client/Resources/Modules/Admin/Jobs/Index.resx @@ -133,16 +133,16 @@ Every - Minute + Minute(s) - Hour + Hour(s) - Day + Day(s) - Month + Month(s) Error Deleting Job @@ -186,4 +186,7 @@ The process responsible for executing this job has been stopped. In order to restart the process you will need to use the Start button or restart the application. + + Week(s) + \ No newline at end of file diff --git a/Oqtane.Client/Themes/ThemeBase.cs b/Oqtane.Client/Themes/ThemeBase.cs index 2771105c..ccf80e04 100644 --- a/Oqtane.Client/Themes/ThemeBase.cs +++ b/Oqtane.Client/Themes/ThemeBase.cs @@ -104,14 +104,19 @@ namespace Oqtane.Themes return Utilities.ContentUrl(PageState.Alias, fileid, asAttachment); } - public string ImageUrl(int fileid, int width, int height, string mode) + public string ImageUrl(int fileid, int width, int height) { - return ImageUrl(fileid, width, height, mode, 0); + return ImageUrl(fileid, width, height, ""); } - public string ImageUrl(int fileid, int width, int height, string mode, int rotate) + public string ImageUrl(int fileid, int width, int height, string mode) { - return Utilities.ImageUrl(PageState.Alias, fileid, width, height, mode, rotate); + return ImageUrl(fileid, width, height, mode, "", "", 0, false); + } + + public string ImageUrl(int fileid, int width, int height, string mode, string position, string background, int rotate, bool recreate) + { + return Utilities.ImageUrl(PageState.Alias, fileid, width, height, mode, position, background, rotate, recreate); } } } diff --git a/Oqtane.Client/UI/SiteRouter.razor b/Oqtane.Client/UI/SiteRouter.razor index 1e339e78..a5339642 100644 --- a/Oqtane.Client/UI/SiteRouter.razor +++ b/Oqtane.Client/UI/SiteRouter.razor @@ -83,7 +83,7 @@ var action = (!string.IsNullOrEmpty(route.Action)) ? route.Action : Constants.DefaultAction; var querystring = ParseQueryString(route.Query); - // reload the client application if there is a forced reload or the user navigated to a site with a different alias + // reload the client application if there is a forced reload or the user navigated to a site with a different alias if (querystring.ContainsKey("reload") || (!route.AbsolutePath.Substring(1).ToLower().StartsWith(SiteState.Alias.Path.ToLower()) && !string.IsNullOrEmpty(SiteState.Alias.Path))) { NavigationManager.NavigateTo(_absoluteUri.Replace("?reload", ""), true); @@ -91,7 +91,7 @@ } else { - // the refresh parameter is used to refresh the PageState + // the refresh parameter is used to refresh the PageState if (querystring.ContainsKey("refresh")) { refresh = UI.Refresh.Site; @@ -104,12 +104,12 @@ lastsyncdate = PageState.LastSyncDate; } - // process any sync events + // process any sync events var sync = await SyncService.GetSyncAsync(lastsyncdate); lastsyncdate = sync.SyncDate; if (sync.SyncEvents.Any()) { - // reload client application if server was restarted or site runtime/rendermode was modified + // reload client application if server was restarted or site runtime/rendermode was modified if (PageState != null && sync.SyncEvents.Exists(item => (item.TenantId == -1 || item.EntityName == EntityNames.Site && item.EntityId == SiteState.Alias.SiteId) && item.Reload)) { NavigationManager.NavigateTo(_absoluteUri, true); @@ -135,12 +135,15 @@ { if (PageState == null || refresh == UI.Refresh.Site) { - // get user + // get user var authState = await AuthenticationStateProvider.GetAuthenticationStateAsync(); if (authState.User.Identity.IsAuthenticated) { user = await UserService.GetUserAsync(authState.User.Identity.Name, site.SiteId); - user.IsAuthenticated = authState.User.Identity.IsAuthenticated; + if (user != null) + { + user.IsAuthenticated = authState.User.Identity.IsAuthenticated; + } } } else diff --git a/Oqtane.Server/Controllers/FileController.cs b/Oqtane.Server/Controllers/FileController.cs index bb1182e9..b5dafc8b 100644 --- a/Oqtane.Server/Controllers/FileController.cs +++ b/Oqtane.Server/Controllers/FileController.cs @@ -508,8 +508,8 @@ namespace Oqtane.Controllers return System.IO.File.Exists(errorPath) ? PhysicalFile(errorPath, MimeUtilities.GetMimeType(errorPath)) : null; } - [HttpGet("image/{id}/{width}/{height}/{mode?}/{rotate?}")] - public IActionResult GetImage(int id, int width, int height, string mode, string rotate) + [HttpGet("image/{id}/{width}/{height}/{mode}/{position}/{background}/{rotate}/{recreate}")] + public IActionResult GetImage(int id, int width, int height, string mode, string position, string background, string rotate, string recreate) { var file = _files.GetFile(id); if (file != null && file.Folder.SiteId == _alias.SiteId && _userPermissions.IsAuthorized(User, PermissionNames.View, file.Folder.Permissions)) @@ -519,21 +519,25 @@ namespace Oqtane.Controllers var filepath = _files.GetFilePath(file); if (System.IO.File.Exists(filepath)) { - mode = (string.IsNullOrEmpty(mode)) ? "crop" : mode; - rotate = (string.IsNullOrEmpty(rotate)) ? "0" : rotate; + // validation + if (!Enum.TryParse(mode, true, out ResizeMode _)) mode = "crop"; + if (!Enum.TryParse(position, true, out AnchorPositionMode _)) position = "center"; + if (!Color.TryParseHex("#" + background, out _)) background = "000000"; + if (!int.TryParse(rotate, out _)) rotate = "0"; + rotate = (int.Parse(rotate) < 0 || int.Parse(rotate) > 360) ? "0" : rotate; + if (!bool.TryParse(recreate, out _)) recreate = "false"; - string imagepath = filepath.Replace(Path.GetExtension(filepath), "." + width.ToString() + "x" + height.ToString() + "." + mode.ToLower() + ".png"); - if (!System.IO.File.Exists(imagepath)) + string imagepath = filepath.Replace(Path.GetExtension(filepath), "." + width.ToString() + "x" + height.ToString() + ".png"); + if (!System.IO.File.Exists(imagepath) || bool.Parse(recreate)) { if ((_userPermissions.IsAuthorized(User, PermissionNames.Edit, file.Folder.Permissions) || - !string.IsNullOrEmpty(file.Folder.ImageSizes) && file.Folder.ImageSizes.ToLower().Split(",").Contains(width.ToString() + "x" + height.ToString())) - && Enum.TryParse(mode, true, out ResizeMode resizemode)) + !string.IsNullOrEmpty(file.Folder.ImageSizes) && file.Folder.ImageSizes.ToLower().Split(",").Contains(width.ToString() + "x" + height.ToString()))) { - imagepath = CreateImage(filepath, width, height, resizemode.ToString(), rotate, imagepath); + imagepath = CreateImage(filepath, width, height, mode, position, background, rotate, imagepath); } else { - _logger.Log(LogLevel.Error, this, LogFunction.Security, "Invalid Image Size For Folder Or Invalid Mode Specification {Folder} {Width} {Height} {Mode}", file.Folder, width, height, mode); + _logger.Log(LogLevel.Error, this, LogFunction.Security, "Invalid Image Size For Folder {Folder} {Width} {Height}", file.Folder, width, height); HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden; } } @@ -569,35 +573,37 @@ namespace Oqtane.Controllers return System.IO.File.Exists(errorPath) ? PhysicalFile(errorPath, MimeUtilities.GetMimeType(errorPath)) : null; } - private string CreateImage(string filepath, int width, int height, string mode, string rotate, string imagepath) + private string CreateImage(string filepath, int width, int height, string mode, string position, string background, string rotate, string imagepath) { try { - FileStream stream = new FileStream(filepath, FileMode.Open, FileAccess.Read); - using (Image image = Image.Load(stream)) + using (var stream = new FileStream(filepath, FileMode.Open, FileAccess.Read)) { - Enum.TryParse(mode, true, out ResizeMode resizemode); - - image.Mutate(x => - x.Resize(new ResizeOptions - { - Size = new Size(width, height), - Mode = resizemode - }) - .BackgroundColor(new Rgba32(255, 255, 255, 0))); - - if (rotate != "0" && int.TryParse(rotate, out int angle)) + stream.Position = 0; + using (var image = Image.Load(stream)) { - image.Mutate(x => x.Rotate(angle)); - } + int.TryParse(rotate, out int angle); + Enum.TryParse(mode, true, out ResizeMode resizemode); + Enum.TryParse(position, true, out AnchorPositionMode anchorpositionmode); - image.Save(imagepath, new PngEncoder()); + image.Mutate(x => x + .AutoOrient() // auto orient the image + .Rotate(angle) + .Resize(new ResizeOptions + { + Mode = resizemode, + Position = anchorpositionmode, + Size = new Size(width, height) + }) + .BackgroundColor(Color.ParseHex("#" + background))); + + image.Save(imagepath, new PngEncoder()); + } } - stream.Close(); } catch (Exception ex) { - _logger.Log(LogLevel.Error, this, LogFunction.Security, "Error Creating Image For File {FilePath} {Width} {Height} {Mode} {Error}", filepath, width, height, mode, ex.Message); + _logger.Log(LogLevel.Error, this, LogFunction.Security, "Error Creating Image For File {FilePath} {Width} {Height} {Mode} {Rotate} {Error}", filepath, width, height, mode, rotate, ex.Message); imagepath = ""; } diff --git a/Oqtane.Server/Infrastructure/Jobs/HostedServiceBase.cs b/Oqtane.Server/Infrastructure/Jobs/HostedServiceBase.cs index d208bc72..21cfff4f 100644 --- a/Oqtane.Server/Infrastructure/Jobs/HostedServiceBase.cs +++ b/Oqtane.Server/Infrastructure/Jobs/HostedServiceBase.cs @@ -158,6 +158,14 @@ namespace Oqtane.Infrastructure nextExecution = nextExecution.Date.Add(job.StartDate.Value.TimeOfDay); } break; + case "w": // weeks + nextExecution = nextExecution.AddDays(job.Interval * 7); + if (job.StartDate != null && job.StartDate.Value.TimeOfDay.TotalSeconds != 0) + { + // set the start time + nextExecution = nextExecution.Date.Add(job.StartDate.Value.TimeOfDay); + } + break; case "M": // months nextExecution = nextExecution.AddMonths(job.Interval); if (job.StartDate != null && job.StartDate.Value.TimeOfDay.TotalSeconds != 0) diff --git a/Oqtane.Server/Infrastructure/SiteTemplates/DefaultSiteTemplate.cs b/Oqtane.Server/Infrastructure/SiteTemplates/DefaultSiteTemplate.cs index ae03d057..13c0aa67 100644 --- a/Oqtane.Server/Infrastructure/SiteTemplates/DefaultSiteTemplate.cs +++ b/Oqtane.Server/Infrastructure/SiteTemplates/DefaultSiteTemplate.cs @@ -68,7 +68,7 @@ namespace Oqtane.SiteTemplates new Permission(PermissionNames.View, RoleNames.Admin, true), new Permission(PermissionNames.Edit, RoleNames.Admin, true) }.EncodePermissions(), - Content = "

Copyright (c) 2019-2021 .NET Foundation

" + + Content = "

Copyright (c) 2018-2022 .NET Foundation

" + "

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

" + "

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

" + "

THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

" diff --git a/Oqtane.Shared/Shared/Utilities.cs b/Oqtane.Shared/Shared/Utilities.cs index 1ac3f900..b1313a7a 100644 --- a/Oqtane.Shared/Shared/Utilities.cs +++ b/Oqtane.Shared/Shared/Utilities.cs @@ -114,13 +114,16 @@ namespace Oqtane.Shared public static string ImageUrl(Alias alias, int fileId, int width, int height, string mode) { - return ImageUrl(alias, fileId, width, height, mode, 0); + return ImageUrl(alias, fileId, width, height, mode, "", "", 0, false); } - public static string ImageUrl(Alias alias, int fileId, int width, int height, string mode, int rotate) + public static string ImageUrl(Alias alias, int fileId, int width, int height, string mode, string position, string background, int rotate, bool recreate) { var aliasUrl = (alias != null && !string.IsNullOrEmpty(alias.Path)) ? "/" + alias.Path : ""; - return $"{aliasUrl}{Constants.ImageUrl}{fileId}/{width}/{height}/{mode}/{rotate}"; + mode = string.IsNullOrEmpty(mode) ? "crop" : mode; + position = string.IsNullOrEmpty(position) ? "center" : position; + background = string.IsNullOrEmpty(background) ? "000000" : background; + return $"{aliasUrl}{Constants.ImageUrl}{fileId}/{width}/{height}/{mode}/{position}/{background}/{rotate}/{recreate}"; } public static string TenantUrl(Alias alias, string url)