Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

increment copyright date to 2022, allow scheduled jobs to support weekly interval, improve dynamic image generation, add defensive logic to router #1909

Merged
merged 1 commit into from
Jan 5, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion LICENSE
Original file line number Diff line number Diff line change
@@ -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
Expand Down
1 change: 1 addition & 0 deletions Oqtane.Client/Modules/Admin/Jobs/Edit.razor
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
<option value="m">@Localizer["Minute(s)"]</option>
<option value="H">@Localizer["Hour(s)"]</option>
<option value="d">@Localizer["Day(s)"]</option>
<option value="w">@Localizer["Week(s)"]</option>
<option value="M">@Localizer["Month(s)"]</option>
</select>
</div>
Expand Down
3 changes: 3 additions & 0 deletions Oqtane.Client/Modules/Admin/Jobs/Index.razor
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,9 @@ else
case "d":
result += Localizer["Day"];
break;
case "w":
result += Localizer["Week"];
break;
case "M":
result += Localizer["Month"];
break;
Expand Down
2 changes: 1 addition & 1 deletion Oqtane.Client/Modules/Admin/UserProfile/Index.razor
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@

@if (PageState.User != null && photo != null)
{
<img src="@ImageUrl(photofileid, 400, 400, "crop")" alt="@displayname" style="max-width: 400px" class="rounded-circle mx-auto d-block">
<img src="@ImageUrl(photofileid, 400, 400)" alt="@displayname" style="max-width: 400px" class="rounded-circle mx-auto d-block">
}
else
{
Expand Down
11 changes: 8 additions & 3 deletions Oqtane.Client/Modules/ModuleBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -134,14 +134,19 @@ public string ContentUrl(int fileid, bool asAttachment)
return Utilities.ContentUrl(PageState.Alias, fileid, asAttachment);
}

public string ImageUrl(int fileid, int width, int height)
{
return ImageUrl(fileid, width, height, "");
}

public string ImageUrl(int fileid, int width, int height, string mode)
{
return ImageUrl(fileid, width, height, mode, 0);
return ImageUrl(fileid, width, height, mode, "", "", 0, false);
}

public string ImageUrl(int fileid, int width, int height, string mode, int rotate)
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, rotate);
return Utilities.ImageUrl(PageState.Alias, fileid, width, height, mode, position, background, rotate, recreate);
}

public virtual Dictionary<string, string> GetUrlParameters(string parametersTemplate = "")
Expand Down
3 changes: 3 additions & 0 deletions Oqtane.Client/Resources/Modules/Admin/Jobs/Edit.resx
Original file line number Diff line number Diff line change
Expand Up @@ -186,4 +186,7 @@
<data name="NextExecution.Text" xml:space="preserve">
<value>Next Execution: </value>
</data>
<data name="Week(s)" xml:space="preserve">
<value>Week(s)</value>
</data>
</root>
11 changes: 7 additions & 4 deletions Oqtane.Client/Resources/Modules/Admin/Jobs/Index.resx
Original file line number Diff line number Diff line change
Expand Up @@ -133,16 +133,16 @@
<value>Every</value>
</data>
<data name="Minute" xml:space="preserve">
<value>Minute</value>
<value>Minute(s)</value>
</data>
<data name="Hour" xml:space="preserve">
<value>Hour</value>
<value>Hour(s)</value>
</data>
<data name="Day" xml:space="preserve">
<value>Day</value>
<value>Day(s)</value>
</data>
<data name="Month" xml:space="preserve">
<value>Month</value>
<value>Month(s)</value>
</data>
<data name="Error.Job.Delete" xml:space="preserve">
<value>Error Deleting Job</value>
Expand Down Expand Up @@ -186,4 +186,7 @@
<data name="Message.Job.Stop" xml:space="preserve">
<value>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.</value>
</data>
<data name="Week" xml:space="preserve">
<value>Week(s)</value>
</data>
</root>
11 changes: 8 additions & 3 deletions Oqtane.Client/Themes/ThemeBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -104,14 +104,19 @@ public string ContentUrl(int fileid, bool asAttachment)
return Utilities.ContentUrl(PageState.Alias, fileid, asAttachment);
}

public string ImageUrl(int fileid, int width, int height)
{
return ImageUrl(fileid, width, height, "");
}

public string ImageUrl(int fileid, int width, int height, string mode)
{
return ImageUrl(fileid, width, height, mode, 0);
return ImageUrl(fileid, width, height, mode, "", "", 0, false);
}

public string ImageUrl(int fileid, int width, int height, string mode, int rotate)
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, rotate);
return Utilities.ImageUrl(PageState.Alias, fileid, width, height, mode, position, background, rotate, recreate);
}
}
}
15 changes: 9 additions & 6 deletions Oqtane.Client/UI/SiteRouter.razor
Original file line number Diff line number Diff line change
Expand Up @@ -83,15 +83,15 @@
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);
return;
}
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;
Expand All @@ -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);
Expand All @@ -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
Expand Down
66 changes: 36 additions & 30 deletions Oqtane.Server/Controllers/FileController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -508,8 +508,8 @@ private IActionResult Download(int id, bool asAttachment)
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))
Expand All @@ -519,21 +519,25 @@ public IActionResult GetImage(int id, int width, int height, string mode, string
var filepath = _files.GetFilePath(file);
if (System.IO.File.Exists(filepath))
{
mode = (string.IsNullOrEmpty(mode)) ? "crop" : mode;
rotate = (string.IsNullOrEmpty(rotate)) ? "0" : rotate;

string imagepath = filepath.Replace(Path.GetExtension(filepath), "." + width.ToString() + "x" + height.ToString() + "." + mode.ToLower() + ".png");
if (!System.IO.File.Exists(imagepath))
// 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() + ".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;
}
}
Expand Down Expand Up @@ -569,35 +573,37 @@ public IActionResult GetImage(int id, int width, int height, string mode, string
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.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());
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 = "";
}

Expand Down
8 changes: 8 additions & 0 deletions Oqtane.Server/Infrastructure/Jobs/HostedServiceBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,14 @@ private DateTime CalculateNextExecution(DateTime nextExecution, Job job)
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)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ public List<PageTemplate> CreateSite(Site site)
new Permission(PermissionNames.View, RoleNames.Admin, true),
new Permission(PermissionNames.Edit, RoleNames.Admin, true)
}.EncodePermissions(),
Content = "<p>Copyright (c) 2019-2021 .NET Foundation</p>" +
Content = "<p>Copyright (c) 2018-2022 .NET Foundation</p>" +
"<p>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:</p>" +
"<p>The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.</p>" +
"<p>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.</p>"
Expand Down
9 changes: 6 additions & 3 deletions Oqtane.Shared/Shared/Utilities.cs
Original file line number Diff line number Diff line change
Expand Up @@ -114,13 +114,16 @@ public static string ContentUrl(Alias alias, int fileId, bool asAttachment)

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)
Expand Down