Compare commits
77 Commits
Author | SHA1 | Date | |
---|---|---|---|
861dde8627 | |||
69d1f3aa53 | |||
cc9802a0d8 | |||
ea4587d842 | |||
fb4c95f945 | |||
95a27af5f2 | |||
9d7b25ade6 | |||
3a8f4199cd | |||
11002efc02 | |||
367c1c3568 | |||
9e04230d99 | |||
21304db7c9 | |||
f4f6e98045 | |||
ad41eff38a | |||
dbd6cc4148 | |||
dda71e5ccd | |||
cfe8059176 | |||
8b00784ecc | |||
9bcc6bbad0 | |||
ce7995966d | |||
cea5f86df4 | |||
0912253b1b | |||
5aecc4be03 | |||
e09178c14c | |||
477ded6a4a | |||
311c48becb | |||
ec924a7ddf | |||
e39416a786 | |||
51b356cc0e | |||
66b13bdb8b | |||
4ade58da01 | |||
efcfc0783c | |||
aa22db7fe5 | |||
5e0f008b65 | |||
eaf840e1da | |||
fc9e47778b | |||
35edf78aed | |||
07718f0449 | |||
6759156519 | |||
e2688e6feb | |||
65ba6423b1 | |||
a2f8fe3694 | |||
5273a17ab6 | |||
f7c1e7b706 | |||
45bbc4c681 | |||
6af5682548 | |||
24ed06626d | |||
eeff4af167 | |||
17f46afe14 | |||
224618cf21 | |||
ea93ab2a83 | |||
b9f7c39550 | |||
86b4b8e43a | |||
f54d07548e | |||
037db8a3e4 | |||
8f00e85abd | |||
9ccc4c4059 | |||
8408f98693 | |||
cde271fd5b | |||
c21a097fd2 | |||
83c32d4963 | |||
22c2d56da0 | |||
bd8d6e0480 | |||
825eb700b1 | |||
e59ee70f88 | |||
1173a29ed5 | |||
6a2ff369ea | |||
e22606ae79 | |||
bf56c2a9fa | |||
6567b55ea3 | |||
20e90c0de4 | |||
2892d5ec6f | |||
e034811e92 | |||
ee18bbd145 | |||
e3ebbde767 | |||
6a57980439 | |||
765760f3a5 |
2
LICENSE
2
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
|
||||
|
@ -42,6 +42,9 @@
|
||||
[Parameter]
|
||||
public int VisitorId { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public string RemoteIPAddress { get; set; }
|
||||
|
||||
private bool _initialized = false;
|
||||
private string _display = "display: none;";
|
||||
private Installation _installation = new Installation { Success = false, Message = "" };
|
||||
@ -50,6 +53,7 @@
|
||||
|
||||
protected override async Task OnParametersSetAsync()
|
||||
{
|
||||
SiteState.RemoteIPAddress = RemoteIPAddress;
|
||||
SiteState.AntiForgeryToken = AntiForgeryToken;
|
||||
InstallationService.SetAntiForgeryTokenHeader(AntiForgeryToken);
|
||||
|
||||
|
@ -27,6 +27,6 @@
|
||||
protected override void OnInitialized()
|
||||
{
|
||||
var admin = PageState.Pages.FirstOrDefault(item => item.Path == "admin");
|
||||
_pages = PageState.Pages.Where(item => item.ParentId == admin?.PageId).ToList();
|
||||
_pages = PageState.Pages.Where(item => item.ParentId == admin?.PageId && !item.IsDeleted).ToList();
|
||||
}
|
||||
}
|
||||
|
@ -36,7 +36,9 @@
|
||||
<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>
|
||||
<option value="O">@Localizer["Once"]</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
@ -170,7 +172,14 @@
|
||||
job.JobType = _jobType;
|
||||
job.IsEnabled = Boolean.Parse(_isEnabled);
|
||||
job.Frequency = _frequency;
|
||||
if (job.Frequency == "O") // once
|
||||
{
|
||||
job.Interval = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
job.Interval = int.Parse(_interval);
|
||||
}
|
||||
job.StartDate = _startDate;
|
||||
if (job.StartDate != null)
|
||||
{
|
||||
|
@ -83,28 +83,28 @@ else
|
||||
|
||||
private string DisplayFrequency(int interval, string frequency)
|
||||
{
|
||||
var result = $"{Localizer["Every"]} {interval.ToString()} ";
|
||||
var result = "";
|
||||
switch (frequency)
|
||||
{
|
||||
case "m":
|
||||
result += Localizer["Minute"];
|
||||
result = $"{Localizer["Every"]} {interval.ToString()} " + Localizer["Minute"];
|
||||
break;
|
||||
case "H":
|
||||
result += Localizer["Hour"];
|
||||
result = $"{Localizer["Every"]} {interval.ToString()} " + Localizer["Hour"];
|
||||
break;
|
||||
case "d":
|
||||
result += Localizer["Day"];
|
||||
result = $"{Localizer["Every"]} {interval.ToString()} " + Localizer["Day"];
|
||||
break;
|
||||
case "w":
|
||||
result = $"{Localizer["Every"]} {interval.ToString()} " + Localizer["Week"];
|
||||
break;
|
||||
case "M":
|
||||
result += Localizer["Month"];
|
||||
result = $"{Localizer["Every"]} {interval.ToString()} " + Localizer["Month"];
|
||||
break;
|
||||
case "O":
|
||||
result = Localizer["Once"];
|
||||
break;
|
||||
}
|
||||
|
||||
if (interval > 1)
|
||||
{
|
||||
result += Localizer["s"];
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -114,6 +114,7 @@ else
|
||||
{
|
||||
await JobService.DeleteJobAsync(job.JobId);
|
||||
await logger.LogInformation("Job Deleted {Job}", job);
|
||||
_jobs = await JobService.GetJobsAsync();
|
||||
StateHasChanged();
|
||||
}
|
||||
catch (Exception ex)
|
||||
@ -124,13 +125,37 @@ else
|
||||
}
|
||||
|
||||
private async Task StartJob(int jobId)
|
||||
{
|
||||
try
|
||||
{
|
||||
await JobService.StartJobAsync(jobId);
|
||||
await logger.LogInformation("Job Started {JobId}", jobId);
|
||||
AddModuleMessage(Localizer["Message.Job.Start"], MessageType.Success);
|
||||
_jobs = await JobService.GetJobsAsync();
|
||||
StateHasChanged();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
await logger.LogError(ex, "Error Starting Job {JobId} {Error}", jobId, ex.Message);
|
||||
AddModuleMessage(Localizer["Error.Job.Start"], MessageType.Error);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task StopJob(int jobId)
|
||||
{
|
||||
try
|
||||
{
|
||||
await JobService.StopJobAsync(jobId);
|
||||
await logger.LogInformation("Job Stopped {JobId}", jobId);
|
||||
AddModuleMessage(Localizer["Message.Job.Stop"], MessageType.Success);
|
||||
_jobs = await JobService.GetJobsAsync();
|
||||
StateHasChanged();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
await logger.LogError(ex, "Error Stopping Job {JobId} {Error}", jobId, ex.Message);
|
||||
AddModuleMessage(Localizer["Error.Job.Stop"], MessageType.Error);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task Refresh()
|
||||
|
@ -177,17 +177,17 @@
|
||||
{
|
||||
await UserService.ForgotPasswordAsync(user);
|
||||
await logger.LogInformation(LogFunction.Security, "Password Reset Notification Sent For Username {Username}", _username);
|
||||
_message = "Please Check The Email Address Associated To Your User Account For A Password Reset Notification";
|
||||
_message = Localizer["Message.ForgotUser"];
|
||||
}
|
||||
else
|
||||
{
|
||||
_message = "User Does Not Exist";
|
||||
_message = Localizer["Message.UserDoesNotExist"];
|
||||
_type = MessageType.Warning;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_message = "Please Enter The Username Related To Your Account And Then Select The Forgot Password Option Again";
|
||||
_message = Localizer["Message.ForgotPassword"];
|
||||
}
|
||||
|
||||
StateHasChanged();
|
||||
|
@ -1,6 +1,7 @@
|
||||
@namespace Oqtane.Modules.Admin.Logs
|
||||
@inherits ModuleBase
|
||||
@inject ILogService LogService
|
||||
@inject ISettingService SettingService
|
||||
@inject IStringLocalizer<Index> Localizer
|
||||
@inject IStringLocalizer<SharedResources> SharedLocalizer
|
||||
|
||||
@ -10,6 +11,8 @@
|
||||
}
|
||||
else
|
||||
{
|
||||
<TabStrip>
|
||||
<TabPanel Name="Events" Heading="Events" ResourceKey="Events">
|
||||
<div class="container g-0">
|
||||
<div class="row mb-1 align-items-center">
|
||||
<div class="col-sm-4">
|
||||
@ -46,6 +49,7 @@ else
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<br />
|
||||
|
||||
@if (_logs.Any())
|
||||
{
|
||||
@ -70,6 +74,20 @@ else
|
||||
{
|
||||
<p><em>@Localizer["NoLogs"]</em></p>
|
||||
}
|
||||
</TabPanel>
|
||||
<TabPanel Name="Settings" Heading="Settings" ResourceKey="Settings">
|
||||
<div class="container">
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="retention" HelpText="Number of days of events to retain" ResourceKey="Retention">Retention (Days): </Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="retention" class="form-control" @bind="@_retention" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<br />
|
||||
<button type="button" class="btn btn-success" @onclick="SaveSiteSettings">@SharedLocalizer["Save"]</button>
|
||||
</TabPanel>
|
||||
</TabStrip>
|
||||
}
|
||||
|
||||
@code {
|
||||
@ -77,6 +95,7 @@ else
|
||||
private string _function = "-";
|
||||
private string _rows = "10";
|
||||
private List<Log> _logs;
|
||||
private string _retention = "";
|
||||
|
||||
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Host;
|
||||
|
||||
@ -85,6 +104,7 @@ else
|
||||
try
|
||||
{
|
||||
await GetLogs();
|
||||
_retention = SettingService.GetSetting(PageState.Site.Settings, "LogRetention", "30");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@ -170,4 +190,22 @@ else
|
||||
}
|
||||
return classname;
|
||||
}
|
||||
|
||||
private async Task SaveSiteSettings()
|
||||
{
|
||||
try
|
||||
{
|
||||
var settings = await SettingService.GetSiteSettingsAsync(PageState.Site.SiteId);
|
||||
settings = SettingService.SetSetting(settings, "LogRetention", _retention, true);
|
||||
await SettingService.UpdateSiteSettingsAsync(settings, PageState.Site.SiteId);
|
||||
|
||||
AddModuleMessage(Localizer["Success.SaveSiteSettings"], MessageType.Success);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
await logger.LogError(ex, "Error Saving Site Settings {Error}", ex.Message);
|
||||
AddModuleMessage(Localizer["Error.SaveSiteSettings"], MessageType.Error);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -128,7 +128,7 @@ else
|
||||
var moduleDefinition = new ModuleDefinition { Owner = _owner, Name = _module, Description = _description, Template = _template, Version = _reference };
|
||||
moduleDefinition = await ModuleDefinitionService.CreateModuleDefinitionAsync(moduleDefinition);
|
||||
|
||||
var settings = ModuleState.Settings;
|
||||
var settings = await SettingService.GetModuleSettingsAsync(ModuleState.ModuleId);
|
||||
SettingService.SetSetting(settings, "ModuleDefinitionName", moduleDefinition.ModuleDefinitionName);
|
||||
await SettingService.UpdateModuleSettingsAsync(settings, ModuleState.ModuleId);
|
||||
|
||||
|
@ -7,21 +7,28 @@
|
||||
|
||||
<form @ref="form" class="@(validated ? "was-validated" : "needs-validation")" novalidate>
|
||||
<div class="container">
|
||||
<div class="form-group">
|
||||
<label for="Username" class="control-label">@SharedLocalizer["Username"] </label>
|
||||
<input type="text" class="form-control" placeholder="Username" @bind="@_username" readonly id="Username" />
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="username" HelpText="Your username will be populated from the link you received in the password reset notification" ResourceKey="Username">Username: </Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="username" type="text" class="form-control" @bind="@_username" readonly />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="Password" class="control-label">@SharedLocalizer["Password"] </label>
|
||||
<input type="password" class="form-control" placeholder="Password" @bind="@_password" id="Password" required />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="Confirm" class="control-label">@Localizer["Password.Confirm"] </label>
|
||||
<input type="password" class="form-control" placeholder="Password" @bind="@_confirm" id="Confirm" required />
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="password" HelpText="The new password. It must satisfy complexity rules for the site." ResourceKey="Password">Password: </Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="password" type="password" class="form-control" @bind="@_password" required />
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="confirm" HelpText="Enter the password again. It must exactly match the password entered above." ResourceKey="Confirm">Confirm: </Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="confirm" type="password" class="form-control" @bind="@_confirm" required />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<br />
|
||||
<button type="button" class="btn btn-primary" @onclick="Reset">@Localizer["Password.Reset"]</button>
|
||||
<button type="button" class="btn btn-secondary" @onclick="Cancel">@SharedLocalizer["Cancel"]</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
@code {
|
||||
@ -33,7 +40,7 @@
|
||||
|
||||
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Anonymous;
|
||||
|
||||
protected override void OnInitialized()
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
if (PageState.QueryString.ContainsKey("name") && PageState.QueryString.ContainsKey("token"))
|
||||
{
|
||||
@ -41,7 +48,8 @@
|
||||
}
|
||||
else
|
||||
{
|
||||
NavigationManager.NavigateTo(NavigateUrl(string.Empty));
|
||||
await logger.LogError(LogFunction.Security, "Invalid Attempt To Access User Password Reset");
|
||||
NavigationManager.NavigateTo(NavigateUrl("")); // home page
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,6 @@
|
||||
@namespace Oqtane.Modules.Admin.Site
|
||||
@inherits ModuleBase
|
||||
@using System.Text.RegularExpressions
|
||||
@inject NavigationManager NavigationManager
|
||||
@inject ISiteService SiteService
|
||||
@inject ITenantService TenantService
|
||||
@ -21,31 +22,6 @@
|
||||
<input id="name" class="form-control" @bind="@_name" maxlength="200" required />
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="alias" HelpText="The aliases for the site. An alias can be a domain name (www.site.com) or a virtual folder (ie. www.site.com/folder). If a site has multiple aliases they should be separated by commas." ResourceKey="Aliases">Aliases: </Label>
|
||||
<div class="col-sm-9">
|
||||
@if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host))
|
||||
{
|
||||
<textarea id="alias" class="form-control" @bind="@_urls" rows="3" required></textarea>
|
||||
}
|
||||
else
|
||||
{
|
||||
<textarea id="alias" class="form-control" @bind="@_urls" rows="3" readonly></textarea>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="isDeleted" HelpText="Is this site deleted?" ResourceKey="IsDeleted">Is Deleted? </Label>
|
||||
<div class="col-sm-9">
|
||||
<select id="isDeleted" class="form-select" @bind="@_isdeleted" required>
|
||||
<option value="True">@SharedLocalizer["Yes"]</option>
|
||||
<option value="False">@SharedLocalizer["No"]</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<Section Name="Appearance" Heading="Appearance" ResourceKey="Appearance">
|
||||
<div class="container">
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="logo" HelpText="Specify a logo for the site" ResourceKey="Logo">Logo: </Label>
|
||||
<div class="col-sm-9">
|
||||
@ -94,9 +70,17 @@
|
||||
}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="isDeleted" HelpText="Is this site deleted?" ResourceKey="IsDeleted">Deleted? </Label>
|
||||
<div class="col-sm-9">
|
||||
<select id="isDeleted" class="form-select" @bind="@_isdeleted" required>
|
||||
<option value="True">@SharedLocalizer["Yes"]</option>
|
||||
<option value="False">@SharedLocalizer["No"]</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Section>
|
||||
<Section Name="SMTP" Heading="SMTP Settings" ResourceKey="SMTPSettings">
|
||||
<div class="container">
|
||||
<div class="row mb-1 align-items-center">
|
||||
@ -174,8 +158,29 @@
|
||||
</div>
|
||||
</div>
|
||||
</Section>
|
||||
@if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host))
|
||||
@if (_aliases != null && UserSecurity.IsAuthorized(PageState.User, RoleNames.Host))
|
||||
{
|
||||
<Section Name="Aliases" Heading="Aliases" ResourceKey="Aliases">
|
||||
<div class="container">
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="alias" HelpText="The aliases for the site. An alias can be a domain name (www.site.com) or a virtual folder (ie. www.site.com/folder). If a site has multiple aliases they should be separated by commas." ResourceKey="Aliases">Aliases: </Label>
|
||||
<div class="col-sm-9">
|
||||
<textarea id="alias" class="form-control" @bind="@_urls" rows="3" required></textarea>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="defaultalias" HelpText="The default alias for the site. Requests for non-default aliases will be redirected to the default alias." ResourceKey="DefaultAlias">Default Alias: </Label>
|
||||
<div class="col-sm-9">
|
||||
<select id="defaultalias" class="form-select" @bind="@_defaultalias" required>
|
||||
@foreach (string name in _urls.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries).Select(sValue => sValue.Trim()).ToArray())
|
||||
{
|
||||
<option value="@name">@name</option>
|
||||
}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Section>
|
||||
<Section Name="Hosting" Heading="Hosting Model" ResourceKey="Hosting">
|
||||
<div class="container">
|
||||
<div class="row mb-1 align-items-center">
|
||||
@ -238,7 +243,8 @@
|
||||
private List<ThemeControl> _themes = new List<ThemeControl>();
|
||||
private List<ThemeControl> _containers = new List<ThemeControl>();
|
||||
private string _name = string.Empty;
|
||||
private List<Alias> _aliasList;
|
||||
private List<Alias> _aliases;
|
||||
private string _defaultalias = string.Empty;
|
||||
private string _urls = string.Empty;
|
||||
private string _runtime = "";
|
||||
private string _prerender = "";
|
||||
@ -288,13 +294,7 @@
|
||||
|
||||
if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host))
|
||||
{
|
||||
_aliasList = await AliasService.GetAliasesAsync();
|
||||
foreach (Alias alias in _aliasList.Where(item => item.SiteId == site.SiteId && item.TenantId == site.TenantId).ToList())
|
||||
{
|
||||
_urls += alias.Name + ",";
|
||||
}
|
||||
_urls = _urls.Substring(0, _urls.Length - 1);
|
||||
|
||||
await GetAliases();
|
||||
}
|
||||
|
||||
if (site.LogoFileId != null)
|
||||
@ -398,13 +398,17 @@
|
||||
var unique = true;
|
||||
if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host))
|
||||
{
|
||||
foreach (string name in _urls.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries))
|
||||
_urls = Regex.Replace(_urls, @"\r\n?|\n", ","); // convert line breaks to commas
|
||||
var aliases = await AliasService.GetAliasesAsync();
|
||||
foreach (string name in _urls.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries).Select(sValue => sValue.Trim()).ToArray())
|
||||
{
|
||||
if (_aliasList.Exists(item => item.Name == name && item.SiteId != PageState.Alias.SiteId && item.TenantId != PageState.Alias.TenantId))
|
||||
var alias = aliases.Where(item => item.Name == name).FirstOrDefault();
|
||||
if (alias != null && unique)
|
||||
{
|
||||
unique = false;
|
||||
unique = (alias.TenantId == PageState.Site.TenantId && alias.SiteId == PageState.Site.SiteId);
|
||||
}
|
||||
}
|
||||
if (unique && string.IsNullOrEmpty(_defaultalias)) unique = false;
|
||||
}
|
||||
|
||||
if (unique)
|
||||
@ -422,7 +426,6 @@
|
||||
{
|
||||
site.Runtime = _runtime;
|
||||
site.RenderMode = _runtime + _prerender;
|
||||
refresh = true;
|
||||
reload = true; // needs to be reloaded on server
|
||||
}
|
||||
}
|
||||
@ -476,20 +479,21 @@
|
||||
site = await SiteService.UpdateSiteAsync(site);
|
||||
|
||||
var settings = await SettingService.GetSiteSettingsAsync(site.SiteId);
|
||||
SettingService.SetSetting(settings, "SMTPHost", _smtphost);
|
||||
SettingService.SetSetting(settings, "SMTPPort", _smtpport);
|
||||
SettingService.SetSetting(settings, "SMTPSSL", _smtpssl);
|
||||
SettingService.SetSetting(settings, "SMTPUsername", _smtpusername);
|
||||
SettingService.SetSetting(settings, "SMTPPassword", _smtppassword);
|
||||
SettingService.SetSetting(settings, "SMTPSender", _smtpsender);
|
||||
SettingService.SetSetting(settings, "SMTPHost", _smtphost, true);
|
||||
SettingService.SetSetting(settings, "SMTPPort", _smtpport, true);
|
||||
SettingService.SetSetting(settings, "SMTPSSL", _smtpssl, true);
|
||||
SettingService.SetSetting(settings, "SMTPUsername", _smtpusername, true);
|
||||
SettingService.SetSetting(settings, "SMTPPassword", _smtppassword, true);
|
||||
SettingService.SetSetting(settings, "SMTPSender", _smtpsender, true);
|
||||
await SettingService.UpdateSiteSettingsAsync(settings, site.SiteId);
|
||||
|
||||
if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host))
|
||||
{
|
||||
var names = _urls.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
|
||||
foreach (Alias alias in _aliasList.Where(item => item.SiteId == site.SiteId && item.TenantId == site.TenantId).ToList())
|
||||
var names = _urls.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries)
|
||||
.Select(sValue => sValue.Trim()).ToArray();
|
||||
foreach (Alias alias in _aliases)
|
||||
{
|
||||
if (!names.Contains(alias.Name))
|
||||
if (!names.Contains(alias.Name.Trim()))
|
||||
{
|
||||
await AliasService.DeleteAliasAsync(alias.AliasId);
|
||||
}
|
||||
@ -497,30 +501,43 @@
|
||||
|
||||
foreach (string name in names)
|
||||
{
|
||||
if (!_aliasList.Exists(item => item.Name == name))
|
||||
var alias = _aliases.Find(item => item.Name.Trim() == name);
|
||||
if (alias == null)
|
||||
{
|
||||
Alias alias = new Alias();
|
||||
alias = new Alias();
|
||||
alias.Name = name;
|
||||
alias.TenantId = site.TenantId;
|
||||
alias.SiteId = site.SiteId;
|
||||
alias.IsDefault = (name == _defaultalias);
|
||||
await AliasService.AddAliasAsync(alias);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (alias.Name != name || alias.IsDefault != (alias.Name.Trim() == _defaultalias))
|
||||
{
|
||||
alias.Name = name;
|
||||
alias.IsDefault = (name == _defaultalias);
|
||||
await AliasService.UpdateAliasAsync(alias);
|
||||
}
|
||||
}
|
||||
}
|
||||
await GetAliases();
|
||||
}
|
||||
|
||||
await logger.LogInformation("Site Settings Saved {Site}", site);
|
||||
|
||||
if (refresh)
|
||||
if (refresh || reload)
|
||||
{
|
||||
NavigationManager.NavigateTo(NavigateUrl(true), reload); // refresh/reload
|
||||
}
|
||||
else
|
||||
{
|
||||
AddModuleMessage(Localizer["Success.Settings.SaveSite"], MessageType.Success);
|
||||
await interop.ScrollTo(0, 0, "smooth");
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
else // deuplicate alias or default alias not specified
|
||||
{
|
||||
AddModuleMessage(Localizer["Message.Aliases.Taken"], MessageType.Warning);
|
||||
}
|
||||
@ -579,12 +596,12 @@
|
||||
try
|
||||
{
|
||||
var settings = await SettingService.GetSiteSettingsAsync(PageState.Site.SiteId);
|
||||
SettingService.SetSetting(settings, "SMTPHost", _smtphost);
|
||||
SettingService.SetSetting(settings, "SMTPPort", _smtpport);
|
||||
SettingService.SetSetting(settings, "SMTPSSL", _smtpssl);
|
||||
SettingService.SetSetting(settings, "SMTPUsername", _smtpusername);
|
||||
SettingService.SetSetting(settings, "SMTPPassword", _smtppassword);
|
||||
SettingService.SetSetting(settings, "SMTPSender", _smtpsender);
|
||||
SettingService.SetSetting(settings, "SMTPHost", _smtphost, true);
|
||||
SettingService.SetSetting(settings, "SMTPPort", _smtpport, true);
|
||||
SettingService.SetSetting(settings, "SMTPSSL", _smtpssl, true);
|
||||
SettingService.SetSetting(settings, "SMTPUsername", _smtpusername, true);
|
||||
SettingService.SetSetting(settings, "SMTPPassword", _smtppassword, true);
|
||||
SettingService.SetSetting(settings, "SMTPSender", _smtpsender, true);
|
||||
await SettingService.UpdateSiteSettingsAsync(settings, PageState.Site.SiteId);
|
||||
await logger.LogInformation("Site SMTP Settings Saved");
|
||||
|
||||
@ -602,4 +619,18 @@
|
||||
AddModuleMessage(Localizer["Message.required.Smtp"], MessageType.Warning);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task GetAliases()
|
||||
{
|
||||
_urls = string.Empty;
|
||||
_defaultalias = string.Empty;
|
||||
_aliases = await AliasService.GetAliasesAsync();
|
||||
_aliases = _aliases.Where(item => item.SiteId == PageState.Site.SiteId && item.TenantId == PageState.Site.TenantId).OrderBy(item => item.AliasId).ToList();
|
||||
foreach (Alias alias in _aliases)
|
||||
{
|
||||
_urls += (_urls == string.Empty) ? alias.Name.Trim() : ", " + alias.Name.Trim();
|
||||
if (alias.IsDefault && string.IsNullOrEmpty(_defaultalias)) _defaultalias = alias.Name.Trim();
|
||||
}
|
||||
if (string.IsNullOrEmpty(_defaultalias)) _defaultalias = _aliases.First().Name.Trim();
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
@namespace Oqtane.Modules.Admin.Sites
|
||||
@using Oqtane.Interfaces
|
||||
@using System.Text.RegularExpressions
|
||||
@inherits ModuleBase
|
||||
@inject NavigationManager NavigationManager
|
||||
@inject ITenantService TenantService
|
||||
@ -282,6 +283,7 @@ else
|
||||
{
|
||||
if (_tenantid != "-" && _name != string.Empty && _urls != string.Empty && _themetype != "-" && _containertype != "-" && _sitetemplatetype != "-")
|
||||
{
|
||||
_urls = Regex.Replace(_urls, @"\r\n?|\n", ",");
|
||||
var duplicates = new List<string>();
|
||||
var aliases = await AliasService.GetAliasesAsync();
|
||||
foreach (string name in _urls.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries))
|
||||
@ -370,8 +372,7 @@ else
|
||||
if (installation.Success)
|
||||
{
|
||||
var aliasname = config.Aliases.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries)[0];
|
||||
var uri = new Uri(NavigationManager.Uri);
|
||||
NavigationManager.NavigateTo(uri.Scheme + "://" + aliasname, true);
|
||||
NavigationManager.NavigateTo(PageState.Uri.Scheme + "://" + aliasname, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -30,20 +30,16 @@ else
|
||||
|
||||
@code {
|
||||
private List<Alias> _sites;
|
||||
private string _scheme;
|
||||
|
||||
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Host;
|
||||
|
||||
protected override async Task OnParametersSetAsync()
|
||||
{
|
||||
var uri = new Uri(NavigationManager.Uri);
|
||||
_scheme = uri.Scheme + "://";
|
||||
|
||||
var aliases = await AliasService.GetAliasesAsync();
|
||||
_sites = new List<Alias>();
|
||||
foreach (Alias alias in aliases)
|
||||
{
|
||||
if (!_sites.Exists(item => item.TenantId == alias.TenantId && item.SiteId == alias.SiteId))
|
||||
if (alias.IsDefault && !_sites.Exists(item => item.TenantId == alias.TenantId && item.SiteId == alias.SiteId))
|
||||
{
|
||||
_sites.Add(alias);
|
||||
}
|
||||
@ -52,16 +48,11 @@ else
|
||||
|
||||
private void Edit(string name)
|
||||
{
|
||||
if (name.Equals("*"))
|
||||
{
|
||||
var uri = new Uri(NavigationManager.Uri);
|
||||
name = uri.Authority;
|
||||
}
|
||||
NavigationManager.NavigateTo(_scheme + name + "/admin/site/?reload");
|
||||
NavigationManager.NavigateTo(PageState.Uri.Scheme + "://" + name + "/admin/site", true);
|
||||
}
|
||||
|
||||
private void Browse(string name)
|
||||
{
|
||||
NavigationManager.NavigateTo(_scheme + name + "/?reload");
|
||||
NavigationManager.NavigateTo(PageState.Uri.Scheme + "://" + name, true);
|
||||
}
|
||||
}
|
||||
|
@ -57,7 +57,6 @@ else
|
||||
{
|
||||
@if (_results.Count > 0)
|
||||
{
|
||||
<div class="table-responsive">
|
||||
<Pager Class="table table-bordered" Items="@_results">
|
||||
<Header>
|
||||
@foreach (KeyValuePair<string, string> kvp in _results.First())
|
||||
@ -72,7 +71,6 @@ else
|
||||
}
|
||||
</Row>
|
||||
</Pager>
|
||||
</div>
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -33,7 +33,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="servertime" HelpText="Server Time" ResourceKey="ServerTime">Server Time: </Label>
|
||||
<Label Class="col-sm-3" For="servertime" HelpText="Server Date/Time (in UTC)" ResourceKey="ServerTime">Server Date/Time: </Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="servertime" class="form-control" @bind="@_servertime" readonly />
|
||||
</div>
|
||||
@ -123,7 +123,7 @@
|
||||
_clrversion = systeminfo["clrversion"];
|
||||
_osversion = systeminfo["osversion"];
|
||||
_serverpath = systeminfo["serverpath"];
|
||||
_servertime = systeminfo["servertime"];
|
||||
_servertime = systeminfo["servertime"] + " UTC";
|
||||
_installationid = systeminfo["installationid"];
|
||||
|
||||
_detailederrors = systeminfo["detailederrors"];
|
||||
|
@ -40,13 +40,21 @@
|
||||
var interop = new Interop(JSRuntime);
|
||||
if (await interop.FormValid(form))
|
||||
{
|
||||
var route = new Route(_url, PageState.Alias.Path);
|
||||
var url = route.SiteUrl + "/" + route.PagePath;
|
||||
if (_url != _mappedurl)
|
||||
{
|
||||
var url = PageState.Uri.Scheme + "://" + PageState.Uri.Authority + "/";
|
||||
url = url + (!string.IsNullOrEmpty(PageState.Alias.Path) ? PageState.Alias.Path + "/" : "");
|
||||
|
||||
_url = (_url.StartsWith("/")) ? _url.Substring(1) : _url;
|
||||
_url = (!_url.StartsWith("http")) ? url + _url : _url;
|
||||
|
||||
if (_url.StartsWith(url))
|
||||
{
|
||||
var urlmapping = new UrlMapping();
|
||||
urlmapping.SiteId = PageState.Site.SiteId;
|
||||
urlmapping.Url = url;
|
||||
urlmapping.MappedUrl = _mappedurl;
|
||||
var route = new Route(_url, PageState.Alias.Path);
|
||||
urlmapping.Url = route.PagePath;
|
||||
urlmapping.MappedUrl = _mappedurl.Replace(url, "");
|
||||
urlmapping.Requests = 0;
|
||||
urlmapping.CreatedOn = DateTime.UtcNow;
|
||||
urlmapping.RequestedOn = DateTime.UtcNow;
|
||||
@ -64,6 +72,16 @@
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
AddModuleMessage(Localizer["Message.SaveUrlMapping"], MessageType.Warning);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
AddModuleMessage(Localizer["Message.DuplicateUrlMapping"], MessageType.Warning);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
AddModuleMessage(SharedLocalizer["Message.InfoRequired"], MessageType.Warning);
|
||||
}
|
||||
|
@ -60,23 +60,32 @@
|
||||
var interop = new Interop(JSRuntime);
|
||||
if (await interop.FormValid(form))
|
||||
{
|
||||
var urlmapping = await UrlMappingService.GetUrlMappingAsync(_urlmappingid);
|
||||
urlmapping.MappedUrl = _mappedurl;
|
||||
|
||||
if (_url != _mappedurl)
|
||||
{
|
||||
try
|
||||
{
|
||||
var url = PageState.Uri.Scheme + "://" + PageState.Uri.Authority + "/";
|
||||
url = url + (!string.IsNullOrEmpty(PageState.Alias.Path) ? PageState.Alias.Path + "/" : "");
|
||||
|
||||
var urlmapping = await UrlMappingService.GetUrlMappingAsync(_urlmappingid);
|
||||
urlmapping.MappedUrl = _mappedurl.Replace(url, "");
|
||||
urlmapping = await UrlMappingService.UpdateUrlMappingAsync(urlmapping);
|
||||
await logger.LogInformation("UrlMapping Saved {UrlMapping}", urlmapping);
|
||||
NavigationManager.NavigateTo(NavigateUrl());
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
await logger.LogError(ex, "Error Saving UrlMapping {UrlMapping} {Error}", urlmapping, ex.Message);
|
||||
await logger.LogError(ex, "Error Saving UrlMapping {UrlMappingId} {Error}", _urlmappingid, ex.Message);
|
||||
AddModuleMessage(Localizer["Error.SaveUrlMapping"], MessageType.Error);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
AddModuleMessage(Localizer["Message.DuplicateUrlMapping"], MessageType.Warning);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
AddModuleMessage(SharedLocalizer["Message.InfoRequired"], MessageType.Warning);
|
||||
}
|
||||
}
|
||||
|
@ -40,10 +40,10 @@ else
|
||||
<td><ActionLink Action="Edit" Parameters="@($"id=" + context.UrlMappingId.ToString())" ResourceKey="Edit" /></td>
|
||||
<td><ActionDialog Header="Delete Url Mapping" Message="@string.Format(Localizer["Confirm.DeleteUrlMapping"], context.Url)" Action="Delete" Security="SecurityAccessLevel.Admin" Class="btn btn-danger" OnClick="@(async () => await DeleteUrlMapping(context))" ResourceKey="DeleteUrlMapping" /></td>
|
||||
<td>
|
||||
<a href="" onclick="@(() => BrowseUrl(context.Url))">@context.Url</a>
|
||||
<a href="@Utilities.TenantUrl(PageState.Alias, context.Url)">@context.Url</a>
|
||||
@if (_mapped)
|
||||
{
|
||||
@((MarkupString)"<br />>> ")<a href="" onclick="@(() => BrowseUrl(context.MappedUrl))">@context.MappedUrl</a>
|
||||
@((MarkupString)"<br />>> ")<a href="@((context.MappedUrl.StartsWith("http") ? context.MappedUrl : Utilities.TenantUrl(PageState.Alias, context.MappedUrl)))">@context.MappedUrl</a>
|
||||
}
|
||||
</td>
|
||||
<td>@context.Requests</td>
|
||||
@ -96,11 +96,6 @@ else
|
||||
}
|
||||
}
|
||||
|
||||
private void BrowseUrl(string url)
|
||||
{
|
||||
NavigationManager.NavigateTo(url, true);
|
||||
}
|
||||
|
||||
private async Task DeleteUrlMapping(UrlMapping urlMapping)
|
||||
{
|
||||
try
|
||||
|
@ -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
|
||||
{
|
||||
|
@ -89,7 +89,7 @@ else
|
||||
{
|
||||
var results = allroles.Where(item => item.Role.Name == RoleNames.Registered || (item.Role.Name == RoleNames.Host && UserSecurity.IsAuthorized(PageState.User, RoleNames.Host)));
|
||||
|
||||
if (string.IsNullOrEmpty(_search))
|
||||
if (!string.IsNullOrEmpty(_search))
|
||||
{
|
||||
results = results.Where(item =>
|
||||
(
|
||||
|
@ -2,6 +2,7 @@
|
||||
@inherits ModuleBase
|
||||
@inject IVisitorService VisitorService
|
||||
@inject ISiteService SiteService
|
||||
@inject ISettingService SettingService
|
||||
@inject IStringLocalizer<Index> Localizer
|
||||
@inject IStringLocalizer<SharedResources> SharedLocalizer
|
||||
|
||||
@ -60,14 +61,26 @@ else
|
||||
<TabPanel Name="Settings" Heading="Settings" ResourceKey="Settings">
|
||||
<div class="container">
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="visitortracking" HelpText="Specify if visitor tracking is enabled" ResourceKey="VisitorTracking">Visitor Tracking Enabled? </Label>
|
||||
<Label Class="col-sm-3" For="tracking" HelpText="Specify if visitor tracking is enabled" ResourceKey="Tracking">Tracking Enabled? </Label>
|
||||
<div class="col-sm-9">
|
||||
<select id="visitortracking" class="form-select" @bind="@_visitortracking" >
|
||||
<select id="tracking" class="form-select" @bind="@_tracking" >
|
||||
<option value="True">@SharedLocalizer["Yes"]</option>
|
||||
<option value="False">@SharedLocalizer["No"]</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="filter" HelpText="Comma delimited list of terms which may exist in IP addresses, user agents, or languages which identify visitors which should not be tracked (ie. bots)" ResourceKey="Filter">Filter: </Label>
|
||||
<div class="col-sm-9">
|
||||
<textarea id="filter" class="form-control" @bind="@_filter" rows="3"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="retention" HelpText="Number of days of visitor activity to retain" ResourceKey="Retention">Retention (Days): </Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="retention" class="form-control" @bind="@_retention" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<br />
|
||||
<button type="button" class="btn btn-success" @onclick="SaveSiteSettings">@SharedLocalizer["Save"]</button>
|
||||
@ -79,14 +92,18 @@ else
|
||||
private bool _users = false;
|
||||
private int _days = 1;
|
||||
private List<Visitor> _visitors;
|
||||
private string _visitortracking;
|
||||
private string _tracking;
|
||||
private string _filter = "";
|
||||
private string _retention = "";
|
||||
|
||||
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Admin;
|
||||
|
||||
protected override async Task OnParametersSetAsync()
|
||||
{
|
||||
await GetVisitors();
|
||||
_visitortracking = PageState.Site.VisitorTracking.ToString();
|
||||
_tracking = PageState.Site.VisitorTracking.ToString();
|
||||
_filter = SettingService.GetSetting(PageState.Site.Settings, "VisitorFilter", "");
|
||||
_retention = SettingService.GetSetting(PageState.Site.Settings, "VisitorRetention", "30");
|
||||
}
|
||||
|
||||
private async void TypeChanged(ChangeEventArgs e)
|
||||
@ -131,8 +148,14 @@ else
|
||||
try
|
||||
{
|
||||
var site = PageState.Site;
|
||||
site.VisitorTracking = bool.Parse(_visitortracking);
|
||||
site.VisitorTracking = bool.Parse(_tracking);
|
||||
await SiteService.UpdateSiteAsync(site);
|
||||
|
||||
var settings = await SettingService.GetSiteSettingsAsync(PageState.Site.SiteId);
|
||||
settings = SettingService.SetSetting(settings, "VisitorFilter", _filter, true);
|
||||
settings = SettingService.SetSetting(settings, "VisitorRetention", _retention, true);
|
||||
await SettingService.UpdateSiteSettingsAsync(settings, PageState.Site.SiteId);
|
||||
|
||||
AddModuleMessage(Localizer["Success.SaveSiteSettings"], MessageType.Success);
|
||||
}
|
||||
catch (Exception ex)
|
||||
|
@ -25,8 +25,8 @@ else
|
||||
[Parameter]
|
||||
public string HelpText { get; set; } // optional - tooltip for this label
|
||||
|
||||
private string _spanclass = "app-tooltip";
|
||||
private string _labelclass = "form-label";
|
||||
private string _spanclass;
|
||||
private string _labelclass;
|
||||
private string _helptext = string.Empty;
|
||||
|
||||
protected override void OnParametersSet()
|
||||
@ -36,11 +36,15 @@ else
|
||||
if (!string.IsNullOrEmpty(HelpText))
|
||||
{
|
||||
_helptext = Localize(nameof(HelpText), HelpText);
|
||||
_spanclass += (!string.IsNullOrEmpty(Class)) ? " " + Class : "";
|
||||
_labelclass = "form-label";
|
||||
|
||||
var spanclass = (!string.IsNullOrEmpty(Class)) ? " " + Class : "";
|
||||
_spanclass = "app-tooltip" + spanclass;
|
||||
}
|
||||
else
|
||||
{
|
||||
_labelclass += (!string.IsNullOrEmpty(Class)) ? " " + Class : "";
|
||||
var labelclass = (!string.IsNullOrEmpty(Class)) ? " " + Class : "";
|
||||
_labelclass = "form-label" + labelclass;
|
||||
}
|
||||
|
||||
var text = Localize("Text", String.Empty);
|
||||
|
@ -13,6 +13,9 @@ namespace Oqtane.Modules.Controls
|
||||
[Parameter]
|
||||
public string ResourceKey { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public string ResourceType { get; set; }
|
||||
|
||||
protected bool IsLocalizable { get; private set; }
|
||||
|
||||
protected string Localize(string name) => _localizer?[name] ?? name;
|
||||
@ -50,9 +53,14 @@ namespace Oqtane.Modules.Controls
|
||||
{
|
||||
IsLocalizable = false;
|
||||
|
||||
if (!String.IsNullOrEmpty(ResourceKey) && ModuleState?.ModuleType != null)
|
||||
if (string.IsNullOrEmpty(ResourceType))
|
||||
{
|
||||
var moduleType = Type.GetType(ModuleState.ModuleType);
|
||||
ResourceType = ModuleState?.ModuleType;
|
||||
}
|
||||
|
||||
if (!String.IsNullOrEmpty(ResourceKey) && !string.IsNullOrEmpty(ResourceType))
|
||||
{
|
||||
var moduleType = Type.GetType(ResourceType);
|
||||
if (moduleType != null)
|
||||
{
|
||||
using (var scope = ServiceActivator.GetScope())
|
||||
|
@ -54,6 +54,7 @@
|
||||
}
|
||||
@if (Format == "Table" && Row != null)
|
||||
{
|
||||
<div class="table-responsive">
|
||||
<table class="@Class">
|
||||
<thead>
|
||||
<tr>@Header</tr>
|
||||
@ -69,6 +70,7 @@
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
}
|
||||
@if (Format == "Grid" && Row != null)
|
||||
{
|
||||
@ -107,7 +109,7 @@
|
||||
<li class="page-item@((_page > 1) ? "" : " disabled")">
|
||||
<a class="page-link" @onclick=@(async () => UpdateList(1))><span class="oi oi-media-step-backward" title="start" aria-hidden="true"></span></a>
|
||||
</li>
|
||||
@if (_pages > _displayPages)
|
||||
@if (_pages > _displayPages && _displayPages > 1)
|
||||
{
|
||||
<li class="page-item@((_page > _displayPages) ? "" : " disabled")">
|
||||
<a class="page-link" @onclick=@(async () => SkipPages("back"))><span class="oi oi-media-skip-backward" title="skip back" aria-hidden="true"></span></a>
|
||||
@ -135,7 +137,7 @@
|
||||
<li class="page-item@((_page < _pages) ? "" : " disabled")">
|
||||
<a class="page-link" @onclick=@(async () => NavigateToPage("next"))><span class="oi oi-chevron-right" title="next" aria-hidden="true"></span></a>
|
||||
</li>
|
||||
@if (_pages > _displayPages)
|
||||
@if (_pages > _displayPages && _displayPages > 1)
|
||||
{
|
||||
<li class="page-item@((_endPage < _pages) ? "" : " disabled")">
|
||||
<a class="page-link" @onclick=@(async () => SkipPages("forward"))><span class="oi oi-media-skip-forward" title="skip forward" aria-hidden="true"></span></a>
|
||||
@ -145,7 +147,7 @@
|
||||
<a class="page-link" @onclick=@(async () => UpdateList(_pages))><span class="oi oi-media-step-forward" title="end" aria-hidden="true"></span></a>
|
||||
</li>
|
||||
<li class="page-item disabled">
|
||||
<a class="page-link">Page @_page of @_pages</a>
|
||||
<a class="page-link" style="white-space: nowrap;">Page @_page of @_pages</a>
|
||||
</li>
|
||||
</ul>
|
||||
}
|
||||
|
@ -120,19 +120,20 @@
|
||||
new Resource { ResourceType = ResourceType.Script, Bundle = "Quill", Url = "js/quill-interop.js" }
|
||||
};
|
||||
|
||||
protected override void OnParametersSet()
|
||||
protected override async Task OnParametersSetAsync()
|
||||
{
|
||||
_content = Content; // raw HTML
|
||||
await RefreshRichText();
|
||||
}
|
||||
|
||||
protected override async Task OnAfterRenderAsync(bool firstRender)
|
||||
{
|
||||
var interop = new RichTextEditorInterop(JSRuntime);
|
||||
|
||||
if (firstRender)
|
||||
{
|
||||
await base.OnAfterRenderAsync(firstRender);
|
||||
|
||||
|
||||
await interop.CreateEditor(
|
||||
_editorElement,
|
||||
_toolBar,
|
||||
@ -140,15 +141,14 @@
|
||||
Placeholder,
|
||||
Theme,
|
||||
DebugLevel);
|
||||
}
|
||||
|
||||
await interop.LoadEditorContent(_editorElement, Content);
|
||||
|
||||
_content = Content; // raw HTML
|
||||
|
||||
}
|
||||
// preserve a copy of the rich text content ( Quill sanitizes content so we need to retrieve it from the editor )
|
||||
_original = await interop.GetHtml(_editorElement);
|
||||
|
||||
}
|
||||
|
||||
public void CloseFileManager()
|
||||
|
@ -7,7 +7,7 @@
|
||||
|
||||
<div class="container">
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="files" ResourceKey="Allow File Management" HelpText="Specify If Editors Can Upload and Select Files">Allow File Management: </Label>
|
||||
<Label Class="col-sm-3" For="files" ResourceKey="AllowFileManagement" ResourceType="@resourceType" HelpText="Specify If Editors Can Upload and Select Files">Allow File Management: </Label>
|
||||
<div class="col-sm-9">
|
||||
<select id="files" class="form-select" @bind="@_allowfilemanagement">
|
||||
<option value="true">@SharedLocalizer["Yes"]</option>
|
||||
@ -18,6 +18,7 @@
|
||||
</div>
|
||||
|
||||
@code {
|
||||
private string resourceType = "Oqtane.Modules.HtmlText.Settings, Oqtane.Client"; // for localization
|
||||
private string _allowfilemanagement;
|
||||
|
||||
protected override void OnInitialized()
|
||||
@ -36,7 +37,7 @@
|
||||
{
|
||||
try
|
||||
{
|
||||
var settings = ModuleState.Settings;
|
||||
var settings = await SettingService.GetModuleSettingsAsync(ModuleState.ModuleId);
|
||||
settings = SettingService.SetSetting(settings, "AllowFileManagement", _allowfilemanagement);
|
||||
await SettingService.UpdateModuleSettingsAsync(settings, ModuleState.ModuleId);
|
||||
}
|
||||
|
@ -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<string, string> GetUrlParameters(string parametersTemplate = "")
|
||||
|
@ -5,7 +5,7 @@
|
||||
<OutputType>Exe</OutputType>
|
||||
<RazorLangVersion>3.0</RazorLangVersion>
|
||||
<Configurations>Debug;Release</Configurations>
|
||||
<Version>3.0.1</Version>
|
||||
<Version>3.0.2</Version>
|
||||
<Product>Oqtane</Product>
|
||||
<Authors>Shaun Walker</Authors>
|
||||
<Company>.NET Foundation</Company>
|
||||
@ -13,7 +13,7 @@
|
||||
<Copyright>.NET Foundation</Copyright>
|
||||
<PackageProjectUrl>https://www.oqtane.org</PackageProjectUrl>
|
||||
<PackageLicenseUrl>https://github.com/oqtane/oqtane.framework/blob/dev/LICENSE</PackageLicenseUrl>
|
||||
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.0.1</PackageReleaseNotes>
|
||||
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.0.2</PackageReleaseNotes>
|
||||
<RepositoryUrl>https://github.com/oqtane/oqtane.framework</RepositoryUrl>
|
||||
<RepositoryType>Git</RepositoryType>
|
||||
<RootNamespace>Oqtane</RootNamespace>
|
||||
|
@ -186,4 +186,10 @@
|
||||
<data name="NextExecution.Text" xml:space="preserve">
|
||||
<value>Next Execution: </value>
|
||||
</data>
|
||||
<data name="Week(s)" xml:space="preserve">
|
||||
<value>Week(s)</value>
|
||||
</data>
|
||||
<data name="Once" xml:space="preserve">
|
||||
<value>Execute Once</value>
|
||||
</data>
|
||||
</root>
|
@ -133,19 +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>
|
||||
</data>
|
||||
<data name="s" xml:space="preserve">
|
||||
<value>s</value>
|
||||
<value>Month(s)</value>
|
||||
</data>
|
||||
<data name="Error.Job.Delete" xml:space="preserve">
|
||||
<value>Error Deleting Job</value>
|
||||
@ -177,4 +174,22 @@
|
||||
<data name="JobLog.Text" xml:space="preserve">
|
||||
<value>Log</value>
|
||||
</data>
|
||||
<data name="Error.Job.Start" xml:space="preserve">
|
||||
<value>An error occurred when starting the job</value>
|
||||
</data>
|
||||
<data name="Error.Job.Stop" xml:space="preserve">
|
||||
<value>An error occurred when stopping the job</value>
|
||||
</data>
|
||||
<data name="Message.Job.Start" xml:space="preserve">
|
||||
<value>The process responsible for executing this job has been started. The next execution will be based on the schedule criteria for the job.</value>
|
||||
</data>
|
||||
<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>
|
||||
<data name="Once" xml:space="preserve">
|
||||
<value>Execute Once</value>
|
||||
</data>
|
||||
</root>
|
@ -138,4 +138,13 @@
|
||||
<data name="Info.SignedIn" xml:space="preserve">
|
||||
<value>You Are Already Signed In</value>
|
||||
</data>
|
||||
<data name="Message.ForgotPassword" xml:space="preserve">
|
||||
<value>Please Enter The Username Related To Your Account And Then Select The Forgot Password Option Again</value>
|
||||
</data>
|
||||
<data name="Message.ForgotUser" xml:space="preserve">
|
||||
<value>Please Check The Email Address Associated To Your User Account For A Password Reset Notification</value>
|
||||
</data>
|
||||
<data name="Message.UserDoesNotExist" xml:space="preserve">
|
||||
<value>User Does Not Exist</value>
|
||||
</data>
|
||||
</root>
|
@ -192,4 +192,22 @@
|
||||
<data name="LogDetails.Text" xml:space="preserve">
|
||||
<value>Details</value>
|
||||
</data>
|
||||
<data name="Error.SaveSiteSettings" xml:space="preserve">
|
||||
<value>Error Saving Settings</value>
|
||||
</data>
|
||||
<data name="Events.Heading" xml:space="preserve">
|
||||
<value>Events</value>
|
||||
</data>
|
||||
<data name="Retention.HelpText" xml:space="preserve">
|
||||
<value>Number of days of events to retain</value>
|
||||
</data>
|
||||
<data name="Retention.Text" xml:space="preserve">
|
||||
<value>Retention (Days):</value>
|
||||
</data>
|
||||
<data name="Settings.Heading" xml:space="preserve">
|
||||
<value>Settings</value>
|
||||
</data>
|
||||
<data name="Success.SaveSiteSettings" xml:space="preserve">
|
||||
<value>Settings Saved Successfully</value>
|
||||
</data>
|
||||
</root>
|
@ -258,9 +258,6 @@
|
||||
<data name="ThemeSettings.Heading" xml:space="preserve">
|
||||
<value>Theme Settings</value>
|
||||
</data>
|
||||
<data name="Appearance.Hea" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
<data name="Clickable.HelpText" xml:space="preserve">
|
||||
<value>Select whether the link in the site navigation is enabled or disabled</value>
|
||||
</data>
|
||||
|
@ -117,22 +117,37 @@
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<data name="Password.Confirm" xml:space="preserve">
|
||||
<value>Confirm Password:</value>
|
||||
</data>
|
||||
<data name="Message.Password.NoMatch" xml:space="preserve">
|
||||
<value>Passwords Entered Do Not Match</value>
|
||||
</data>
|
||||
<data name="Message.Required.UserInfo" xml:space="preserve">
|
||||
<value>You Must Provide A Username, Password, and Email Address</value>
|
||||
<value>You Must Provide The Password And Confirmation</value>
|
||||
</data>
|
||||
<data name="Password.Reset" xml:space="preserve">
|
||||
<value>Reset Password</value>
|
||||
</data>
|
||||
<data name="Error.Password.ResetInfo" xml:space="preserve">
|
||||
<value>Error Resetting User Password. Please Ensure Password Meets Complexity Requirements.</value>
|
||||
<value>Error Resetting User Password. Please Ensure The Request To Reset Your Password Was Made Within The Past 24 Hours And The New Password Meets The Complexity Requirements.</value>
|
||||
</data>
|
||||
<data name="Error.Password.Reset" xml:space="preserve">
|
||||
<value>Error Resetting User Password</value>
|
||||
</data>
|
||||
<data name="Confirm.Text" xml:space="preserve">
|
||||
<value>Confirm:</value>
|
||||
</data>
|
||||
<data name="Conform.HelpText" xml:space="preserve">
|
||||
<value>Enter the password again. It must exactly match the password entered above.</value>
|
||||
</data>
|
||||
<data name="Password.HelpText" xml:space="preserve">
|
||||
<value>The new password. It must satisfy complexity rules for the site.</value>
|
||||
</data>
|
||||
<data name="Password.Text" xml:space="preserve">
|
||||
<value>Password:</value>
|
||||
</data>
|
||||
<data name="Username.HelpText" xml:space="preserve">
|
||||
<value>Your username will be populated from the link you received in the password reset notification</value>
|
||||
</data>
|
||||
<data name="Username.Text" xml:space="preserve">
|
||||
<value>Username:</value>
|
||||
</data>
|
||||
</root>
|
@ -129,9 +129,6 @@
|
||||
<data name="DefaultContainer.Text" xml:space="preserve">
|
||||
<value>Default Container: </value>
|
||||
</data>
|
||||
<data name="Appearance.Heading" xml:space="preserve">
|
||||
<value>Appearance</value>
|
||||
</data>
|
||||
<data name="DefaultAdminContainer" xml:space="preserve">
|
||||
<value>Default Admin Container</value>
|
||||
</data>
|
||||
@ -145,7 +142,7 @@
|
||||
<value>Site Settings Saved</value>
|
||||
</data>
|
||||
<data name="Message.Aliases.Taken" xml:space="preserve">
|
||||
<value>An Alias Specified Has Already Been Used For Another Site</value>
|
||||
<value>The Default Alias Has Not Been Specified Or An Alias Was Specified That Has Already Been Used For Another Site</value>
|
||||
</data>
|
||||
<data name="Message.Required.SiteName" xml:space="preserve">
|
||||
<value>You Must Provide A Site Name, Alias, And Default Theme/Container</value>
|
||||
@ -223,7 +220,7 @@
|
||||
<value>Aliases: </value>
|
||||
</data>
|
||||
<data name="IsDeleted.Text" xml:space="preserve">
|
||||
<value>Is Deleted? </value>
|
||||
<value>Deleted? </value>
|
||||
</data>
|
||||
<data name="Logo.Text" xml:space="preserve">
|
||||
<value>Logo: </value>
|
||||
@ -318,4 +315,13 @@
|
||||
<data name="DeleteSite.Text" xml:space="preserve">
|
||||
<value>Delete Site</value>
|
||||
</data>
|
||||
<data name="DefaultAlias.HelpText" xml:space="preserve">
|
||||
<value>The default alias for the site. Requests for non-default aliases will be redirected to the default alias.</value>
|
||||
</data>
|
||||
<data name="DefaultAlias.Text" xml:space="preserve">
|
||||
<value>Default Alias: </value>
|
||||
</data>
|
||||
<data name="Aliases.Heading" xml:space="preserve">
|
||||
<value>Aliases</value>
|
||||
</data>
|
||||
</root>
|
@ -133,7 +133,7 @@
|
||||
<value>Server Path</value>
|
||||
</data>
|
||||
<data name="ServerTime.HelpText" xml:space="preserve">
|
||||
<value>Server Time</value>
|
||||
<value>Server Date/Time (in UTC)</value>
|
||||
</data>
|
||||
<data name="FrameworkVersion.Text" xml:space="preserve">
|
||||
<value>Framework Version: </value>
|
||||
@ -148,7 +148,7 @@
|
||||
<value>Server Path: </value>
|
||||
</data>
|
||||
<data name="ServerTime.Text" xml:space="preserve">
|
||||
<value>Server Time: </value>
|
||||
<value>Server Date/Time: </value>
|
||||
</data>
|
||||
<data name="RestartApplication.Header" xml:space="preserve">
|
||||
<value>Restart Application</value>
|
||||
|
@ -121,10 +121,10 @@
|
||||
<value>Redirect To:</value>
|
||||
</data>
|
||||
<data name="MappedUrl.HelpText" xml:space="preserve">
|
||||
<value>A fully qualified Url where the user will be redirected</value>
|
||||
<value>A relative or absolute Url where the user will be redirected</value>
|
||||
</data>
|
||||
<data name="Url.HelpText" xml:space="preserve">
|
||||
<value>A fully qualified Url for this site</value>
|
||||
<value>An absolute Url identifying a path to a specific page in the site</value>
|
||||
</data>
|
||||
<data name="Url.Text" xml:space="preserve">
|
||||
<value>Url:</value>
|
||||
@ -135,4 +135,10 @@
|
||||
<data name="Message.InfoRequired" xml:space="preserve">
|
||||
<value>Please Provide All Required Information</value>
|
||||
</data>
|
||||
<data name="Message.DuplicateUrlMapping" xml:space="preserve">
|
||||
<value>The Url and Redirect To cannot be the same</value>
|
||||
</data>
|
||||
<data name="Message.SaveUrlMapping" xml:space="preserve">
|
||||
<value>The Url must belong to the current site</value>
|
||||
</data>
|
||||
</root>
|
@ -121,10 +121,10 @@
|
||||
<value>Redirect To:</value>
|
||||
</data>
|
||||
<data name="MappedUrl.HelpText" xml:space="preserve">
|
||||
<value>A fully qualified Url where the user will be redirected</value>
|
||||
<value>A relative or absolute Url where the user will be redirected</value>
|
||||
</data>
|
||||
<data name="Url.HelpText" xml:space="preserve">
|
||||
<value>A fully qualified Url for this site</value>
|
||||
<value>A relative Url identifying a path to a specific page in the site</value>
|
||||
</data>
|
||||
<data name="Url.Text" xml:space="preserve">
|
||||
<value>Url:</value>
|
||||
@ -138,4 +138,7 @@
|
||||
<data name="Message.InfoRequired" xml:space="preserve">
|
||||
<value>Please Provide All Required Information</value>
|
||||
</data>
|
||||
<data name="Message.DuplicateUrlMapping" xml:space="preserve">
|
||||
<value>The Url and Redirect To cannot be the same</value>
|
||||
</data>
|
||||
</root>
|
@ -156,11 +156,11 @@
|
||||
<data name="Visitors.Heading" xml:space="preserve">
|
||||
<value>Visitors</value>
|
||||
</data>
|
||||
<data name="VisitorTracking.HelpText" xml:space="preserve">
|
||||
<data name="Tracking.HelpText" xml:space="preserve">
|
||||
<value>Specify if visitor tracking is enabled</value>
|
||||
</data>
|
||||
<data name="VisitorTracking.Text" xml:space="preserve">
|
||||
<value>Visitor Tracking Enabled?</value>
|
||||
<data name="Tracking.Text" xml:space="preserve">
|
||||
<value>Tracking Enabled?</value>
|
||||
</data>
|
||||
<data name="IP" xml:space="preserve">
|
||||
<value>IP</value>
|
||||
@ -171,4 +171,19 @@
|
||||
<data name="Created" xml:space="preserve">
|
||||
<value>Created</value>
|
||||
</data>
|
||||
<data name="Details.Text" xml:space="preserve">
|
||||
<value>Details</value>
|
||||
</data>
|
||||
<data name="Filter.HelpText" xml:space="preserve">
|
||||
<value>Comma delimited list of terms which may exist in IP addresses, user agents, or languages which identify visitors which should not be tracked (ie. bots)</value>
|
||||
</data>
|
||||
<data name="Filter.Text" xml:space="preserve">
|
||||
<value>Filter:</value>
|
||||
</data>
|
||||
<data name="Retention.HelpText" xml:space="preserve">
|
||||
<value>Number of days of visitor activity to retain</value>
|
||||
</data>
|
||||
<data name="Retention.Text" xml:space="preserve">
|
||||
<value>Retention (Days):</value>
|
||||
</data>
|
||||
</root>
|
@ -177,4 +177,13 @@
|
||||
<data name="System.Update" xml:space="preserve">
|
||||
<value>Check For System Updates</value>
|
||||
</data>
|
||||
<data name="Visibility" xml:space="preserve">
|
||||
<value>Visibility:</value>
|
||||
</data>
|
||||
<data name="VisibilityEdit" xml:space="preserve">
|
||||
<value>Page Editors Only</value>
|
||||
</data>
|
||||
<data name="VisibilityView" xml:space="preserve">
|
||||
<value>Same As Page</value>
|
||||
</data>
|
||||
</root>
|
@ -59,7 +59,7 @@
|
||||
: using a System.ComponentModel.TypeConverter
|
||||
: and then encoded with base64 encoding.
|
||||
-->
|
||||
<xsd:schema xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata" id="root">
|
||||
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||
<xsd:element name="root" msdata:IsDataSet="true">
|
||||
<xsd:complexType>
|
@ -121,7 +121,7 @@
|
||||
<value>Specify if a Footer pane should always be displayed in a fixed location at the bottom of the page.</value>
|
||||
</data>
|
||||
<data name="Footer.Text" xml:space="preserve">
|
||||
<value>Display Footer?</value>
|
||||
<value>Display Fixed Footer?</value>
|
||||
</data>
|
||||
<data name="Login.HelpText" xml:space="preserve">
|
||||
<value>Specify if a Login option should be displayed, Note that this option does not prevent the login page from being accessible via a direct url.</value>
|
@ -206,7 +206,7 @@ namespace Oqtane.Services
|
||||
/// <returns></returns>
|
||||
Dictionary<string, string> SetSetting(Dictionary<string, string> settings, string settingName, string settingValue);
|
||||
|
||||
Dictionary<string, string> SetSetting(Dictionary<string, string> settings, string settingName, string settingValue, bool isPublic);
|
||||
Dictionary<string, string> SetSetting(Dictionary<string, string> settings, string settingName, string settingValue, bool isPrivate);
|
||||
|
||||
Dictionary<string, string> MergeSettings(Dictionary<string, string> settings1, Dictionary<string, string> settings2);
|
||||
|
||||
|
@ -24,6 +24,14 @@ namespace Oqtane.Services
|
||||
/// <returns></returns>
|
||||
Task<UrlMapping> GetUrlMappingAsync(int urlMappingId);
|
||||
|
||||
/// <summary>
|
||||
/// Get one specific <see cref="UrlMapping"/>
|
||||
/// </summary>
|
||||
/// <param name="siteId">ID-reference of a <see cref="Site"/></param>
|
||||
/// <param name="url">A url</param>
|
||||
/// <returns></returns>
|
||||
Task<UrlMapping> GetUrlMappingAsync(int siteId, string url);
|
||||
|
||||
/// <summary>
|
||||
/// Add / save a new <see cref="UrlMapping"/> to the database.
|
||||
/// </summary>
|
||||
|
@ -131,14 +131,21 @@ namespace Oqtane.Services
|
||||
foreach (KeyValuePair<string, string> kvp in settings)
|
||||
{
|
||||
string value = kvp.Value;
|
||||
bool ispublic = false;
|
||||
bool modified = false;
|
||||
bool isprivate = false;
|
||||
|
||||
// manage settings modified with SetSetting method
|
||||
if (value.StartsWith("[Private]"))
|
||||
{
|
||||
modified = true;
|
||||
isprivate = true;
|
||||
value = value.Substring(9);
|
||||
}
|
||||
if (value.StartsWith("[Public]"))
|
||||
{
|
||||
if (entityName == EntityNames.Site)
|
||||
{
|
||||
ispublic = true;
|
||||
}
|
||||
value = value.Substring(8); // remove [Public]
|
||||
modified = true;
|
||||
isprivate = false;
|
||||
value = value.Substring(8);
|
||||
}
|
||||
|
||||
Setting setting = settingsList.FirstOrDefault(item => item.SettingName.Equals(kvp.Key, StringComparison.OrdinalIgnoreCase));
|
||||
@ -149,22 +156,21 @@ namespace Oqtane.Services
|
||||
setting.EntityId = entityId;
|
||||
setting.SettingName = kvp.Key;
|
||||
setting.SettingValue = value;
|
||||
setting.IsPublic = ispublic;
|
||||
setting.IsPrivate = isprivate;
|
||||
setting = await AddSettingAsync(setting);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (setting.SettingValue != kvp.Value)
|
||||
if (setting.SettingValue != value || (modified && setting.IsPrivate != isprivate))
|
||||
{
|
||||
setting.SettingValue = value;
|
||||
setting.IsPublic = ispublic;
|
||||
setting.IsPrivate = isprivate;
|
||||
setting = await UpdateSettingAsync(setting);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public async Task<Setting> GetSettingAsync(string entityName, int settingId)
|
||||
{
|
||||
return await GetJsonAsync<Setting>($"{Apiurl}/{settingId}/{entityName}");
|
||||
@ -201,13 +207,13 @@ namespace Oqtane.Services
|
||||
return SetSetting(settings, settingName, settingValue, false);
|
||||
}
|
||||
|
||||
public Dictionary<string, string> SetSetting(Dictionary<string, string> settings, string settingName, string settingValue, bool isPublic)
|
||||
public Dictionary<string, string> SetSetting(Dictionary<string, string> settings, string settingName, string settingValue, bool isPrivate)
|
||||
{
|
||||
if (settings == null)
|
||||
{
|
||||
settings = new Dictionary<string, string>();
|
||||
}
|
||||
settingValue = (isPublic) ? "[Public]" + settingValue : settingValue;
|
||||
settingValue = (isPrivate) ? "[Private]" + settingValue : "[Public]" + settingValue;
|
||||
if (settings.ContainsKey(settingName))
|
||||
{
|
||||
settings[settingName] = settingValue;
|
||||
|
@ -5,6 +5,7 @@ using System.Linq;
|
||||
using System.Collections.Generic;
|
||||
using Oqtane.Documentation;
|
||||
using Oqtane.Shared;
|
||||
using System.Net;
|
||||
|
||||
namespace Oqtane.Services
|
||||
{
|
||||
@ -33,6 +34,11 @@ namespace Oqtane.Services
|
||||
return await GetJsonAsync<UrlMapping>($"{Apiurl}/{urlMappingId}");
|
||||
}
|
||||
|
||||
public async Task<UrlMapping> GetUrlMappingAsync(int siteId, string url)
|
||||
{
|
||||
return await GetJsonAsync<UrlMapping>($"{Apiurl}/url/{siteId}?url={WebUtility.UrlEncode(url)}");
|
||||
}
|
||||
|
||||
public async Task<UrlMapping> AddUrlMappingAsync(UrlMapping role)
|
||||
{
|
||||
return await PostJsonAsync<UrlMapping>(Apiurl, role);
|
||||
|
@ -1,12 +1,19 @@
|
||||
@namespace Oqtane.Themes.BlazorTheme
|
||||
@inherits ContainerBase
|
||||
<div class="container">
|
||||
@if (ModuleState.Title != "-")
|
||||
{
|
||||
<div class="row p-4">
|
||||
<div class="d-flex flex-nowrap">
|
||||
<ModuleActions /><h2><ModuleTitle /></h2>
|
||||
</div>
|
||||
<hr class="app-rule" />
|
||||
</div>
|
||||
}
|
||||
else
|
||||
{
|
||||
<ModuleActions />
|
||||
}
|
||||
<div class="row px-4">
|
||||
<div class="container">
|
||||
<ModuleInstance />
|
||||
|
@ -61,14 +61,13 @@
|
||||
<label class="control-label">@Localizer["Page.Manage"] </label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row d-flex">
|
||||
<div class="row d-flex mb-2">
|
||||
<div class="col d-flex justify-content-between">
|
||||
<button type="button" class="btn btn-secondary col me-1" data-bs-dismiss="offcanvas" @onclick=@(async () => Navigate("Add"))>@SharedLocalizer["Add"]</button>
|
||||
<button type="button" class="btn btn-secondary col" data-bs-dismiss="offcanvas" @onclick=@(async () => Navigate("Edit"))>@SharedLocalizer["Edit"]</button>
|
||||
<button type="button" class="btn btn-danger col ms-1" @onclick="ConfirmDelete">@SharedLocalizer["Delete"]</button>
|
||||
</div>
|
||||
</div>
|
||||
<br />
|
||||
<div class="row d-flex">
|
||||
<div class="col">
|
||||
@if (UserSecurity.GetPermissionStrings(PageState.Page.Permissions).FirstOrDefault(item => item.PermissionName == PermissionNames.View).Permissions.Split(';').Contains(RoleNames.Everyone))
|
||||
@ -150,7 +149,6 @@
|
||||
}
|
||||
}
|
||||
</select>
|
||||
@((MarkupString) Description)
|
||||
}
|
||||
}
|
||||
else
|
||||
@ -203,21 +201,23 @@
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<br />
|
||||
<button type="button" class="btn btn-primary col-12" @onclick="@AddModule">@Localizer["Page.Module.Add"]</button>
|
||||
<div class="row">
|
||||
<div class="col text-center">
|
||||
<label for="visibility" class="control-label">@Localizer["Visibility"]</label>
|
||||
<select class="form-select" @bind="@Visibility">
|
||||
<option value="edit" selected>@Localizer["VisibilityEdit"]</option>
|
||||
<option value="view">@Localizer["VisibilityView"]</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<button type="button" class="btn btn-primary col-12 mt-4" @onclick="@AddModule">@Localizer["Page.Module.Add"]</button>
|
||||
@((MarkupString) Message)
|
||||
@if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host))
|
||||
{
|
||||
<hr class="app-rule" />
|
||||
<NavLink class="btn btn-info col-12" data-bs-dismiss="offcanvas" href="@NavigateUrl("admin/update")">@Localizer["System.Update"]</NavLink>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
@code{
|
||||
|
||||
private bool _deleteConfirmation = false;
|
||||
private List<string> _categories = new List<string>();
|
||||
private List<ModuleDefinition> _allModuleDefinitions;
|
||||
@ -242,7 +242,7 @@
|
||||
_category = value;
|
||||
_moduleDefinitions = _allModuleDefinitions.Where(item => item.Categories.Contains(Category)).ToList();
|
||||
ModuleDefinitionName = "-";
|
||||
Description = "";
|
||||
Message = "";
|
||||
StateHasChanged();
|
||||
_ = UpdateSettingsAsync();
|
||||
}
|
||||
@ -262,11 +262,9 @@
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
protected string Description { get; private set; } = "";
|
||||
|
||||
protected string Title { get; private set; } = "";
|
||||
protected string ContainerType { get; private set; } = "";
|
||||
protected string Visibility { get; private set; } = "edit";
|
||||
protected string Message { get; private set; } = "";
|
||||
|
||||
[Parameter]
|
||||
@ -320,13 +318,12 @@
|
||||
if (ModuleDefinitionName != "-")
|
||||
{
|
||||
var moduleDefinition = _moduleDefinitions.FirstOrDefault(item => item.ModuleDefinitionName == ModuleDefinitionName);
|
||||
Description = "<br /><div class=\"alert alert-info\" role=\"alert\">" + moduleDefinition.Description + "</div>";
|
||||
Message = "<div class=\"alert alert-info mt-2 text-center\" role=\"alert\">" + moduleDefinition.Description + "</div>";
|
||||
}
|
||||
else
|
||||
{
|
||||
Description = "";
|
||||
Message = "";
|
||||
}
|
||||
|
||||
StateHasChanged();
|
||||
}
|
||||
|
||||
@ -359,9 +356,17 @@
|
||||
module.ModuleDefinitionName = ModuleDefinitionName;
|
||||
module.AllPages = false;
|
||||
|
||||
// set module view permissions to page edit permissions
|
||||
List<PermissionString> permissions = UserSecurity.GetPermissionStrings(PageState.Page.Permissions);
|
||||
if (Visibility == "view")
|
||||
{
|
||||
// set module view permissions to page view permissions
|
||||
permissions.Find(p => p.PermissionName == PermissionNames.View).Permissions = permissions.Find(p => p.PermissionName == PermissionNames.View).Permissions;
|
||||
}
|
||||
else
|
||||
{
|
||||
// set module view permissions to page edit permissions
|
||||
permissions.Find(p => p.PermissionName == PermissionNames.View).Permissions = permissions.Find(p => p.PermissionName == PermissionNames.Edit).Permissions;
|
||||
}
|
||||
module.Permissions = UserSecurity.SetPermissionStrings(permissions);
|
||||
|
||||
module = await ModuleService.AddModuleAsync(module);
|
||||
@ -399,6 +404,7 @@
|
||||
await PageModuleService.UpdatePageModuleOrderAsync(pageModule.PageId, pageModule.Pane);
|
||||
|
||||
Message = $"<div class=\"alert alert-success mt-2 text-center\" role=\"alert\">{Localizer["Success.Page.ModuleAdd"]}</div>";
|
||||
Title = "";
|
||||
NavigationManager.NavigateTo(NavigateUrl());
|
||||
}
|
||||
else
|
||||
|
@ -2,6 +2,7 @@ using System;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using Microsoft.JSInterop;
|
||||
using Oqtane.Enums;
|
||||
using Oqtane.Providers;
|
||||
using Oqtane.Security;
|
||||
using Oqtane.Services;
|
||||
@ -17,6 +18,7 @@ namespace Oqtane.Themes.Controls
|
||||
[Inject] public IJSRuntime jsRuntime { get; set; }
|
||||
[Inject] public IServiceProvider ServiceProvider { get; set; }
|
||||
[Inject] public SiteState SiteState { get; set; }
|
||||
[Inject] public ILogService LoggingService { get; set; }
|
||||
|
||||
protected void LoginUser()
|
||||
{
|
||||
@ -31,6 +33,8 @@ namespace Oqtane.Themes.Controls
|
||||
protected async Task LogoutUser()
|
||||
{
|
||||
await UserService.LogoutUserAsync(PageState.User);
|
||||
await LoggingService.Log(PageState.Alias, PageState.Page.PageId, PageState.ModuleId, PageState.User.UserId, GetType().AssemblyQualifiedName, "Logout", LogFunction.Security, LogLevel.Information, null, "User Logout For Username {Username}", PageState.User.Username);
|
||||
|
||||
PageState.User = null;
|
||||
bool authorizedtoviewpage = UserSecurity.IsAuthorized(PageState.User, PermissionNames.View, PageState.Page.Permissions);
|
||||
|
||||
|
@ -3,7 +3,7 @@
|
||||
@inject ISettingService SettingService
|
||||
|
||||
<div class="@_classes">
|
||||
@if (_title)
|
||||
@if (_title && ModuleState.Title != "-")
|
||||
{
|
||||
<div class="row px-4">
|
||||
<div class="d-flex flex-nowrap">
|
||||
|
@ -6,7 +6,7 @@
|
||||
|
||||
<div class="container">
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="title" ResourceKey="Title" HelpText="Specify If The Module Title Should Be Displayed">Display Title?</Label>
|
||||
<Label Class="col-sm-3" For="title" ResourceKey="Title" ResourceType="@resourceType" HelpText="Specify If The Module Title Should Be Displayed">Display Title?</Label>
|
||||
<div class="col-sm-9">
|
||||
<select id="title" class="form-select" @bind="@_title">
|
||||
<option value="true">Yes</option>
|
||||
@ -15,7 +15,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="background\" ResourceKey="Background" HelpText="Optionally Specify A Background Color For The Container">Background Color:</Label>
|
||||
<Label Class="col-sm-3" For="background\" ResourceKey="Background" ResourceType="@resourceType" HelpText="Optionally Specify A Background Color For The Container">Background Color:</Label>
|
||||
<div class="col-sm-9">
|
||||
<select id="background" class="form-select" @bind="@_background">
|
||||
<option value="">None</option>
|
||||
@ -31,7 +31,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="text" ResourceKey="Text" HelpText="Optionally Specify A Text Color For The Container">Text Color:</Label>
|
||||
<Label Class="col-sm-3" For="text" ResourceKey="Text" ResourceType="@resourceType" HelpText="Optionally Specify A Text Color For The Container">Text Color:</Label>
|
||||
<div class="col-sm-9">
|
||||
<select id="text" class="form-select" @bind="@_text">
|
||||
<option value="">None</option>
|
||||
@ -47,7 +47,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="border" ResourceKey="Border" HelpText="Optionally Specify A Border For The Container">Border Color:</Label>
|
||||
<Label Class="col-sm-3" For="border" ResourceKey="Border" ResourceType="@resourceType" HelpText="Optionally Specify A Border For The Container">Border Color:</Label>
|
||||
<div class="col-sm-9">
|
||||
<select id="border" class="form-select" @bind="@_border">
|
||||
<option value="">None</option>
|
||||
@ -66,6 +66,7 @@
|
||||
</div>
|
||||
|
||||
@code {
|
||||
private string resourceType = "Oqtane.Themes.OqtaneTheme.ContainerSettings, Oqtane.Client"; // for localization
|
||||
private string _title = "true";
|
||||
private string _background = "";
|
||||
private string _text = "";
|
||||
@ -90,7 +91,7 @@
|
||||
{
|
||||
try
|
||||
{
|
||||
var settings = ModuleState.Settings;
|
||||
var settings = await SettingService.GetModuleSettingsAsync(ModuleState.ModuleId);
|
||||
settings = SettingService.SetSetting(settings, GetType().Namespace + ":Title", _title);
|
||||
settings = SettingService.SetSetting(settings, GetType().Namespace + ":Background", _background);
|
||||
settings = SettingService.SetSetting(settings, GetType().Namespace + ":Text", _text);
|
||||
|
@ -8,7 +8,7 @@
|
||||
|
||||
<div class="container">
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="scope" ResourceKey="Scope" HelpText="Specify if the settings are applicable to this page or the entire site.">Setting Scope:</Label>
|
||||
<Label Class="col-sm-3" For="scope" ResourceKey="Scope" ResourceType="@resourceType" HelpText="Specify if the settings are applicable to this page or the entire site.">Setting Scope:</Label>
|
||||
<div class="col-sm-9">
|
||||
<select id="scope" class="form-select" value="@_scope" @onchange="(e => ScopeChanged(e))">
|
||||
@if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Admin))
|
||||
@ -20,7 +20,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="login" ResourceKey="Login" HelpText="Specify if a Login option should be displayed. Note that this option does not prevent the login page from being accessible via a direct url.">Show Login?</Label>
|
||||
<Label Class="col-sm-3" For="login" ResourceKey="Login" ResourceType="@resourceType" HelpText="Specify if a Login option should be displayed. Note that this option does not prevent the login page from being accessible via a direct url.">Show Login?</Label>
|
||||
<div class="col-sm-9">
|
||||
<select id="login" class="form-select" @bind="@_login">
|
||||
<option value="-"><@SharedLocalizer["Not Specified"]></option>
|
||||
@ -30,7 +30,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="register" ResourceKey="Register" HelpText="Specify if a Register option should be displayed. Note that this option is also dependent on the Allow Registration option in Site Settings.">Show Register?</Label>
|
||||
<Label Class="col-sm-3" For="register" ResourceKey="Register" ResourceType="@resourceType" HelpText="Specify if a Register option should be displayed. Note that this option is also dependent on the Allow Registration option in Site Settings.">Show Register?</Label>
|
||||
<div class="col-sm-9">
|
||||
<select id="register" class="form-select" @bind="@_register">
|
||||
<option value="-"><@SharedLocalizer["Not Specified"]></option>
|
||||
@ -40,7 +40,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="footer" ResourceKey="Footer" HelpText="Specify if a Footer pane should always be displayed in a fixed location at the bottom of the page">Display Fixed Footer?</Label>
|
||||
<Label Class="col-sm-3" For="footer" ResourceKey="Footer" ResourceType="@resourceType" HelpText="Specify if a Footer pane should always be displayed in a fixed location at the bottom of the page">Display Fixed Footer?</Label>
|
||||
<div class="col-sm-9">
|
||||
<select id="footer" class="form-select" @bind="@_footer">
|
||||
<option value="-"><@SharedLocalizer["Not Specified"]></option>
|
||||
@ -52,6 +52,7 @@
|
||||
</div>
|
||||
|
||||
@code {
|
||||
private string resourceType = "Oqtane.Themes.OqtaneTheme.ThemeSettings, Oqtane.Client"; // for localization
|
||||
private string _scope = "page";
|
||||
private string _login = "-";
|
||||
private string _register = "-";
|
||||
@ -110,7 +111,7 @@
|
||||
{
|
||||
if (_scope == "site")
|
||||
{
|
||||
var settings = PageState.Site.Settings;
|
||||
var settings = await SettingService.GetSiteSettingsAsync(PageState.Site.SiteId);
|
||||
if (_login != "-")
|
||||
{
|
||||
settings = SettingService.SetSetting(settings, GetType().Namespace + ":Login", _login, true);
|
||||
@ -127,7 +128,7 @@
|
||||
}
|
||||
else
|
||||
{
|
||||
var settings = PageState.Page.Settings;
|
||||
var settings = await SettingService.GetPageSettingsAsync(PageState.Page.PageId);
|
||||
if (_login != "-")
|
||||
{
|
||||
settings = SettingService.SetSetting(settings, GetType().Namespace + ":Login", _login);
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -262,5 +262,22 @@ namespace Oqtane.UI
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
|
||||
public Task ScrollTo(int top, int left, string behavior)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (string.IsNullOrEmpty(behavior)) behavior = "smooth";
|
||||
_jsRuntime.InvokeVoidAsync(
|
||||
"Oqtane.Interop.scrollTo",
|
||||
top, left, behavior);
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -21,5 +21,6 @@ namespace Oqtane.UI
|
||||
public DateTime LastSyncDate { get; set; }
|
||||
public Oqtane.Shared.Runtime Runtime { get; set; }
|
||||
public int VisitorId { get; set; }
|
||||
public string RemoteIPAddress { get; set; }
|
||||
}
|
||||
}
|
||||
|
@ -9,6 +9,7 @@
|
||||
@inject IPageService PageService
|
||||
@inject IUserService UserService
|
||||
@inject IModuleService ModuleService
|
||||
@inject IUrlMappingService UrlMappingService
|
||||
@inject ILogService LogService
|
||||
@implements IHandleAfterRender
|
||||
|
||||
@ -139,9 +140,12 @@
|
||||
if (authState.User.Identity.IsAuthenticated)
|
||||
{
|
||||
user = await UserService.GetUserAsync(authState.User.Identity.Name, site.SiteId);
|
||||
if (user != null)
|
||||
{
|
||||
user.IsAuthenticated = authState.User.Identity.IsAuthenticated;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
user = PageState.User;
|
||||
@ -225,17 +229,27 @@
|
||||
EditMode = editmode,
|
||||
LastSyncDate = lastsyncdate,
|
||||
Runtime = runtime,
|
||||
VisitorId = VisitorId
|
||||
VisitorId = VisitorId,
|
||||
RemoteIPAddress = SiteState.RemoteIPAddress
|
||||
};
|
||||
|
||||
OnStateChange?.Invoke(_pagestate);
|
||||
}
|
||||
}
|
||||
else
|
||||
else // page not found
|
||||
{
|
||||
// look for url mapping
|
||||
var urlMapping = await UrlMappingService.GetUrlMappingAsync(site.SiteId, route.PagePath);
|
||||
if (urlMapping != null && !string.IsNullOrEmpty(urlMapping.MappedUrl))
|
||||
{
|
||||
var url = (urlMapping.MappedUrl.StartsWith("http")) ? urlMapping.MappedUrl : route.SiteUrl + "/" + urlMapping.MappedUrl;
|
||||
NavigationManager.NavigateTo(url, false);
|
||||
}
|
||||
else // not mapped
|
||||
{
|
||||
if (user == null)
|
||||
{
|
||||
// redirect to login page
|
||||
// redirect to login page if user not logged in as they may need to be authenticated
|
||||
NavigationManager.NavigateTo(Utilities.NavigateUrl(SiteState.Alias.Path, "login", "?returnurl=" + route.AbsolutePath));
|
||||
}
|
||||
else
|
||||
@ -249,6 +263,7 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// site does not exist
|
||||
|
@ -27,23 +27,11 @@
|
||||
}
|
||||
|
||||
protected override async Task OnAfterRenderAsync(bool firstRender)
|
||||
{
|
||||
if (!firstRender)
|
||||
{
|
||||
var interop = new Interop(JsRuntime);
|
||||
|
||||
// set page title
|
||||
if (!string.IsNullOrEmpty(PageState.Page.Title))
|
||||
{
|
||||
await interop.UpdateTitle(PageState.Page.Title);
|
||||
}
|
||||
else
|
||||
{
|
||||
await interop.UpdateTitle(PageState.Site.Name + " - " + PageState.Page.Name);
|
||||
}
|
||||
|
||||
// manage stylesheets for this page
|
||||
string batch = DateTime.Now.ToString("yyyyMMddHHmmssfff");
|
||||
string batch = DateTime.UtcNow.ToString("yyyyMMddHHmmssfff");
|
||||
var links = new List<object>();
|
||||
foreach (Resource resource in PageState.Page.Resources.Where(item => item.ResourceType == ResourceType.Stylesheet && item.Declaration != ResourceDeclaration.Global))
|
||||
{
|
||||
@ -54,6 +42,15 @@
|
||||
await interop.IncludeLinks(links.ToArray());
|
||||
}
|
||||
await interop.RemoveElementsById("app-stylesheet", "", "app-stylesheet-" + batch + "-00");
|
||||
|
||||
// set page title
|
||||
if (!string.IsNullOrEmpty(PageState.Page.Title))
|
||||
{
|
||||
await interop.UpdateTitle(PageState.Page.Title);
|
||||
}
|
||||
else
|
||||
{
|
||||
await interop.UpdateTitle(PageState.Site.Name + " - " + PageState.Page.Name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<Version>3.0.1</Version>
|
||||
<Version>3.0.2</Version>
|
||||
<Product>Oqtane</Product>
|
||||
<Authors>Shaun Walker</Authors>
|
||||
<Company>.NET Foundation</Company>
|
||||
@ -10,7 +10,7 @@
|
||||
<Copyright>.NET Foundation</Copyright>
|
||||
<PackageProjectUrl>https://www.oqtane.org</PackageProjectUrl>
|
||||
<PackageLicenseUrl>https://github.com/oqtane/oqtane.framework/blob/dev/LICENSE</PackageLicenseUrl>
|
||||
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.0.1</PackageReleaseNotes>
|
||||
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.0.2</PackageReleaseNotes>
|
||||
<RepositoryUrl>https://github.com/oqtane/oqtane.framework</RepositoryUrl>
|
||||
<RepositoryType>Git</RepositoryType>
|
||||
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
|
||||
|
@ -2,7 +2,7 @@
|
||||
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
|
||||
<metadata>
|
||||
<id>Oqtane.Database.MySQL</id>
|
||||
<version>3.0.1</version>
|
||||
<version>3.0.2</version>
|
||||
<authors>Shaun Walker</authors>
|
||||
<owners>.NET Foundation</owners>
|
||||
<title>Oqtane MySQL Provider</title>
|
||||
@ -12,7 +12,7 @@
|
||||
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
||||
<license type="expression">MIT</license>
|
||||
<projectUrl>https://github.com/oqtane/oqtane.framework</projectUrl>
|
||||
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.0.1</releaseNotes>
|
||||
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.0.2</releaseNotes>
|
||||
<icon>icon.png</icon>
|
||||
<tags>oqtane</tags>
|
||||
</metadata>
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<Version>3.0.1</Version>
|
||||
<Version>3.0.2</Version>
|
||||
<Product>Oqtane</Product>
|
||||
<Authors>Shaun Walker</Authors>
|
||||
<Company>.NET Foundation</Company>
|
||||
@ -10,7 +10,7 @@
|
||||
<Copyright>.NET Foundation</Copyright>
|
||||
<PackageProjectUrl>https://www.oqtane.org</PackageProjectUrl>
|
||||
<PackageLicenseUrl>https://github.com/oqtane/oqtane.framework/blob/dev/LICENSE</PackageLicenseUrl>
|
||||
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.0.1</PackageReleaseNotes>
|
||||
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.0.2</PackageReleaseNotes>
|
||||
<RepositoryUrl>https://github.com/oqtane/oqtane.framework</RepositoryUrl>
|
||||
<RepositoryType>Git</RepositoryType>
|
||||
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
|
||||
|
@ -2,7 +2,7 @@
|
||||
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
|
||||
<metadata>
|
||||
<id>Oqtane.Database.PostgreSQL</id>
|
||||
<version>3.0.1</version>
|
||||
<version>3.0.2</version>
|
||||
<authors>Shaun Walker</authors>
|
||||
<owners>.NET Foundation</owners>
|
||||
<title>Oqtane PostgreSQL Provider</title>
|
||||
@ -12,7 +12,7 @@
|
||||
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
||||
<license type="expression">MIT</license>
|
||||
<projectUrl>https://github.com/oqtane/oqtane.framework</projectUrl>
|
||||
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.0.1</releaseNotes>
|
||||
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.0.2</releaseNotes>
|
||||
<icon>icon.png</icon>
|
||||
<tags>oqtane</tags>
|
||||
</metadata>
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<Version>3.0.1</Version>
|
||||
<Version>3.0.2</Version>
|
||||
<Product>Oqtane</Product>
|
||||
<Authors>Shaun Walker</Authors>
|
||||
<Company>.NET Foundation</Company>
|
||||
@ -10,7 +10,7 @@
|
||||
<Copyright>.NET Foundation</Copyright>
|
||||
<PackageProjectUrl>https://www.oqtane.org</PackageProjectUrl>
|
||||
<PackageLicenseUrl>https://github.com/oqtane/oqtane.framework/blob/dev/LICENSE</PackageLicenseUrl>
|
||||
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.0.1</PackageReleaseNotes>
|
||||
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.0.2</PackageReleaseNotes>
|
||||
<RepositoryUrl>https://github.com/oqtane/oqtane.framework</RepositoryUrl>
|
||||
<RepositoryType>Git</RepositoryType>
|
||||
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
|
||||
|
@ -2,7 +2,7 @@
|
||||
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
|
||||
<metadata>
|
||||
<id>Oqtane.Database.SqlServer</id>
|
||||
<version>3.0.1</version>
|
||||
<version>3.0.2</version>
|
||||
<authors>Shaun Walker</authors>
|
||||
<owners>.NET Foundation</owners>
|
||||
<title>Oqtane SQL Server Provider</title>
|
||||
@ -12,7 +12,7 @@
|
||||
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
||||
<license type="expression">MIT</license>
|
||||
<projectUrl>https://github.com/oqtane/oqtane.framework</projectUrl>
|
||||
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.0.1</releaseNotes>
|
||||
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.0.2</releaseNotes>
|
||||
<icon>icon.png</icon>
|
||||
<tags>oqtane</tags>
|
||||
</metadata>
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<Version>3.0.1</Version>
|
||||
<Version>3.0.2</Version>
|
||||
<Product>Oqtane</Product>
|
||||
<Authors>Shaun Walker</Authors>
|
||||
<Company>.NET Foundation</Company>
|
||||
@ -10,7 +10,7 @@
|
||||
<Copyright>.NET Foundation</Copyright>
|
||||
<PackageProjectUrl>https://www.oqtane.org</PackageProjectUrl>
|
||||
<PackageLicenseUrl>https://github.com/oqtane/oqtane.framework/blob/dev/LICENSE</PackageLicenseUrl>
|
||||
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.0.1</PackageReleaseNotes>
|
||||
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.0.2</PackageReleaseNotes>
|
||||
<RepositoryUrl>https://github.com/oqtane/oqtane.framework</RepositoryUrl>
|
||||
<RepositoryType>Git</RepositoryType>
|
||||
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
|
||||
|
@ -2,7 +2,7 @@
|
||||
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
|
||||
<metadata>
|
||||
<id>Oqtane.Database.Sqlite</id>
|
||||
<version>3.0.1</version>
|
||||
<version>3.0.2</version>
|
||||
<authors>Shaun Walker</authors>
|
||||
<owners>.NET Foundation</owners>
|
||||
<title>Oqtane SQLite Provider</title>
|
||||
@ -12,7 +12,7 @@
|
||||
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
||||
<license type="expression">MIT</license>
|
||||
<projectUrl>https://github.com/oqtane/oqtane.framework</projectUrl>
|
||||
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.0.1</releaseNotes>
|
||||
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.0.2</releaseNotes>
|
||||
<icon>icon.png</icon>
|
||||
<tags>oqtane</tags>
|
||||
</metadata>
|
||||
|
@ -2,7 +2,7 @@
|
||||
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
|
||||
<metadata>
|
||||
<id>Oqtane.Client</id>
|
||||
<version>3.0.1</version>
|
||||
<version>3.0.2</version>
|
||||
<authors>Shaun Walker</authors>
|
||||
<owners>.NET Foundation</owners>
|
||||
<title>Oqtane Framework</title>
|
||||
@ -12,7 +12,7 @@
|
||||
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
||||
<license type="expression">MIT</license>
|
||||
<projectUrl>https://github.com/oqtane/oqtane.framework</projectUrl>
|
||||
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.0.1</releaseNotes>
|
||||
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.0.2</releaseNotes>
|
||||
<icon>icon.png</icon>
|
||||
<tags>oqtane</tags>
|
||||
</metadata>
|
||||
|
@ -2,7 +2,7 @@
|
||||
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
|
||||
<metadata>
|
||||
<id>Oqtane.Framework</id>
|
||||
<version>3.0.1</version>
|
||||
<version>3.0.2</version>
|
||||
<authors>Shaun Walker</authors>
|
||||
<owners>.NET Foundation</owners>
|
||||
<title>Oqtane Framework</title>
|
||||
@ -12,7 +12,7 @@
|
||||
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
||||
<license type="expression">MIT</license>
|
||||
<projectUrl>https://github.com/oqtane/oqtane.framework/releases/download/v3.0.1/Oqtane.Framework.3.0.1.Upgrade.zip</projectUrl>
|
||||
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.0.1</releaseNotes>
|
||||
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.0.2</releaseNotes>
|
||||
<icon>icon.png</icon>
|
||||
<tags>oqtane framework</tags>
|
||||
</metadata>
|
||||
|
@ -2,7 +2,7 @@
|
||||
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
|
||||
<metadata>
|
||||
<id>Oqtane.Server</id>
|
||||
<version>3.0.1</version>
|
||||
<version>3.0.2</version>
|
||||
<authors>Shaun Walker</authors>
|
||||
<owners>.NET Foundation</owners>
|
||||
<title>Oqtane Framework</title>
|
||||
@ -12,7 +12,7 @@
|
||||
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
||||
<license type="expression">MIT</license>
|
||||
<projectUrl>https://github.com/oqtane/oqtane.framework</projectUrl>
|
||||
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.0.1</releaseNotes>
|
||||
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.0.2</releaseNotes>
|
||||
<icon>icon.png</icon>
|
||||
<tags>oqtane</tags>
|
||||
</metadata>
|
||||
|
@ -2,7 +2,7 @@
|
||||
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
|
||||
<metadata>
|
||||
<id>Oqtane.Shared</id>
|
||||
<version>3.0.1</version>
|
||||
<version>3.0.2</version>
|
||||
<authors>Shaun Walker</authors>
|
||||
<owners>.NET Foundation</owners>
|
||||
<title>Oqtane Framework</title>
|
||||
@ -12,7 +12,7 @@
|
||||
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
||||
<license type="expression">MIT</license>
|
||||
<projectUrl>https://github.com/oqtane/oqtane.framework</projectUrl>
|
||||
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.0.1</releaseNotes>
|
||||
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.0.2</releaseNotes>
|
||||
<icon>icon.png</icon>
|
||||
<tags>oqtane</tags>
|
||||
</metadata>
|
||||
|
@ -2,7 +2,7 @@
|
||||
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
|
||||
<metadata>
|
||||
<id>Oqtane.Updater</id>
|
||||
<version>3.0.1</version>
|
||||
<version>3.0.2</version>
|
||||
<authors>Shaun Walker</authors>
|
||||
<owners>.NET Foundation</owners>
|
||||
<title>Oqtane Framework</title>
|
||||
@ -12,7 +12,7 @@
|
||||
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
||||
<license type="expression">MIT</license>
|
||||
<projectUrl>https://github.com/oqtane/oqtane.framework</projectUrl>
|
||||
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.0.1</releaseNotes>
|
||||
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.0.2</releaseNotes>
|
||||
<icon>icon.png</icon>
|
||||
<tags>oqtane</tags>
|
||||
</metadata>
|
||||
|
@ -1 +1 @@
|
||||
Compress-Archive -Path "..\Oqtane.Server\bin\Release\net6.0\publish\*" -DestinationPath "Oqtane.Framework.3.0.1.Install.zip" -Force
|
||||
Compress-Archive -Path "..\Oqtane.Server\bin\Release\net6.0\publish\*" -DestinationPath "Oqtane.Framework.3.0.2.Install.zip" -Force
|
@ -1 +1 @@
|
||||
Compress-Archive -Path "..\Oqtane.Server\bin\Release\net6.0\publish\*" -DestinationPath "Oqtane.Framework.3.0.1.Upgrade.zip" -Force
|
||||
Compress-Archive -Path "..\Oqtane.Server\bin\Release\net6.0\publish\*" -DestinationPath "Oqtane.Framework.3.0.2.Upgrade.zip" -Force
|
@ -19,7 +19,6 @@ using Oqtane.Extensions;
|
||||
using SixLabors.ImageSharp;
|
||||
using SixLabors.ImageSharp.Processing;
|
||||
using SixLabors.ImageSharp.Formats.Png;
|
||||
using SixLabors.ImageSharp.PixelFormats;
|
||||
using System.Net.Http;
|
||||
|
||||
// ReSharper disable StringIndexOfIsCultureSpecific.1
|
||||
@ -508,8 +507,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 +518,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 +572,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))
|
||||
{
|
||||
stream.Position = 0;
|
||||
using (var image = Image.Load(stream))
|
||||
{
|
||||
int.TryParse(rotate, out int angle);
|
||||
Enum.TryParse(mode, true, out ResizeMode resizemode);
|
||||
Enum.TryParse(position, true, out AnchorPositionMode anchorpositionmode);
|
||||
|
||||
image.Mutate(x =>
|
||||
x.Resize(new ResizeOptions
|
||||
image.Mutate(x => x
|
||||
.AutoOrient() // auto orient the image
|
||||
.Rotate(angle)
|
||||
.Resize(new ResizeOptions
|
||||
{
|
||||
Size = new Size(width, height),
|
||||
Mode = resizemode
|
||||
Mode = resizemode,
|
||||
Position = anchorpositionmode,
|
||||
Size = new Size(width, height)
|
||||
})
|
||||
.BackgroundColor(new Rgba32(255, 255, 255, 0)));
|
||||
|
||||
if (rotate != "0" && int.TryParse(rotate, out int angle))
|
||||
{
|
||||
image.Mutate(x => x.Rotate(angle));
|
||||
}
|
||||
.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 = "";
|
||||
}
|
||||
|
||||
|
@ -55,7 +55,7 @@ namespace Oqtane.Controllers
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized Job Post Attempt {Alias}", job);
|
||||
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized Job Post Attempt {Job}", job);
|
||||
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
|
||||
job = null;
|
||||
}
|
||||
@ -74,7 +74,7 @@ namespace Oqtane.Controllers
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized Job Put Attempt {Alias}", job);
|
||||
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized Job Put Attempt {Job}", job);
|
||||
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
|
||||
job = null;
|
||||
}
|
||||
|
@ -75,6 +75,7 @@ namespace Oqtane.Controllers
|
||||
|
||||
module.ModuleDefinition = moduledefinitions.Find(item => item.ModuleDefinitionName == module.ModuleDefinitionName);
|
||||
module.Settings = settings.Where(item => item.EntityId == pagemodule.ModuleId)
|
||||
.Where(item => !item.IsPrivate || _userPermissions.IsAuthorized(User, PermissionNames.Edit, pagemodule.Module.Permissions))
|
||||
.ToDictionary(setting => setting.SettingName, setting => setting.SettingValue);
|
||||
|
||||
modules.Add(module);
|
||||
@ -101,6 +102,7 @@ namespace Oqtane.Controllers
|
||||
List<ModuleDefinition> moduledefinitions = _moduleDefinitions.GetModuleDefinitions(module.SiteId).ToList();
|
||||
module.ModuleDefinition = moduledefinitions.Find(item => item.ModuleDefinitionName == module.ModuleDefinitionName);
|
||||
module.Settings = _settings.GetSettings(EntityNames.Module, id)
|
||||
.Where(item => !item.IsPrivate || _userPermissions.IsAuthorized(User, PermissionNames.Edit, module.Permissions))
|
||||
.ToDictionary(setting => setting.SettingName, setting => setting.SettingValue);
|
||||
return module;
|
||||
}
|
||||
@ -171,7 +173,7 @@ namespace Oqtane.Controllers
|
||||
public void Delete(int id)
|
||||
{
|
||||
var module = _modules.GetModule(id);
|
||||
if (module != null && module.SiteId == _alias.SiteId && _userPermissions.IsAuthorized(User, EntityNames.Page, module.ModuleId, PermissionNames.Edit))
|
||||
if (module != null && module.SiteId == _alias.SiteId && _userPermissions.IsAuthorized(User, EntityNames.Module, module.ModuleId, PermissionNames.Edit))
|
||||
{
|
||||
_modules.DeleteModule(id);
|
||||
_syncManager.AddSyncEvent(_alias.TenantId, EntityNames.Site, _alias.SiteId);
|
||||
|
@ -57,6 +57,7 @@ namespace Oqtane.Controllers
|
||||
if (_userPermissions.IsAuthorized(User, PermissionNames.View, page.Permissions))
|
||||
{
|
||||
page.Settings = settings.Where(item => item.EntityId == page.PageId)
|
||||
.Where(item => !item.IsPrivate || _userPermissions.IsAuthorized(User, PermissionNames.Edit, page.Permissions))
|
||||
.ToDictionary(setting => setting.SettingName, setting => setting.SettingValue);
|
||||
pages.Add(page);
|
||||
}
|
||||
@ -85,15 +86,16 @@ namespace Oqtane.Controllers
|
||||
{
|
||||
page = _pages.GetPage(id, int.Parse(userid));
|
||||
}
|
||||
if (_userPermissions.IsAuthorized(User,PermissionNames.View, page.Permissions))
|
||||
if (page != null && page.SiteId == _alias.SiteId && _userPermissions.IsAuthorized(User,PermissionNames.View, page.Permissions))
|
||||
{
|
||||
page.Settings = _settings.GetSettings(EntityNames.Page, page.PageId)
|
||||
.Where(item => !item.IsPrivate || _userPermissions.IsAuthorized(User, PermissionNames.Edit, page.Permissions))
|
||||
.ToDictionary(setting => setting.SettingName, setting => setting.SettingValue);
|
||||
return page;
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized Page Get Attempt {Page}", page);
|
||||
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized Page Get Attempt {PageId} {UserId}", id, userid);
|
||||
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
|
||||
return null;
|
||||
}
|
||||
@ -104,24 +106,16 @@ namespace Oqtane.Controllers
|
||||
public Page Get(string path, int siteid)
|
||||
{
|
||||
Page page = _pages.GetPage(WebUtility.UrlDecode(path), siteid);
|
||||
if (page != null && page.SiteId == _alias.SiteId)
|
||||
{
|
||||
if (_userPermissions.IsAuthorized(User,PermissionNames.View, page.Permissions))
|
||||
if (page != null && page.SiteId == _alias.SiteId && _userPermissions.IsAuthorized(User, PermissionNames.View, page.Permissions))
|
||||
{
|
||||
page.Settings = _settings.GetSettings(EntityNames.Page, page.PageId)
|
||||
.Where(item => !item.IsPrivate || _userPermissions.IsAuthorized(User, PermissionNames.Edit, page.Permissions))
|
||||
.ToDictionary(setting => setting.SettingName, setting => setting.SettingValue);
|
||||
return page;
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized Page Get Attempt {Page}", page);
|
||||
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
|
||||
return null;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized Page Get Attempt {Path} for Site {SiteId}", path, siteid);
|
||||
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized Page Get Attempt {SiteId} {Path}", siteid, path);
|
||||
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
|
||||
return null;
|
||||
}
|
||||
@ -267,11 +261,10 @@ namespace Oqtane.Controllers
|
||||
// save url mapping if page path changed
|
||||
if (currentPage.Path != page.Path)
|
||||
{
|
||||
var url = HttpContext.Request.Scheme + "://" + _alias.Name + "/";
|
||||
var urlMapping = new UrlMapping();
|
||||
urlMapping.SiteId = page.SiteId;
|
||||
urlMapping.Url = url + currentPage.Path;
|
||||
urlMapping.MappedUrl = url + page.Path;
|
||||
urlMapping.Url = currentPage.Path;
|
||||
urlMapping.MappedUrl = page.Path;
|
||||
urlMapping.Requests = 0;
|
||||
urlMapping.CreatedOn = System.DateTime.UtcNow;
|
||||
urlMapping.RequestedOn = System.DateTime.UtcNow;
|
||||
|
@ -20,6 +20,7 @@ namespace Oqtane.Controllers
|
||||
private readonly ISyncManager _syncManager;
|
||||
private readonly ILogManager _logger;
|
||||
private readonly Alias _alias;
|
||||
private readonly string _visitorCookie;
|
||||
|
||||
public SettingController(ISettingRepository settings, IPageModuleRepository pageModules, IUserPermissions userPermissions, ITenantManager tenantManager, ISyncManager syncManager, ILogManager logger)
|
||||
{
|
||||
@ -29,39 +30,25 @@ namespace Oqtane.Controllers
|
||||
_syncManager = syncManager;
|
||||
_logger = logger;
|
||||
_alias = tenantManager.GetAlias();
|
||||
_visitorCookie = "APP_VISITOR_" + _alias.SiteId.ToString();
|
||||
}
|
||||
|
||||
// GET: api/<controller>
|
||||
[HttpGet]
|
||||
public IEnumerable<Setting> Get(string entityName, int entityid)
|
||||
public IEnumerable<Setting> Get(string entityName, int entityId)
|
||||
{
|
||||
List<Setting> settings = new List<Setting>();
|
||||
if (IsAuthorized(entityName, entityid, PermissionNames.View))
|
||||
if (IsAuthorized(entityName, entityId, PermissionNames.View))
|
||||
{
|
||||
settings = _settings.GetSettings(entityName, entityid).ToList();
|
||||
|
||||
// ispublic filter
|
||||
switch (entityName)
|
||||
settings = _settings.GetSettings(entityName, entityId).ToList();
|
||||
if (FilterPrivate(entityName, entityId))
|
||||
{
|
||||
case EntityNames.Tenant:
|
||||
case EntityNames.ModuleDefinition:
|
||||
case EntityNames.Host:
|
||||
if (!User.IsInRole(RoleNames.Host))
|
||||
{
|
||||
settings = settings.Where(item => item.IsPublic).ToList();
|
||||
}
|
||||
break;
|
||||
case EntityNames.Site:
|
||||
if (!User.IsInRole(RoleNames.Admin))
|
||||
{
|
||||
settings = settings.Where(item => item.IsPublic).ToList();
|
||||
}
|
||||
break;
|
||||
settings = settings.Where(item => !item.IsPrivate).ToList();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.Log(LogLevel.Error, this, LogFunction.Read, "User Not Authorized To Access Settings {EntityName} {EntityId}", entityName, entityid);
|
||||
_logger.Log(LogLevel.Error, this, LogFunction.Read, "User Not Authorized To Access Settings {EntityName} {EntityId}", entityName, entityId);
|
||||
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
|
||||
}
|
||||
return settings;
|
||||
@ -74,30 +61,15 @@ namespace Oqtane.Controllers
|
||||
Setting setting = _settings.GetSetting(entityName, id);
|
||||
if (IsAuthorized(setting.EntityName, setting.EntityId, PermissionNames.View))
|
||||
{
|
||||
// ispublic filter
|
||||
switch (entityName)
|
||||
{
|
||||
case EntityNames.Tenant:
|
||||
case EntityNames.ModuleDefinition:
|
||||
case EntityNames.Host:
|
||||
if (!User.IsInRole(RoleNames.Host) && !setting.IsPublic)
|
||||
if (FilterPrivate(entityName, id) && setting.IsPrivate)
|
||||
{
|
||||
setting = null;
|
||||
}
|
||||
break;
|
||||
case EntityNames.Site:
|
||||
if (!User.IsInRole(RoleNames.Admin) && !setting.IsPublic)
|
||||
{
|
||||
setting = null;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return setting;
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.Log(LogLevel.Error, this, LogFunction.Read, "User Not Authorized To Access Setting {Setting}", setting);
|
||||
_logger.Log(LogLevel.Error, this, LogFunction.Read, "User Not Authorized To Access Setting {EntityName} {SettingId}", entityName, id);
|
||||
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
|
||||
return null;
|
||||
}
|
||||
@ -204,20 +176,67 @@ namespace Oqtane.Controllers
|
||||
}
|
||||
break;
|
||||
case EntityNames.Visitor:
|
||||
var visitorCookie = "APP_VISITOR_" + _alias.SiteId.ToString();
|
||||
if (int.TryParse(Request.Cookies[visitorCookie], out int visitorId))
|
||||
authorized = User.IsInRole(RoleNames.Admin);
|
||||
if (!authorized)
|
||||
{
|
||||
if (int.TryParse(Request.Cookies[_visitorCookie], out int visitorId))
|
||||
{
|
||||
authorized = (visitorId == entityId);
|
||||
}
|
||||
}
|
||||
break;
|
||||
default: // custom entity
|
||||
if (permissionName == PermissionNames.Edit)
|
||||
{
|
||||
authorized = User.IsInRole(RoleNames.Admin) || _userPermissions.IsAuthorized(User, entityName, entityId, permissionName);
|
||||
}
|
||||
else
|
||||
{
|
||||
authorized = User.IsInRole(RoleNames.Admin);
|
||||
authorized = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
return authorized;
|
||||
}
|
||||
|
||||
private bool FilterPrivate(string entityName, int entityId)
|
||||
{
|
||||
bool filter = false;
|
||||
switch (entityName)
|
||||
{
|
||||
case EntityNames.Tenant:
|
||||
case EntityNames.ModuleDefinition:
|
||||
case EntityNames.Host:
|
||||
filter = !User.IsInRole(RoleNames.Host);
|
||||
break;
|
||||
case EntityNames.Site:
|
||||
filter = !User.IsInRole(RoleNames.Admin);
|
||||
break;
|
||||
case EntityNames.Page:
|
||||
case EntityNames.Module:
|
||||
case EntityNames.Folder:
|
||||
filter = !_userPermissions.IsAuthorized(User, entityName, entityId, PermissionNames.Edit);
|
||||
break;
|
||||
case EntityNames.User:
|
||||
filter = !User.IsInRole(RoleNames.Admin) && _userPermissions.GetUser(User).UserId != entityId;
|
||||
break;
|
||||
case EntityNames.Visitor:
|
||||
if (!User.IsInRole(RoleNames.Admin))
|
||||
{
|
||||
filter = true;
|
||||
if (int.TryParse(Request.Cookies[_visitorCookie], out int visitorId))
|
||||
{
|
||||
filter = (visitorId != entityId);
|
||||
}
|
||||
}
|
||||
break;
|
||||
default: // custom entity
|
||||
filter = !User.IsInRole(RoleNames.Admin) && !_userPermissions.IsAuthorized(User, entityName, entityId, PermissionNames.Edit);
|
||||
break;
|
||||
}
|
||||
return filter;
|
||||
}
|
||||
|
||||
private void AddSyncEvent(string EntityName)
|
||||
{
|
||||
switch (EntityName)
|
||||
|
@ -44,12 +44,9 @@ namespace Oqtane.Controllers
|
||||
var site = _sites.GetSite(id);
|
||||
if (site.SiteId == _alias.SiteId)
|
||||
{
|
||||
var settings = _settings.GetSettings(EntityNames.Site, site.SiteId);
|
||||
if (!User.IsInRole(RoleNames.Admin))
|
||||
{
|
||||
settings = settings.Where(item => item.IsPublic);
|
||||
}
|
||||
site.Settings = settings.ToDictionary(setting => setting.SettingName, setting => setting.SettingValue);
|
||||
site.Settings = _settings.GetSettings(EntityNames.Site, site.SiteId)
|
||||
.Where(item => !item.IsPrivate || User.IsInRole(RoleNames.Admin))
|
||||
.ToDictionary(setting => setting.SettingName, setting => setting.SettingValue);
|
||||
return site;
|
||||
}
|
||||
else
|
||||
|
@ -31,7 +31,7 @@ namespace Oqtane.Controllers
|
||||
systeminfo.Add("osversion", Environment.OSVersion.ToString());
|
||||
systeminfo.Add("machinename", Environment.MachineName);
|
||||
systeminfo.Add("serverpath", _environment.ContentRootPath);
|
||||
systeminfo.Add("servertime", DateTime.Now.ToString());
|
||||
systeminfo.Add("servertime", DateTime.UtcNow.ToString());
|
||||
systeminfo.Add("installationid", _configManager.GetInstallationId());
|
||||
|
||||
systeminfo.Add("runtime", _configManager.GetSetting("Runtime", "Server"));
|
||||
|
@ -48,7 +48,7 @@ namespace Oqtane.Controllers
|
||||
public UrlMapping Get(int id)
|
||||
{
|
||||
var urlMapping = _urlMappings.GetUrlMapping(id);
|
||||
if (urlMapping != null && (urlMapping.SiteId == _alias.SiteId))
|
||||
if (urlMapping != null && urlMapping.SiteId == _alias.SiteId)
|
||||
{
|
||||
return urlMapping;
|
||||
}
|
||||
@ -60,6 +60,23 @@ namespace Oqtane.Controllers
|
||||
}
|
||||
}
|
||||
|
||||
// GET api/<controller>/url/x?url=y
|
||||
[HttpGet("url/{siteid}")]
|
||||
public UrlMapping Get(int siteid, string url)
|
||||
{
|
||||
var urlMapping = _urlMappings.GetUrlMapping(siteid, WebUtility.UrlDecode(url));
|
||||
if (urlMapping != null && urlMapping.SiteId == _alias.SiteId)
|
||||
{
|
||||
return urlMapping;
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized UrlMapping Get Attempt {SiteId} {Url}", siteid, url);
|
||||
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// POST api/<controller>
|
||||
[HttpPost]
|
||||
[Authorize(Roles = RoleNames.Admin)]
|
||||
|
@ -389,7 +389,7 @@ namespace Oqtane.Controllers
|
||||
if (ModelState.IsValid)
|
||||
{
|
||||
IdentityUser identityuser = await _identityUserManager.FindByNameAsync(user.Username);
|
||||
if (identityuser != null)
|
||||
if (identityuser != null && !string.IsNullOrEmpty(token))
|
||||
{
|
||||
var result = await _identityUserManager.ConfirmEmailAsync(identityuser, token);
|
||||
if (result.Succeeded)
|
||||
@ -398,13 +398,13 @@ namespace Oqtane.Controllers
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Email Verification Failed For {Username}", user.Username);
|
||||
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Email Verification Failed For {Username} - Error {Error}", user.Username, result.Errors.ToString());
|
||||
user = null;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Email Verification Failed For {Username}", user.Username);
|
||||
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Email Verification Failed For {Username}And Token {Token}", user.Username, token);
|
||||
user = null;
|
||||
}
|
||||
}
|
||||
@ -420,9 +420,14 @@ namespace Oqtane.Controllers
|
||||
IdentityUser identityuser = await _identityUserManager.FindByNameAsync(user.Username);
|
||||
if (identityuser != null)
|
||||
{
|
||||
user = _users.GetUser(user.Username);
|
||||
string token = await _identityUserManager.GeneratePasswordResetTokenAsync(identityuser);
|
||||
string url = HttpContext.Request.Scheme + "://" + _alias.Name + "/reset?name=" + user.Username + "&token=" + WebUtility.UrlEncode(token);
|
||||
string body = "Dear " + user.DisplayName + ",\n\nPlease Click The Link Displayed Below To Reset Your Password:\n\n" + url + "\n\nThank You!";
|
||||
string body = "Dear " + user.DisplayName + ",\n\nYou recently requested to reset your password. Please use the link below to complete the process:\n\n" + url +
|
||||
"\n\nPlease note that the link is only valid for 24 hours so if you are unable to take action within that time period, you should initiate another password reset on the site." +
|
||||
"\n\nIf you did not request to reset your password you can safely ignore this message." +
|
||||
"\n\nThank You!";
|
||||
|
||||
var notification = new Notification(user.SiteId, null, user, "User Password Reset", body, null);
|
||||
_notifications.AddNotification(notification);
|
||||
_logger.Log(LogLevel.Information, this, LogFunction.Security, "Password Reset Notification Sent For {Username}", user.Username);
|
||||
@ -451,13 +456,13 @@ namespace Oqtane.Controllers
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Password Reset Failed For {Username}", user.Username);
|
||||
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Password Reset Failed For {Username} - Error {Error}", user.Username, result.Errors.ToString());
|
||||
user = null;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Password Reset Failed For {Username}", user.Username);
|
||||
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Password Reset Failed For {Username} And Token {Token}", user.Username, token);
|
||||
user = null;
|
||||
}
|
||||
}
|
||||
|
@ -47,15 +47,14 @@ namespace Oqtane.Controllers
|
||||
[HttpGet("{id}")]
|
||||
public Visitor Get(int id)
|
||||
{
|
||||
bool authorized;
|
||||
bool authorized = User.IsInRole(RoleNames.Admin);
|
||||
if (!authorized)
|
||||
{
|
||||
var visitorCookie = "APP_VISITOR_" + _alias.SiteId.ToString();
|
||||
if (int.TryParse(Request.Cookies[visitorCookie], out int visitorId))
|
||||
{
|
||||
authorized = (visitorId == id);
|
||||
}
|
||||
else
|
||||
{
|
||||
authorized = User.IsInRole(RoleNames.Admin);
|
||||
}
|
||||
|
||||
var visitor = _visitors.GetVisitor(id);
|
||||
|
@ -367,7 +367,9 @@ namespace Oqtane.Infrastructure
|
||||
tenant = db.Tenant.FirstOrDefault(item => item.Name == install.TenantName);
|
||||
}
|
||||
|
||||
foreach (var aliasName in install.Aliases.Split(new[] {','}, StringSplitOptions.RemoveEmptyEntries))
|
||||
var aliasNames = install.Aliases.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries).Select(sValue => sValue.Trim()).ToArray();
|
||||
var firstAlias = aliasNames[0];
|
||||
foreach (var aliasName in aliasNames)
|
||||
{
|
||||
if (tenant != null)
|
||||
{
|
||||
@ -376,6 +378,7 @@ namespace Oqtane.Infrastructure
|
||||
Name = aliasName,
|
||||
TenantId = tenant.TenantId,
|
||||
SiteId = -1,
|
||||
IsDefault = (aliasName == firstAlias),
|
||||
CreatedBy = "",
|
||||
CreatedOn = DateTime.UtcNow,
|
||||
ModifiedBy = "",
|
||||
@ -438,8 +441,7 @@ namespace Oqtane.Infrastructure
|
||||
var index = Array.FindIndex(versions, item => item == version);
|
||||
if (index != (versions.Length - 1))
|
||||
{
|
||||
if (index == -1) index = 0;
|
||||
for (var i = index; i < versions.Length; i++)
|
||||
for (var i = (index + 1); i < versions.Length; i++)
|
||||
{
|
||||
upgrades.Upgrade(tenant, versions[i]);
|
||||
}
|
||||
@ -558,7 +560,8 @@ namespace Oqtane.Infrastructure
|
||||
{
|
||||
// set the alias explicitly so the tenant can be resolved
|
||||
var aliases = scope.ServiceProvider.GetRequiredService<IAliasRepository>();
|
||||
var firstAlias = install.Aliases.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries)[0];
|
||||
var aliasNames = install.Aliases.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries).Select(sValue => sValue.Trim()).ToArray();
|
||||
var firstAlias = aliasNames[0];
|
||||
var alias = aliases.GetAliases().FirstOrDefault(item => item.Name == firstAlias);
|
||||
var tenantManager = scope.ServiceProvider.GetRequiredService<ITenantManager>();
|
||||
tenantManager.SetAlias(alias);
|
||||
@ -650,7 +653,7 @@ namespace Oqtane.Infrastructure
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var aliasName in install.Aliases.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries))
|
||||
foreach (var aliasName in aliasNames)
|
||||
{
|
||||
alias = aliases.GetAliases().FirstOrDefault(item => item.Name == aliasName);
|
||||
if (alias != null)
|
||||
|
@ -114,7 +114,12 @@ namespace Oqtane.Infrastructure
|
||||
jobLogs.UpdateJobLog(log);
|
||||
|
||||
// update the job
|
||||
job.NextExecution = CalculateNextExecution(NextExecution, job.Frequency, job.Interval);
|
||||
job.NextExecution = CalculateNextExecution(NextExecution, job);
|
||||
if (job.Frequency == "O") // one time
|
||||
{
|
||||
job.EndDate = DateTime.UtcNow;
|
||||
job.NextExecution = null;
|
||||
}
|
||||
job.IsExecuting = false;
|
||||
jobs.UpdateJob(job);
|
||||
|
||||
@ -140,21 +145,41 @@ namespace Oqtane.Infrastructure
|
||||
|
||||
}
|
||||
|
||||
private DateTime CalculateNextExecution(DateTime nextExecution, string frequency, int interval)
|
||||
private DateTime CalculateNextExecution(DateTime nextExecution, Job job)
|
||||
{
|
||||
switch (frequency)
|
||||
switch (job.Frequency)
|
||||
{
|
||||
case "m": // minutes
|
||||
nextExecution = nextExecution.AddMinutes(interval);
|
||||
nextExecution = nextExecution.AddMinutes(job.Interval);
|
||||
break;
|
||||
case "H": // hours
|
||||
nextExecution = nextExecution.AddHours(interval);
|
||||
nextExecution = nextExecution.AddHours(job.Interval);
|
||||
break;
|
||||
case "d": // days
|
||||
nextExecution = nextExecution.AddDays(interval);
|
||||
nextExecution = nextExecution.AddDays(job.Interval);
|
||||
if (job.StartDate != null && job.StartDate.Value.TimeOfDay.TotalSeconds != 0)
|
||||
{
|
||||
// set the start time
|
||||
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(interval);
|
||||
nextExecution = nextExecution.AddMonths(job.Interval);
|
||||
if (job.StartDate != null && job.StartDate.Value.TimeOfDay.TotalSeconds != 0)
|
||||
{
|
||||
// set the start time
|
||||
nextExecution = nextExecution.Date.Add(job.StartDate.Value.TimeOfDay);
|
||||
}
|
||||
break;
|
||||
case "O": // one time
|
||||
break;
|
||||
}
|
||||
if (nextExecution < DateTime.UtcNow)
|
||||
@ -168,7 +193,6 @@ namespace Oqtane.Infrastructure
|
||||
{
|
||||
try
|
||||
{
|
||||
// set IsExecuting to false in case this job was forcefully terminated previously
|
||||
using (var scope = _serviceScopeFactory.CreateScope())
|
||||
{
|
||||
string jobTypeName = Utilities.GetFullTypeName(GetType().AssemblyQualifiedName);
|
||||
@ -176,15 +200,17 @@ namespace Oqtane.Infrastructure
|
||||
Job job = jobs.GetJobs().Where(item => item.JobType == jobTypeName).FirstOrDefault();
|
||||
if (job != null)
|
||||
{
|
||||
// reset in case this job was forcefully terminated previously
|
||||
job.IsStarted = true;
|
||||
job.IsExecuting = false;
|
||||
jobs.UpdateJob(job);
|
||||
}
|
||||
else
|
||||
{
|
||||
// auto registration - does not run on initial installation but will run after restart
|
||||
// auto registration - job will not run on initial installation but will run after restart
|
||||
job = new Job { JobType = jobTypeName };
|
||||
// optional properties
|
||||
|
||||
// optional HostedServiceBase properties
|
||||
var jobType = Type.GetType(jobTypeName);
|
||||
var jobObject = ActivatorUtilities.CreateInstance(scope.ServiceProvider, jobType) as HostedServiceBase;
|
||||
if (jobObject.Name != "")
|
||||
@ -203,6 +229,7 @@ namespace Oqtane.Infrastructure
|
||||
job.IsEnabled = jobObject.IsEnabled;
|
||||
job.IsStarted = true;
|
||||
job.IsExecuting = false;
|
||||
job.NextExecution = null;
|
||||
jobs.AddJob(job);
|
||||
}
|
||||
}
|
||||
@ -224,13 +251,27 @@ namespace Oqtane.Infrastructure
|
||||
|
||||
public async Task StopAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
try
|
||||
{
|
||||
using (var scope = _serviceScopeFactory.CreateScope())
|
||||
{
|
||||
string jobTypeName = Utilities.GetFullTypeName(GetType().AssemblyQualifiedName);
|
||||
IJobRepository jobs = scope.ServiceProvider.GetRequiredService<IJobRepository>();
|
||||
Job job = jobs.GetJobs().Where(item => item.JobType == jobTypeName).FirstOrDefault();
|
||||
if (job != null)
|
||||
{
|
||||
// reset job
|
||||
job.IsStarted = false;
|
||||
job.IsExecuting = false;
|
||||
jobs.UpdateJob(job);
|
||||
}
|
||||
}
|
||||
|
||||
if (_executingTask == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
_cancellationTokenSource.Cancel();
|
||||
}
|
||||
finally
|
||||
|
95
Oqtane.Server/Infrastructure/Jobs/PurgeJob.cs
Normal file
95
Oqtane.Server/Infrastructure/Jobs/PurgeJob.cs
Normal file
@ -0,0 +1,95 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Oqtane.Models;
|
||||
using Oqtane.Repository;
|
||||
using Oqtane.Shared;
|
||||
|
||||
namespace Oqtane.Infrastructure
|
||||
{
|
||||
public class PurgeJob : HostedServiceBase
|
||||
{
|
||||
// JobType = "Oqtane.Infrastructure.PurgeJob, Oqtane.Server"
|
||||
|
||||
public PurgeJob(IServiceScopeFactory serviceScopeFactory) : base(serviceScopeFactory)
|
||||
{
|
||||
Name = "Purge Job";
|
||||
Frequency = "d"; // daily
|
||||
Interval = 1;
|
||||
StartDate = DateTime.ParseExact("03:00", "H:mm", null, System.Globalization.DateTimeStyles.None); // 3 AM
|
||||
IsEnabled = true;
|
||||
}
|
||||
|
||||
// job is executed for each tenant in installation
|
||||
public override string ExecuteJob(IServiceProvider provider)
|
||||
{
|
||||
string log = "";
|
||||
|
||||
// get services
|
||||
var siteRepository = provider.GetRequiredService<ISiteRepository>();
|
||||
var settingRepository = provider.GetRequiredService<ISettingRepository>();
|
||||
var logRepository = provider.GetRequiredService<ILogRepository>();
|
||||
var visitorRepository = provider.GetRequiredService<IVisitorRepository>();
|
||||
|
||||
// iterate through sites for current tenant
|
||||
List<Site> sites = siteRepository.GetSites().ToList();
|
||||
foreach (Site site in sites)
|
||||
{
|
||||
log += "Processing Site: " + site.Name + "<br />";
|
||||
int retention;
|
||||
int count;
|
||||
|
||||
// get site settings
|
||||
Dictionary<string, string> settings = GetSettings(settingRepository.GetSettings(EntityNames.Site, site.SiteId).ToList());
|
||||
|
||||
// purge event log
|
||||
retention = 30; // 30 days
|
||||
if (settings.ContainsKey("LogRetention") && !string.IsNullOrEmpty(settings["LogRetention"]))
|
||||
{
|
||||
retention = int.Parse(settings["LogRetention"]);
|
||||
}
|
||||
try
|
||||
{
|
||||
count = logRepository.DeleteLogs(retention);
|
||||
log += count.ToString() + " Events Purged<br />";
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
log += $"Error Purging Events - {ex.Message}<br />";
|
||||
}
|
||||
|
||||
// purge visitors
|
||||
if (site.VisitorTracking)
|
||||
{
|
||||
retention = 30; // 30 days
|
||||
if (settings.ContainsKey("VisitorRetention") && !string.IsNullOrEmpty(settings["VisitorRetention"]))
|
||||
{
|
||||
retention = int.Parse(settings["VisitorRetention"]);
|
||||
}
|
||||
try
|
||||
{
|
||||
count = visitorRepository.DeleteVisitors(retention);
|
||||
log += count.ToString() + " Visitors Purged<br />";
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
log += $"Error Purging Visitors - {ex.Message}<br />";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return log;
|
||||
}
|
||||
|
||||
private Dictionary<string, string> GetSettings(List<Setting> settings)
|
||||
{
|
||||
Dictionary<string, string> dictionary = new Dictionary<string, string>();
|
||||
foreach (Setting setting in settings.OrderBy(item => item.SettingName).ToList())
|
||||
{
|
||||
dictionary.Add(setting.SettingName, setting.SettingValue);
|
||||
}
|
||||
return dictionary;
|
||||
}
|
||||
}
|
||||
}
|
@ -68,7 +68,7 @@ namespace Oqtane.SiteTemplates
|
||||
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>"
|
||||
|
@ -0,0 +1,31 @@
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Oqtane.Databases.Interfaces;
|
||||
using Oqtane.Migrations.EntityBuilders;
|
||||
using Oqtane.Repository;
|
||||
|
||||
namespace Oqtane.Migrations.Master
|
||||
{
|
||||
[DbContext(typeof(MasterDBContext))]
|
||||
[Migration("Master.03.00.02.01")]
|
||||
public class AddAliasIsDefault : MultiDatabaseMigration
|
||||
{
|
||||
public AddAliasIsDefault(IDatabase database) : base(database)
|
||||
{
|
||||
}
|
||||
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
//Add Column to Alias table
|
||||
var aliasEntityBuilder = new AliasEntityBuilder(migrationBuilder, ActiveDatabase);
|
||||
aliasEntityBuilder.AddBooleanColumn("IsDefault", true);
|
||||
aliasEntityBuilder.UpdateColumn("IsDefault", "1", "bool", "");
|
||||
}
|
||||
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
var aliasEntityBuilder = new AliasEntityBuilder(migrationBuilder, ActiveDatabase);
|
||||
aliasEntityBuilder.DropColumn("IsDefault");
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,30 @@
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Oqtane.Databases.Interfaces;
|
||||
using Oqtane.Migrations.EntityBuilders;
|
||||
using Oqtane.Repository;
|
||||
using Oqtane.Shared;
|
||||
|
||||
namespace Oqtane.Migrations.Tenant
|
||||
{
|
||||
[DbContext(typeof(TenantDBContext))]
|
||||
[Migration("Tenant.03.00.02.01")]
|
||||
public class UpdateSettingIsPublic : MultiDatabaseMigration
|
||||
{
|
||||
public UpdateSettingIsPublic(IDatabase database) : base(database)
|
||||
{
|
||||
}
|
||||
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
var settingEntityBuilder = new SettingEntityBuilder(migrationBuilder, ActiveDatabase);
|
||||
settingEntityBuilder.UpdateColumn("IsPublic", "1", "bool", "SettingName NOT LIKE 'SMTP%'");
|
||||
}
|
||||
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
var settingEntityBuilder = new SettingEntityBuilder(migrationBuilder, ActiveDatabase);
|
||||
settingEntityBuilder.UpdateColumn("IsPublic", "0", "bool", "SettingName NOT LIKE 'SMTP%'");
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,32 @@
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Oqtane.Databases.Interfaces;
|
||||
using Oqtane.Migrations.EntityBuilders;
|
||||
using Oqtane.Repository;
|
||||
using Oqtane.Shared;
|
||||
|
||||
namespace Oqtane.Migrations.Tenant
|
||||
{
|
||||
[DbContext(typeof(TenantDBContext))]
|
||||
[Migration("Tenant.03.00.02.02")]
|
||||
public class UpdateSettingIsPrivate : MultiDatabaseMigration
|
||||
{
|
||||
public UpdateSettingIsPrivate(IDatabase database) : base(database)
|
||||
{
|
||||
}
|
||||
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
var settingEntityBuilder = new SettingEntityBuilder(migrationBuilder, ActiveDatabase);
|
||||
settingEntityBuilder.AddBooleanColumn("IsPrivate", true);
|
||||
settingEntityBuilder.UpdateColumn("IsPrivate", "0", "bool", "");
|
||||
settingEntityBuilder.UpdateColumn("IsPrivate", "1", "bool", "EntityName = 'Site' AND SettingName LIKE 'SMTP%'");
|
||||
settingEntityBuilder.DropColumn("IsPublic");
|
||||
}
|
||||
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
// not implemented
|
||||
}
|
||||
}
|
||||
}
|
@ -3,7 +3,7 @@
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<Configurations>Debug;Release</Configurations>
|
||||
<Version>3.0.1</Version>
|
||||
<Version>3.0.2</Version>
|
||||
<Product>Oqtane</Product>
|
||||
<Authors>Shaun Walker</Authors>
|
||||
<Company>.NET Foundation</Company>
|
||||
@ -11,7 +11,7 @@
|
||||
<Copyright>.NET Foundation</Copyright>
|
||||
<PackageProjectUrl>https://www.oqtane.org</PackageProjectUrl>
|
||||
<PackageLicenseUrl>https://github.com/oqtane/oqtane.framework/blob/dev/LICENSE</PackageLicenseUrl>
|
||||
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.0.1</PackageReleaseNotes>
|
||||
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.0.2</PackageReleaseNotes>
|
||||
<RepositoryUrl>https://github.com/oqtane/oqtane.framework</RepositoryUrl>
|
||||
<RepositoryType>Git</RepositoryType>
|
||||
<RootNamespace>Oqtane</RootNamespace>
|
||||
|
@ -17,13 +17,11 @@
|
||||
<script src="js/app.js"></script>
|
||||
<script src="js/loadjs.min.js"></script>
|
||||
<link rel="stylesheet" href="css/app.css" />
|
||||
@Html.Raw(@Model.HeadResources)
|
||||
@Html.Raw(Model.HeadResources)
|
||||
</head>
|
||||
<body>
|
||||
@(Html.AntiForgeryToken())
|
||||
<app>
|
||||
<component type="typeof(Oqtane.App)" render-mode="@Model.RenderMode" param-AntiForgeryToken="@Model.AntiForgeryToken" param-Runtime="@Model.Runtime" param-RenderMode="@Model.RenderMode.ToString()" param-VisitorId="@Model.VisitorId" />
|
||||
</app>
|
||||
|
||||
<div id="blazor-error-ui">
|
||||
<environment include="Staging,Production">
|
||||
@ -48,8 +46,8 @@
|
||||
}
|
||||
@if (!string.IsNullOrEmpty(Model.PWAScript))
|
||||
{
|
||||
@Model.PWAScript
|
||||
@Html.Raw(Model.PWAScript)
|
||||
}
|
||||
@Html.Raw(@Model.BodyResources)
|
||||
@Html.Raw(Model.BodyResources)
|
||||
</body>
|
||||
</html>
|
||||
|
@ -18,6 +18,8 @@ using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.Net.Http.Headers;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using System.Security.Claims;
|
||||
using System.Net;
|
||||
using Microsoft.Extensions.Primitives;
|
||||
|
||||
namespace Oqtane.Pages
|
||||
{
|
||||
@ -32,8 +34,10 @@ namespace Oqtane.Pages
|
||||
private readonly IPageRepository _pages;
|
||||
private readonly IUrlMappingRepository _urlMappings;
|
||||
private readonly IVisitorRepository _visitors;
|
||||
private readonly IAliasRepository _aliases;
|
||||
private readonly ISettingRepository _settings;
|
||||
|
||||
public HostModel(IConfiguration configuration, ITenantManager tenantManager, ILocalizationManager localizationManager, ILanguageRepository languages, IAntiforgery antiforgery, ISiteRepository sites, IPageRepository pages, IUrlMappingRepository urlMappings, IVisitorRepository visitors)
|
||||
public HostModel(IConfiguration configuration, ITenantManager tenantManager, ILocalizationManager localizationManager, ILanguageRepository languages, IAntiforgery antiforgery, ISiteRepository sites, IPageRepository pages, IUrlMappingRepository urlMappings, IVisitorRepository visitors, IAliasRepository aliases, ISettingRepository settings)
|
||||
{
|
||||
_configuration = configuration;
|
||||
_tenantManager = tenantManager;
|
||||
@ -44,12 +48,15 @@ namespace Oqtane.Pages
|
||||
_pages = pages;
|
||||
_urlMappings = urlMappings;
|
||||
_visitors = visitors;
|
||||
_aliases = aliases;
|
||||
_settings = settings;
|
||||
}
|
||||
|
||||
public string AntiForgeryToken = "";
|
||||
public string Runtime = "Server";
|
||||
public RenderMode RenderMode = RenderMode.Server;
|
||||
public int VisitorId = -1;
|
||||
public string RemoteIPAddress = "";
|
||||
public string HeadResources = "";
|
||||
public string BodyResources = "";
|
||||
public string Title = "";
|
||||
@ -60,6 +67,7 @@ namespace Oqtane.Pages
|
||||
public IActionResult OnGet()
|
||||
{
|
||||
AntiForgeryToken = _antiforgery.GetAndStoreTokens(HttpContext).RequestToken;
|
||||
RemoteIPAddress = HttpContext.Connection.RemoteIpAddress?.ToString() ?? "";
|
||||
|
||||
if (_configuration.GetSection("Runtime").Exists())
|
||||
{
|
||||
@ -77,11 +85,30 @@ namespace Oqtane.Pages
|
||||
var alias = _tenantManager.GetAlias();
|
||||
if (alias != null)
|
||||
{
|
||||
Route route = new Route(HttpContext.Request.GetEncodedUrl(), alias.Path);
|
||||
var url = WebUtility.UrlDecode(HttpContext.Request.GetEncodedUrl());
|
||||
|
||||
// redirect non-default alias
|
||||
if (!alias.IsDefault)
|
||||
{
|
||||
var aliases = _aliases.GetAliases().Where(item => item.TenantId == alias.TenantId && item.SiteId == alias.SiteId);
|
||||
if (aliases.Where(item => item.IsDefault).FirstOrDefault() != null)
|
||||
{
|
||||
return RedirectPermanent(url.Replace(alias.Name, aliases.Where(item => item.IsDefault).FirstOrDefault().Name));
|
||||
}
|
||||
else // no default specified - use first alias
|
||||
{
|
||||
if (alias.Name.Trim() != aliases.First().Name.Trim())
|
||||
{
|
||||
return RedirectPermanent(url.Replace(alias.Name, aliases.First().Name));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var site = _sites.GetSite(alias.SiteId);
|
||||
if (site != null)
|
||||
{
|
||||
Route route = new Route(url, alias.Path);
|
||||
|
||||
if (!string.IsNullOrEmpty(site.Runtime))
|
||||
{
|
||||
Runtime = site.Runtime;
|
||||
@ -125,35 +152,14 @@ namespace Oqtane.Pages
|
||||
ThemeType = page.ThemeType;
|
||||
}
|
||||
}
|
||||
else
|
||||
else // page not found
|
||||
{
|
||||
// page does not exist
|
||||
var url = route.SiteUrl + "/" + route.PagePath;
|
||||
var urlMapping = _urlMappings.GetUrlMapping(site.SiteId, url);
|
||||
if (urlMapping == null)
|
||||
// look for url mapping
|
||||
var urlMapping = _urlMappings.GetUrlMapping(site.SiteId, route.PagePath);
|
||||
if (urlMapping != null && !string.IsNullOrEmpty(urlMapping.MappedUrl))
|
||||
{
|
||||
if (site.CaptureBrokenUrls)
|
||||
{
|
||||
urlMapping = new UrlMapping();
|
||||
urlMapping.SiteId = site.SiteId;
|
||||
urlMapping.Url = url;
|
||||
urlMapping.MappedUrl = "";
|
||||
urlMapping.Requests = 1;
|
||||
urlMapping.CreatedOn = DateTime.UtcNow;
|
||||
urlMapping.RequestedOn = DateTime.UtcNow;
|
||||
_urlMappings.AddUrlMapping(urlMapping);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
urlMapping.Requests += 1;
|
||||
urlMapping.RequestedOn = DateTime.UtcNow;
|
||||
_urlMappings.UpdateUrlMapping(urlMapping);
|
||||
|
||||
if (!string.IsNullOrEmpty(urlMapping.MappedUrl))
|
||||
{
|
||||
return RedirectPermanent(urlMapping.MappedUrl);
|
||||
}
|
||||
url = (urlMapping.MappedUrl.StartsWith("http")) ? urlMapping.MappedUrl : route.SiteUrl + "/" + urlMapping.MappedUrl;
|
||||
return RedirectPermanent(url);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -190,15 +196,27 @@ namespace Oqtane.Pages
|
||||
private void TrackVisitor(int SiteId)
|
||||
{
|
||||
// get request attributes
|
||||
string ip = HttpContext.Connection.RemoteIpAddress.ToString();
|
||||
string useragent = Request.Headers[HeaderNames.UserAgent];
|
||||
string language = Request.Headers[HeaderNames.AcceptLanguage];
|
||||
if (language.Contains(","))
|
||||
string useragent = (Request.Headers[HeaderNames.UserAgent] != StringValues.Empty) ? Request.Headers[HeaderNames.UserAgent] : "";
|
||||
string language = (Request.Headers[HeaderNames.AcceptLanguage] != StringValues.Empty) ? Request.Headers[HeaderNames.AcceptLanguage] : "";
|
||||
language = (language.Contains(",")) ? language.Substring(0, language.IndexOf(",")) : language;
|
||||
language = (language.Contains(";")) ? language.Substring(0, language.IndexOf(";")) : language;
|
||||
language = (language.Trim().Length == 0) ? "??" : language;
|
||||
|
||||
// filter
|
||||
var filter = _settings.GetSetting(EntityNames.Site, SiteId, "VisitorFilter");
|
||||
if (filter != null && !string.IsNullOrEmpty(filter.SettingValue))
|
||||
{
|
||||
language = language.Substring(0, language.IndexOf(","));
|
||||
foreach (string term in filter.SettingValue.ToLower().Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries).Select(sValue => sValue.Trim()).ToArray())
|
||||
{
|
||||
if (RemoteIPAddress.ToLower().Contains(term) || useragent.ToLower().Contains(term) || language.ToLower().Contains(term))
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
string url = Request.GetEncodedUrl();
|
||||
string referrer = Request.Headers[HeaderNames.Referer];
|
||||
string referrer = (Request.Headers[HeaderNames.Referer] != StringValues.Empty) ? Request.Headers[HeaderNames.Referer] : "";
|
||||
int? userid = null;
|
||||
if (User.HasClaim(item => item.Type == ClaimTypes.PrimarySid))
|
||||
{
|
||||
@ -210,7 +228,7 @@ namespace Oqtane.Pages
|
||||
{
|
||||
var visitor = new Visitor();
|
||||
visitor.SiteId = SiteId;
|
||||
visitor.IPAddress = ip;
|
||||
visitor.IPAddress = RemoteIPAddress;
|
||||
visitor.UserAgent = useragent;
|
||||
visitor.Language = language;
|
||||
visitor.Url = url;
|
||||
@ -236,7 +254,7 @@ namespace Oqtane.Pages
|
||||
var visitor = _visitors.GetVisitor(VisitorId);
|
||||
if (visitor != null)
|
||||
{
|
||||
visitor.IPAddress = ip;
|
||||
visitor.IPAddress = RemoteIPAddress;
|
||||
visitor.UserAgent = useragent;
|
||||
visitor.Language = language;
|
||||
visitor.Url = url;
|
||||
@ -262,7 +280,7 @@ namespace Oqtane.Pages
|
||||
private string CreatePWAScript(Alias alias, Site site, Route route)
|
||||
{
|
||||
return
|
||||
"<script>" +
|
||||
"<script id=\"app-pwa\">" +
|
||||
"setTimeout(() => { " +
|
||||
"var manifest = { " +
|
||||
"\"name\": \"" + site.Name + "\", " +
|
||||
@ -287,6 +305,8 @@ namespace Oqtane.Pages
|
||||
"document.getElementById('app-manifest').setAttribute('href', url); " +
|
||||
"} " +
|
||||
", 1000);" +
|
||||
"</script>" + Environment.NewLine +
|
||||
"<script id=\"app-serviceworker\">" +
|
||||
"if ('serviceWorker' in navigator) { " +
|
||||
"navigator.serviceWorker.register('/service-worker.js').then(function(registration) { " +
|
||||
"console.log('ServiceWorker Registration Successful'); " +
|
||||
@ -305,7 +325,7 @@ namespace Oqtane.Pages
|
||||
var obj = Activator.CreateInstance(type) as IHostResources;
|
||||
foreach (var resource in obj.Resources)
|
||||
{
|
||||
ProcessResource(resource);
|
||||
ProcessResource(resource, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -325,7 +345,7 @@ namespace Oqtane.Pages
|
||||
{
|
||||
if (resource.Declaration == ResourceDeclaration.Global)
|
||||
{
|
||||
ProcessResource(resource);
|
||||
ProcessResource(resource, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -343,24 +363,25 @@ namespace Oqtane.Pages
|
||||
var obj = Activator.CreateInstance(type) as IThemeControl;
|
||||
if (obj.Resources != null)
|
||||
{
|
||||
int count = 0; // required for local stylesheets for current theme
|
||||
foreach (var resource in obj.Resources)
|
||||
{
|
||||
if (resource.Declaration == ResourceDeclaration.Global || (Utilities.GetFullTypeName(type.AssemblyQualifiedName) == ThemeType && resource.ResourceType == ResourceType.Stylesheet))
|
||||
{
|
||||
ProcessResource(resource);
|
||||
ProcessResource(resource, count++);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
private void ProcessResource(Resource resource)
|
||||
private void ProcessResource(Resource resource, int count)
|
||||
{
|
||||
switch (resource.ResourceType)
|
||||
{
|
||||
case ResourceType.Stylesheet:
|
||||
if (!HeadResources.Contains(resource.Url, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
var id = (resource.Declaration == ResourceDeclaration.Global) ? "" : "id=\"app-stylesheet-" + DateTime.Now.ToString("yyyyMMddHHmmssfff") + "-00\" ";
|
||||
var id = (resource.Declaration == ResourceDeclaration.Global) ? "" : "id=\"app-stylesheet-" + DateTime.UtcNow.ToString("yyyyMMddHHmmssfff") + "-" + count.ToString("00") + "\" ";
|
||||
HeadResources += "<link " + id + "rel=\"stylesheet\" href=\"" + resource.Url + "\"" + CrossOrigin(resource.CrossOrigin) + Integrity(resource.Integrity) + " />" + Environment.NewLine;
|
||||
}
|
||||
break;
|
||||
|
@ -4,6 +4,7 @@ using System.Linq;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Caching.Memory;
|
||||
using Oqtane.Models;
|
||||
using Oqtane.Shared;
|
||||
|
||||
namespace Oqtane.Repository
|
||||
{
|
||||
@ -72,7 +73,7 @@ namespace Oqtane.Repository
|
||||
int start = segments.Length;
|
||||
for (int i = 0; i < segments.Length; i++)
|
||||
{
|
||||
if (segments[i] == "api" || segments[i] == "pages" || segments[i] == "*")
|
||||
if (segments[i] == "api" || segments[i] == "pages" || segments[i] == Constants.ModuleDelimiter)
|
||||
{
|
||||
start = i;
|
||||
break;
|
||||
@ -89,8 +90,18 @@ namespace Oqtane.Repository
|
||||
}
|
||||
}
|
||||
|
||||
// return fallback alias if none found
|
||||
return alias ?? aliases.Find(item => item.Name.Equals("*"));
|
||||
// auto register alias if there is only a single tenant/site
|
||||
if (alias == null && aliases.Select(item => new { item.TenantId, item.SiteId }).Distinct().Count() == 1)
|
||||
{
|
||||
alias = new Alias();
|
||||
alias.TenantId = aliases.First().TenantId;
|
||||
alias.SiteId = aliases.First().SiteId;
|
||||
alias.Name = url;
|
||||
alias.IsDefault = false;
|
||||
alias = AddAlias(alias);
|
||||
}
|
||||
|
||||
return alias;
|
||||
}
|
||||
|
||||
public void DeleteAlias(int aliasId)
|
||||
|
@ -1,4 +1,4 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Generic;
|
||||
using Oqtane.Models;
|
||||
|
||||
namespace Oqtane.Repository
|
||||
@ -8,5 +8,6 @@ namespace Oqtane.Repository
|
||||
IEnumerable<Log> GetLogs(int siteId, string level, string function, int rows);
|
||||
Log GetLog(int logId);
|
||||
void AddLog(Log log);
|
||||
int DeleteLogs(int age);
|
||||
}
|
||||
}
|
||||
|
@ -10,6 +10,8 @@ namespace Oqtane.Repository
|
||||
Setting AddSetting(Setting setting);
|
||||
Setting UpdateSetting(Setting setting);
|
||||
Setting GetSetting(string entityName, int settingId);
|
||||
Setting GetSetting(string entityName, int entityId, string settingName);
|
||||
void DeleteSetting(string entityName, int settingId);
|
||||
void DeleteSettings(string entityName, int entityId);
|
||||
}
|
||||
}
|
||||
|
@ -11,5 +11,6 @@ namespace Oqtane.Repository
|
||||
Visitor UpdateVisitor(Visitor visitor);
|
||||
Visitor GetVisitor(int visitorId);
|
||||
void DeleteVisitor(int visitorId);
|
||||
int DeleteVisitors(int age);
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
using System.Collections.Generic;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Oqtane.Models;
|
||||
|
||||
@ -47,5 +48,23 @@ namespace Oqtane.Repository
|
||||
_db.Log.Add(log);
|
||||
_db.SaveChanges();
|
||||
}
|
||||
|
||||
public int DeleteLogs(int age)
|
||||
{
|
||||
// delete logs in batches of 100 records
|
||||
int count = 0;
|
||||
var purgedate = DateTime.UtcNow.AddDays(-age);
|
||||
var logs = _db.Log.Where(item => item.Level != "Error" && item.LogDate < purgedate)
|
||||
.OrderBy(item => item.LogDate).Take(100).ToList();
|
||||
while (logs.Count > 0)
|
||||
{
|
||||
count += logs.Count;
|
||||
_db.Log.RemoveRange(logs);
|
||||
_db.SaveChanges();
|
||||
logs = _db.Log.Where(item => item.Level != "Error" && item.LogDate < purgedate)
|
||||
.OrderBy(item => item.LogDate).Take(100).ToList();
|
||||
}
|
||||
return count;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -6,6 +6,7 @@ using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Oqtane.Models;
|
||||
using Oqtane.Modules;
|
||||
using Oqtane.Shared;
|
||||
using Module = Oqtane.Models.Module;
|
||||
|
||||
namespace Oqtane.Repository
|
||||
@ -14,13 +15,15 @@ namespace Oqtane.Repository
|
||||
{
|
||||
private TenantDBContext _db;
|
||||
private readonly IPermissionRepository _permissions;
|
||||
private readonly ISettingRepository _settings;
|
||||
private readonly IModuleDefinitionRepository _moduleDefinitions;
|
||||
private readonly IServiceProvider _serviceProvider;
|
||||
|
||||
public ModuleRepository(TenantDBContext context, IPermissionRepository permissions, IModuleDefinitionRepository moduleDefinitions, IServiceProvider serviceProvider)
|
||||
public ModuleRepository(TenantDBContext context, IPermissionRepository permissions, ISettingRepository settings, IModuleDefinitionRepository moduleDefinitions, IServiceProvider serviceProvider)
|
||||
{
|
||||
_db = context;
|
||||
_permissions = permissions;
|
||||
_settings = settings;
|
||||
_moduleDefinitions = moduleDefinitions;
|
||||
_serviceProvider = serviceProvider;
|
||||
}
|
||||
@ -34,7 +37,7 @@ namespace Oqtane.Repository
|
||||
{
|
||||
_db.Module.Add(module);
|
||||
_db.SaveChanges();
|
||||
_permissions.UpdatePermissions(module.SiteId, "Module", module.ModuleId, module.Permissions);
|
||||
_permissions.UpdatePermissions(module.SiteId, EntityNames.Module, module.ModuleId, module.Permissions);
|
||||
return module;
|
||||
}
|
||||
|
||||
@ -42,7 +45,7 @@ namespace Oqtane.Repository
|
||||
{
|
||||
_db.Entry(module).State = EntityState.Modified;
|
||||
_db.SaveChanges();
|
||||
_permissions.UpdatePermissions(module.SiteId, "Module", module.ModuleId, module.Permissions);
|
||||
_permissions.UpdatePermissions(module.SiteId, EntityNames.Module, module.ModuleId, module.Permissions);
|
||||
return module;
|
||||
}
|
||||
|
||||
@ -64,7 +67,7 @@ namespace Oqtane.Repository
|
||||
}
|
||||
if (module != null)
|
||||
{
|
||||
module.Permissions = _permissions.GetPermissionString("Module", module.ModuleId);
|
||||
module.Permissions = _permissions.GetPermissionString(EntityNames.Module, module.ModuleId);
|
||||
}
|
||||
return module;
|
||||
}
|
||||
@ -72,7 +75,8 @@ namespace Oqtane.Repository
|
||||
public void DeleteModule(int moduleId)
|
||||
{
|
||||
Module module = _db.Module.Find(moduleId);
|
||||
_permissions.DeletePermissions(module.SiteId, "Module", moduleId);
|
||||
_permissions.DeletePermissions(module.SiteId, EntityNames.Module, moduleId);
|
||||
_settings.DeleteSettings(EntityNames.Module, moduleId);
|
||||
_db.Module.Remove(module);
|
||||
_db.SaveChanges();
|
||||
}
|
||||
|
@ -77,6 +77,18 @@ namespace Oqtane.Repository
|
||||
}
|
||||
}
|
||||
|
||||
public Setting GetSetting(string entityName, int entityId, string settingName)
|
||||
{
|
||||
if (IsMaster(entityName))
|
||||
{
|
||||
return _master.Setting.Where(item => item.EntityName == entityName && item.EntityId == entityId && item.SettingName == settingName).FirstOrDefault();
|
||||
}
|
||||
else
|
||||
{
|
||||
return _tenant.Setting.Where(item => item.EntityName == entityName && item.EntityId == entityId && item.SettingName == settingName).FirstOrDefault();
|
||||
}
|
||||
}
|
||||
|
||||
public void DeleteSetting(string entityName, int settingId)
|
||||
{
|
||||
if (IsMaster(entityName))
|
||||
@ -93,6 +105,32 @@ namespace Oqtane.Repository
|
||||
}
|
||||
}
|
||||
|
||||
public void DeleteSettings(string entityName, int entityId)
|
||||
{
|
||||
if (IsMaster(entityName))
|
||||
{
|
||||
IEnumerable<Setting> settings = _master.Setting
|
||||
.Where(item => item.EntityName == entityName)
|
||||
.Where(item => item.EntityId == entityId);
|
||||
foreach (Setting setting in settings)
|
||||
{
|
||||
_master.Setting.Remove(setting);
|
||||
}
|
||||
_master.SaveChanges();
|
||||
}
|
||||
else
|
||||
{
|
||||
IEnumerable<Setting> settings = _tenant.Setting
|
||||
.Where(item => item.EntityName == entityName)
|
||||
.Where(item => item.EntityId == entityId);
|
||||
foreach (Setting setting in settings)
|
||||
{
|
||||
_tenant.Setting.Remove(setting);
|
||||
}
|
||||
_tenant.SaveChanges();
|
||||
}
|
||||
}
|
||||
|
||||
private bool IsMaster(string EntityName)
|
||||
{
|
||||
return (EntityName == EntityNames.ModuleDefinition || EntityName == EntityNames.Host);
|
||||
|
@ -9,10 +9,12 @@ namespace Oqtane.Repository
|
||||
public class UrlMappingRepository : IUrlMappingRepository
|
||||
{
|
||||
private TenantDBContext _db;
|
||||
private readonly ISiteRepository _sites;
|
||||
|
||||
public UrlMappingRepository(TenantDBContext context)
|
||||
public UrlMappingRepository(TenantDBContext context, ISiteRepository sites)
|
||||
{
|
||||
_db = context;
|
||||
_sites = sites;
|
||||
}
|
||||
|
||||
public IEnumerable<UrlMapping> GetUrlMappings(int siteId, bool isMapped)
|
||||
@ -60,7 +62,29 @@ namespace Oqtane.Repository
|
||||
|
||||
public UrlMapping GetUrlMapping(int siteId, string url)
|
||||
{
|
||||
return _db.UrlMapping.Where(item => item.SiteId == siteId && item.Url == url).FirstOrDefault();
|
||||
var urlMapping = _db.UrlMapping.Where(item => item.SiteId == siteId && item.Url == url).FirstOrDefault();
|
||||
if (urlMapping == null)
|
||||
{
|
||||
var site = _sites.GetSite(siteId);
|
||||
if (site.CaptureBrokenUrls)
|
||||
{
|
||||
urlMapping = new UrlMapping();
|
||||
urlMapping.SiteId = siteId;
|
||||
urlMapping.Url = url;
|
||||
urlMapping.MappedUrl = "";
|
||||
urlMapping.Requests = 1;
|
||||
urlMapping.CreatedOn = DateTime.UtcNow;
|
||||
urlMapping.RequestedOn = DateTime.UtcNow;
|
||||
urlMapping = AddUrlMapping(urlMapping);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
urlMapping.Requests += 1;
|
||||
urlMapping.RequestedOn = DateTime.UtcNow;
|
||||
urlMapping = UpdateUrlMapping(urlMapping);
|
||||
}
|
||||
return urlMapping;
|
||||
}
|
||||
|
||||
public void DeleteUrlMapping(int urlMappingId)
|
||||
|
@ -47,5 +47,23 @@ namespace Oqtane.Repository
|
||||
_db.Visitor.Remove(visitor);
|
||||
_db.SaveChanges();
|
||||
}
|
||||
|
||||
public int DeleteVisitors(int age)
|
||||
{
|
||||
// delete visitors in batches of 100 records
|
||||
int count = 0;
|
||||
var purgedate = DateTime.UtcNow.AddDays(-age);
|
||||
var visitors = _db.Visitor.Where(item => item.Visits <= 1 && item.VisitedOn < purgedate)
|
||||
.OrderBy(item => item.VisitedOn).Take(100).ToList();
|
||||
while (visitors.Count > 0)
|
||||
{
|
||||
count += visitors.Count;
|
||||
_db.Visitor.RemoveRange(visitors);
|
||||
_db.SaveChanges();
|
||||
visitors = _db.Visitor.Where(item => item.Visits < 2 && item.VisitedOn < purgedate)
|
||||
.OrderBy(item => item.VisitedOn).Take(100).ToList();
|
||||
}
|
||||
return count;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user