Compare commits
190 Commits
Author | SHA1 | Date | |
---|---|---|---|
5d8829ba63 | |||
31ccd80894 | |||
bac2234616 | |||
bd61db76a3 | |||
bc99e3b992 | |||
b7314b0813 | |||
4759bd569f | |||
b88c28f864 | |||
774ccb05f8 | |||
0ac48cba34 | |||
e36880fe3a | |||
713cf5de2c | |||
0fa336411f | |||
8ebdb09d68 | |||
40bc53001e | |||
b1656d1eea | |||
7aa54bf979 | |||
231f9bca84 | |||
e4f8596c19 | |||
020b7233d0 | |||
85fc0b3e2f | |||
5dcc7c14f3 | |||
7993d27b11 | |||
1f8c54ce74 | |||
73a414a34b | |||
8fa19c4a51 | |||
0667ae3e15 | |||
db1d00cd07 | |||
b27f092bef | |||
4eaea8e586 | |||
89cd7d3bbb | |||
2fff1d8d21 | |||
850631f00e | |||
1cea8846cf | |||
af48a48559 | |||
655c1762aa | |||
f706ccfd87 | |||
71e4c7f117 | |||
ad6182f4bd | |||
86bf0f65b0 | |||
7742f7747d | |||
eb998c41f2 | |||
657bd7c97c | |||
c8286148c1 | |||
e6ba2cce62 | |||
6105ff44b4 | |||
72da77be01 | |||
4c29b31f1b | |||
6e640108ed | |||
157322441d | |||
61d967e6af | |||
99f2158e55 | |||
1cba78cc4e | |||
1770c1ee11 | |||
a57fbea0cc | |||
f0c27c83f1 | |||
7873ca564c | |||
5ebc1fec24 | |||
f2559b7d4d | |||
1ee92a248e | |||
8376f98f21 | |||
810a3e0171 | |||
2eac9c3795 | |||
75f2425668 | |||
2dd1d7e926 | |||
5bb05a0a51 | |||
bc2c5b00c6 | |||
09f5e158dd | |||
4656471a0a | |||
69d58a4273 | |||
53a27677d4 | |||
f243ad0348 | |||
b4ce6bbb42 | |||
fa32937045 | |||
812e5f3c8e | |||
e2981e802c | |||
4ae4705c73 | |||
fbf4b12713 | |||
e4ece3e0dc | |||
9f231421be | |||
b4fdbb5e48 | |||
fbf62ca30d | |||
05d2096fb8 | |||
7683af81bc | |||
bad10b3812 | |||
9f9522c2ed | |||
62879c3e52 | |||
45610f8dd7 | |||
18102cbd78 | |||
262d6a1529 | |||
1124ddaf90 | |||
981add3872 | |||
8d4b30140e | |||
b9c59137a8 | |||
0b1c7e06ca | |||
fcaf80cba6 | |||
6358b9eabb | |||
70a3fab1ff | |||
bdf86ace86 | |||
d57132d1e4 | |||
a6e87abf99 | |||
f1771610fe | |||
a88ea9780f | |||
bca0866d72 | |||
cebed93abf | |||
ee2b2e3569 | |||
cb8e9ee244 | |||
486184b16c | |||
9f9bd1988f | |||
ba1bfd1bc0 | |||
e12926e971 | |||
5b4db0de3b | |||
70ff55faa6 | |||
f2bd47d8bc | |||
e2b9c9e98e | |||
b0791a594f | |||
ea5eaa6ed2 | |||
ec3fd1d585 | |||
d76de22977 | |||
c0e3483cc7 | |||
0994cdf3b6 | |||
a76fd82262 | |||
2f919c7d69 | |||
f12592731b | |||
4a20e1a25d | |||
cc7111c3ff | |||
81972aed62 | |||
5e2092c6d4 | |||
d136f8ac91 | |||
5b23917940 | |||
2cda0a3798 | |||
f315ad1ce9 | |||
49f1c273c2 | |||
8518476c87 | |||
a34ed756db | |||
a48232c4e3 | |||
ac65e38390 | |||
eab3a753f5 | |||
9e5922e121 | |||
bf57b23776 | |||
8f4a20fd46 | |||
b3716da5ac | |||
2c129fd800 | |||
6fb18e7a25 | |||
38d28d6944 | |||
a187e1a7a2 | |||
7d7a19c7c2 | |||
6a2ae2153a | |||
f50ba1a91e | |||
b09575dbd6 | |||
c52ee3d91d | |||
1ced5c0425 | |||
e399a5c9b1 | |||
08dff5fb67 | |||
912760f2a7 | |||
4b62fdbf93 | |||
df593d43a7 | |||
89b1fba771 | |||
5505c91ae0 | |||
cc720ff399 | |||
29f07f6c56 | |||
a69e197a1f | |||
6dddd8eff8 | |||
51aada8922 | |||
b47bf40e8f | |||
48151bf365 | |||
659950996d | |||
6e656a4d0a | |||
bf308dd13d | |||
982f3b1943 | |||
7a4ea8cf1b | |||
7c0482a87c | |||
101ededd89 | |||
a8cbc0040e | |||
ed91bb445b | |||
f158a222f4 | |||
46bcad1fca | |||
5e147afb9f | |||
b061d4593f | |||
3fa520b4ef | |||
2df05b4afd | |||
e0569a6748 | |||
2e6ab398d9 | |||
94b03d2a6b | |||
f84fe30bb6 | |||
049ddef531 | |||
a1a214c742 | |||
c40a483ffa | |||
aff99acfae | |||
628129c08d |
@ -1,2 +0,0 @@
|
||||
[config]
|
||||
project = Oqtane.Server/Oqtane.Server.csproj
|
@ -52,6 +52,8 @@ namespace Microsoft.Extensions.DependencyInjection
|
||||
services.AddScoped<IVisitorService, VisitorService>();
|
||||
services.AddScoped<ISyncService, SyncService>();
|
||||
services.AddScoped<ILocalizationCookieService, LocalizationCookieService>();
|
||||
services.AddScoped<ICookieConsentService, CookieConsentService>();
|
||||
services.AddScoped<IOutputCacheService, OutputCacheService>();
|
||||
|
||||
// providers
|
||||
services.AddScoped<ITextEditor, Oqtane.Modules.Controls.QuillJSTextEditor>();
|
||||
|
119
Oqtane.Client/Installer/Controls/AzureSqlConfig.razor
Normal file
119
Oqtane.Client/Installer/Controls/AzureSqlConfig.razor
Normal file
@ -0,0 +1,119 @@
|
||||
@namespace Oqtane.Installer.Controls
|
||||
@implements Oqtane.Interfaces.IDatabaseConfigControl
|
||||
@inject IStringLocalizer<SqlServerConfig> Localizer
|
||||
@inject IStringLocalizer<SharedResources> SharedLocalizer
|
||||
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="server" HelpText="Enter the database server name. This might include a port number as well if you are using a cloud service." ResourceKey="Server">Server:</Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="server" type="text" class="form-control" @bind="@_server" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="database" HelpText="Enter the name of the database" ResourceKey="Database">Database:</Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="database" type="text" class="form-control" @bind="@_database" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="security" HelpText="Select your security method" ResourceKey="Security">Security:</Label>
|
||||
<div class="col-sm-9">
|
||||
<select id="security" class="form-select custom-select" @bind="@_security">
|
||||
<option value="integrated" selected>@Localizer["Integrated"]</option>
|
||||
<option value="custom">@Localizer["Custom"]</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
@if (_security == "custom")
|
||||
{
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="uid" HelpText="Enter the username to use for the database" ResourceKey="Uid">User Id:</Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="uid" type="text" class="form-control" @bind="@_uid" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="pwd" HelpText="Enter the password to use for the database" ResourceKey="Pwd">Password:</Label>
|
||||
<div class="col-sm-9">
|
||||
<div class="input-group">
|
||||
<input id="pwd" type="@_passwordType" class="form-control" @bind="@_pwd" autocomplete="new-password" />
|
||||
<button type="button" class="btn btn-secondary" @onclick="@TogglePassword" tabindex="-1">@_togglePassword</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="encryption" HelpText="Specify if you are using an encrypted database connection. It is highly recommended to use encryption in a production environment." ResourceKey="Encryption">Encryption:</Label>
|
||||
<div class="col-sm-9">
|
||||
<select id="encryption" class="form-select custom-select" @bind="@_encryption">
|
||||
<option value="true">@SharedLocalizer["True"]</option>
|
||||
<option value="false">@SharedLocalizer["False"]</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
@if (_encryption == "true")
|
||||
{
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="trustservercertificate" HelpText="Specify the type of certificate you are using for encryption. Verifiable is equivalent to False. Self Signed is equivalent to True." ResourceKey="TrustServerCertificate">Certificate:</Label>
|
||||
<div class="col-sm-9">
|
||||
<select id="encryption" class="form-select custom-select" @bind="@_trustservercertificate">
|
||||
<option value="true">@Localizer["Self Signed"]</option>
|
||||
<option value="false">@Localizer["Verifiable"]</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
@code {
|
||||
private string _server = "tcp:{SQL Server Name}.database.windows.net,1433";
|
||||
private string _database = "{SQL Database Name}";
|
||||
private string _security = "custom";
|
||||
private string _uid = "{SQL Administrator Login}";
|
||||
private string _pwd = String.Empty;
|
||||
private string _passwordType = "password";
|
||||
private string _togglePassword = string.Empty;
|
||||
private string _encryption = "true";
|
||||
private string _trustservercertificate = "false";
|
||||
|
||||
protected override void OnInitialized()
|
||||
{
|
||||
_togglePassword = SharedLocalizer["ShowPassword"];
|
||||
}
|
||||
|
||||
public string GetConnectionString()
|
||||
{
|
||||
var connectionString = String.Empty;
|
||||
|
||||
if (!String.IsNullOrEmpty(_server) && !String.IsNullOrEmpty(_database))
|
||||
{
|
||||
connectionString = $"Data Source={_server};Initial Catalog={_database};";
|
||||
}
|
||||
|
||||
if (_security == "integrated")
|
||||
{
|
||||
connectionString += "Integrated Security=SSPI;";
|
||||
}
|
||||
else
|
||||
{
|
||||
connectionString += $"User ID={_uid};Password={_pwd};";
|
||||
}
|
||||
connectionString += $"Encrypt={_encryption};";
|
||||
connectionString += $"TrustServerCertificate={_trustservercertificate};";
|
||||
|
||||
return connectionString;
|
||||
}
|
||||
|
||||
private void TogglePassword()
|
||||
{
|
||||
if (_passwordType == "password")
|
||||
{
|
||||
_passwordType = "text";
|
||||
_togglePassword = SharedLocalizer["HidePassword"];
|
||||
}
|
||||
else
|
||||
{
|
||||
_passwordType = "password";
|
||||
_togglePassword = SharedLocalizer["ShowPassword"];
|
||||
}
|
||||
}
|
||||
}
|
@ -4,7 +4,7 @@
|
||||
@inject IStringLocalizer<SharedResources> SharedLocalizer
|
||||
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="server" HelpText="Enter the database server name. This might include a port number as well if you are using a cloud service (ie. servername.database.windows.net,1433) " ResourceKey="Server">Server:</Label>
|
||||
<Label Class="col-sm-3" For="server" HelpText="Enter the database server name. This might include a port number as well if you are using a cloud service." ResourceKey="Server">Server:</Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="server" type="text" class="form-control" @bind="@_server" />
|
||||
</div>
|
||||
|
@ -15,7 +15,7 @@
|
||||
<div class="row">
|
||||
<div class="mx-auto text-center">
|
||||
<img src="oqtane-black.png" />
|
||||
<div style="font-weight: bold">@SharedLocalizer["Version"] @Constants.Version (.NET 9)</div>
|
||||
<div style="font-weight: bold">@SharedLocalizer["Version"] @Constants.Version (.NET @Environment.Version.Major)</div>
|
||||
</div>
|
||||
</div>
|
||||
<hr class="app-rule" />
|
||||
|
@ -45,7 +45,7 @@
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="retention" HelpText="Number of log entries to retain for this job" ResourceKey="RetentionLog">Retention Log (Items): </Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="retention" type="number" min="0" step ="1" class="form-control" @bind="@_retentionHistory" maxlength="4" required />
|
||||
<input id="retention" type="number" min="0" step ="1" class="form-control" @bind="@_retentionHistory" maxlength="3" required />
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
|
@ -55,10 +55,6 @@ else
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
await GetJobs();
|
||||
if (_jobs.Count == 0)
|
||||
{
|
||||
AddModuleMessage(string.Format(Localizer["Message.NoJobs"], NavigateUrl("admin/system")), MessageType.Warning);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task GetJobs()
|
||||
|
@ -44,14 +44,12 @@ else
|
||||
|
||||
private async Task GetJobLogs()
|
||||
{
|
||||
_jobLogs = await JobLogService.GetJobLogsAsync();
|
||||
|
||||
var jobId = -1;
|
||||
if (PageState.QueryString.ContainsKey("id"))
|
||||
{
|
||||
_jobLogs = _jobLogs.Where(item => item.JobId == Int32.Parse(PageState.QueryString["id"])).ToList();
|
||||
jobId = int.Parse(PageState.QueryString["id"]);
|
||||
}
|
||||
|
||||
_jobLogs = _jobLogs.OrderByDescending(item => item.JobLogId).ToList();
|
||||
_jobLogs = await JobLogService.GetJobLogsAsync(jobId);
|
||||
}
|
||||
|
||||
private string DisplayStatus(bool isExecuting, bool? succeeded)
|
||||
|
@ -29,12 +29,12 @@ else
|
||||
{
|
||||
<div class="form-group">
|
||||
<Label Class="control-label" For="username" HelpText="Please enter your Username" ResourceKey="Username">Username:</Label>
|
||||
<input id="username" type="text" @ref="username" class="form-control" placeholder="@Localizer["Username.Placeholder"]" @bind="@_username" required />
|
||||
<input id="username" type="text" @ref="username" class="form-control" placeholder="@Localizer["Username.Placeholder"]" @bind="@_username" @bind:event="oninput" required />
|
||||
</div>
|
||||
<div class="form-group mt-2">
|
||||
<Label Class="control-label" For="password" HelpText="Please enter your Password" ResourceKey="Password">Password:</Label>
|
||||
<div class="input-group">
|
||||
<input id="password" type="@_passwordtype" name="Password" class="form-control" placeholder="@Localizer["Password.Placeholder"]" @bind="@_password" required />
|
||||
<input id="password" type="@_passwordtype" name="Password" class="form-control" placeholder="@Localizer["Password.Placeholder"]" @bind="@_password" @bind:event="oninput" required />
|
||||
<button type="button" class="btn btn-secondary" @onclick="@TogglePassword" tabindex="-1">@_togglepassword</button>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -63,24 +63,7 @@
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="packagename" HelpText="The unique name of the package from which this module was installed. This value must be specified within the module's IModule interface specification." ResourceKey="PackageName">Package Name: </Label>
|
||||
<div class="col-sm-9">
|
||||
@if (!string.IsNullOrEmpty(_packagename))
|
||||
{
|
||||
<div class="input-group">
|
||||
<input id="packagename" class="form-control" @bind="@_packagename" disabled />
|
||||
@if (string.IsNullOrEmpty(_packageurl))
|
||||
{
|
||||
<button type="button" class="btn btn-secondary" @onclick="ValidatePackage">@Localizer["Validate"]</button>
|
||||
}
|
||||
else
|
||||
{
|
||||
<a href="@_packageurl" target="_blank" class="btn btn-primary">@SharedLocalizer["Download"]</a>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
else
|
||||
{
|
||||
<input id="packagename" class="form-control" @bind="@_packagename" disabled />
|
||||
}
|
||||
<input id="packagename" class="form-control" @bind="@_packagename" disabled />
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
@ -90,7 +73,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="url" HelpText="The reference url of the module" ResourceKey="ReferenceUrl">Reference Url: </Label>
|
||||
<Label Class="col-sm-3" For="url" HelpText="The url of the module" ResourceKey="Url">Url: </Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="url" class="form-control" @bind="@_url" disabled />
|
||||
</div>
|
||||
@ -244,7 +227,6 @@
|
||||
private string _moduledefinitionname = "";
|
||||
private string _version;
|
||||
private string _packagename = "";
|
||||
private string _packageurl = "";
|
||||
private string _owner = "";
|
||||
private string _url = "";
|
||||
private string _contact = "";
|
||||
@ -445,27 +427,5 @@
|
||||
}
|
||||
}
|
||||
|
||||
private async Task ValidatePackage()
|
||||
{
|
||||
try
|
||||
{
|
||||
var package = await PackageService.GetPackageAsync(_packagename, _version, true);
|
||||
if (package == null || string.IsNullOrEmpty(package.PackageUrl))
|
||||
{
|
||||
AddModuleMessage(Localizer["Message.Validate"], MessageType.Warning);
|
||||
}
|
||||
else
|
||||
{
|
||||
_packageurl = package.PackageUrl;
|
||||
AddModuleMessage(Localizer["Message.Download"], MessageType.Info);
|
||||
}
|
||||
StateHasChanged();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
await logger.LogError(ex, "Error Downloading Package {PackageId} {Version}", _packagename, _version);
|
||||
AddModuleMessage(Localizer["Error.Validate"], MessageType.Error);
|
||||
}
|
||||
}
|
||||
private string Browse(Page page) => string.IsNullOrEmpty(page.Url) ? NavigateUrl(page.Path) : page.Url;
|
||||
}
|
||||
|
@ -13,32 +13,32 @@
|
||||
}
|
||||
else
|
||||
{
|
||||
<div class="container">
|
||||
<div class="row mb-3 align-items-center">
|
||||
<div class="col-sm-6">
|
||||
<ActionLink Action="Add" Text="Install Module" ResourceKey="InstallModule" />
|
||||
@((MarkupString)" ")
|
||||
<ActionLink Action="Create" Text="Create Module" ResourceKey="CreateModule" Class="btn btn-secondary" />
|
||||
</div>
|
||||
<div class="col-sm-6">
|
||||
<select class="form-select" @onchange="(e => CategoryChanged(e))">
|
||||
@foreach (var category in _categories)
|
||||
{
|
||||
if (category == _category)
|
||||
{
|
||||
<option value="@category" selected>@category @Localizer["Modules"]</option>
|
||||
}
|
||||
else
|
||||
{
|
||||
<option value="@category">@category @Localizer["Modules"]</option>
|
||||
}
|
||||
}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Pager Items="@_moduleDefinitions">
|
||||
<div class="container">
|
||||
<div class="row mb-3 align-items-center">
|
||||
<div class="col-sm-6">
|
||||
<ActionLink Action="Add" Text="Install Module" ResourceKey="InstallModule" />
|
||||
<ActionLink Action="Create" Text="Create Module" ResourceKey="CreateModule" Class="btn btn-secondary ps-2" />
|
||||
<button type="button" class="btn btn-secondary pw-2" @onclick="@Synchronize">@Localizer["Synchronize"]</button>
|
||||
</div>
|
||||
<div class="col-sm-6">
|
||||
<select class="form-select" @onchange="(e => CategoryChanged(e))">
|
||||
@foreach (var category in _categories)
|
||||
{
|
||||
if (category == _category)
|
||||
{
|
||||
<option value="@category" selected>@category @Localizer["Modules"]</option>
|
||||
}
|
||||
else
|
||||
{
|
||||
<option value="@category">@category @Localizer["Modules"]</option>
|
||||
}
|
||||
}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Pager Items="@_moduleDefinitions">
|
||||
<Header>
|
||||
<th style="width: 1px;"> </th>
|
||||
<th style="width: 1px;"> </th>
|
||||
@ -61,17 +61,17 @@ else
|
||||
<td>@context.Name</td>
|
||||
<td>@context.Version</td>
|
||||
<td>
|
||||
@if (context.IsEnabled)
|
||||
{
|
||||
<span>@SharedLocalizer["Yes"]</span>
|
||||
}
|
||||
else
|
||||
{
|
||||
<span>@SharedLocalizer["No"]</span>
|
||||
}
|
||||
@if (context.IsEnabled)
|
||||
{
|
||||
<span>@SharedLocalizer["Yes"]</span>
|
||||
}
|
||||
else
|
||||
{
|
||||
<span>@SharedLocalizer["No"]</span>
|
||||
}
|
||||
</td>
|
||||
<td>
|
||||
@if (context.AssemblyName == Constants.ClientId || _modules.Where(m => m.ModuleDefinition?.ModuleDefinitionId == context.ModuleDefinitionId).FirstOrDefault() != null)
|
||||
@if (context.AssemblyName == Constants.ClientId || _modules.Where(m => m.ModuleDefinition?.ModuleDefinitionId == context.ModuleDefinitionId).FirstOrDefault() != null)
|
||||
{
|
||||
<span>@SharedLocalizer["Yes"]</span>
|
||||
}
|
||||
@ -87,9 +87,9 @@ else
|
||||
@((MarkupString)PurchaseLink(context.PackageName))
|
||||
</td>
|
||||
<td>
|
||||
@{
|
||||
var version = UpgradeAvailable(context.PackageName, context.Version);
|
||||
}
|
||||
@{
|
||||
var version = UpgradeAvailable(context.PackageName, context.Version);
|
||||
}
|
||||
@if (version != context.Version)
|
||||
{
|
||||
<button type="button" class="btn btn-success" @onclick=@(async () => await DownloadModule(context.PackageName, version))>@SharedLocalizer["Upgrade"]</button>
|
||||
@ -153,10 +153,10 @@ else
|
||||
link = "<a class=\"btn btn-primary\" style=\"text-decoration: none !important\" href=\"" + package.PaymentUrl + "\" target=\"_new\">" + package.ExpiryDate.Value.Date.ToString("MMM dd, yyyy") + "</a>";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return link;
|
||||
}
|
||||
}
|
||||
}
|
||||
return link;
|
||||
}
|
||||
|
||||
private string SupportLink(string packagename, string version)
|
||||
{
|
||||
@ -172,52 +172,75 @@ else
|
||||
return link;
|
||||
}
|
||||
|
||||
private string UpgradeAvailable(string packagename, string version)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(packagename) && _packages != null)
|
||||
{
|
||||
var package = _packages.Where(item => item.PackageId == packagename).FirstOrDefault();
|
||||
if (package != null && Version.Parse(package.Version).CompareTo(Version.Parse(version)) > 0)
|
||||
{
|
||||
return package.Version;
|
||||
}
|
||||
}
|
||||
return version;
|
||||
}
|
||||
private string UpgradeAvailable(string packagename, string version)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(packagename) && _packages != null)
|
||||
{
|
||||
var package = _packages.Where(item => item.PackageId == packagename).FirstOrDefault();
|
||||
if (package != null && Version.Parse(package.Version).CompareTo(Version.Parse(version)) > 0)
|
||||
{
|
||||
return package.Version;
|
||||
}
|
||||
}
|
||||
return version;
|
||||
}
|
||||
|
||||
private async Task DownloadModule(string packagename, string version)
|
||||
{
|
||||
try
|
||||
{
|
||||
await PackageService.DownloadPackageAsync(packagename, version);
|
||||
await logger.LogInformation("Module Downloaded {ModuleDefinitionName} {Version}", packagename, version);
|
||||
AddModuleMessage(string.Format(Localizer["Success.Module.Install"], NavigateUrl("admin/system")), MessageType.Success);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
await logger.LogError(ex, "Error Downloading Module {ModuleDefinitionName} {Version} {Error}", packagename, version, ex.Message);
|
||||
AddModuleMessage(Localizer["Error.Module.Download"], MessageType.Error);
|
||||
}
|
||||
}
|
||||
private async Task DownloadModule(string packagename, string version)
|
||||
{
|
||||
try
|
||||
{
|
||||
await PackageService.DownloadPackageAsync(packagename, version);
|
||||
await logger.LogInformation("Module Downloaded {ModuleDefinitionName} {Version}", packagename, version);
|
||||
AddModuleMessage(string.Format(Localizer["Success.Module.Install"], NavigateUrl("admin/system")), MessageType.Success);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
await logger.LogError(ex, "Error Downloading Module {ModuleDefinitionName} {Version} {Error}", packagename, version, ex.Message);
|
||||
AddModuleMessage(Localizer["Error.Module.Download"], MessageType.Error);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task DeleteModule(ModuleDefinition moduleDefinition)
|
||||
{
|
||||
try
|
||||
{
|
||||
await ModuleDefinitionService.DeleteModuleDefinitionAsync(moduleDefinition.ModuleDefinitionId, moduleDefinition.SiteId);
|
||||
AddModuleMessage(Localizer["Success.Module.Delete"], MessageType.Success);
|
||||
NavigationManager.NavigateTo(NavigateUrl(PageState.Page.Path, true));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
await logger.LogError(ex, "Error Deleting Module {ModuleDefinition} {Error}", moduleDefinition, ex.Message);
|
||||
AddModuleMessage(Localizer["Error.Module.Delete"], MessageType.Error);
|
||||
}
|
||||
}
|
||||
private async Task DeleteModule(ModuleDefinition moduleDefinition)
|
||||
{
|
||||
try
|
||||
{
|
||||
await ModuleDefinitionService.DeleteModuleDefinitionAsync(moduleDefinition.ModuleDefinitionId, moduleDefinition.SiteId);
|
||||
AddModuleMessage(Localizer["Success.Module.Delete"], MessageType.Success);
|
||||
NavigationManager.NavigateTo(NavigateUrl(PageState.Page.Path, true));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
await logger.LogError(ex, "Error Deleting Module {ModuleDefinition} {Error}", moduleDefinition, ex.Message);
|
||||
AddModuleMessage(Localizer["Error.Module.Delete"], MessageType.Error);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task CategoryChanged(ChangeEventArgs e)
|
||||
{
|
||||
_category = (string)e.Value;
|
||||
private async Task CategoryChanged(ChangeEventArgs e)
|
||||
{
|
||||
_category = (string)e.Value;
|
||||
await LoadModuleDefinitions();
|
||||
}
|
||||
}
|
||||
|
||||
private async Task Synchronize()
|
||||
{
|
||||
try
|
||||
{
|
||||
ShowProgressIndicator();
|
||||
foreach (var moduleDefinition in _moduleDefinitions)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(moduleDefinition.PackageName) && !_packages.Any(item => item.PackageId == moduleDefinition.PackageName))
|
||||
{
|
||||
var package = await PackageService.GetPackageAsync(moduleDefinition.PackageName, moduleDefinition.Version, false);
|
||||
}
|
||||
}
|
||||
HideProgressIndicator();
|
||||
AddModuleMessage(Localizer["Success.Module.Synchronize"], MessageType.Success);
|
||||
NavigationManager.NavigateTo(NavigateUrl(PageState.Page.Path, true));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
await logger.LogError(ex, "Error Synchronizing Modules {Error}", ex.Message);
|
||||
AddModuleMessage(Localizer["Error.Module.Synchronize"], MessageType.Error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -16,7 +16,7 @@
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="name" HelpText="Enter the page name" ResourceKey="Name">Name: </Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="name" class="form-control" @bind="@_name" required />
|
||||
<input id="name" class="form-control" @bind="@_name" maxlength="50" required />
|
||||
</div>
|
||||
</div>
|
||||
@if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Admin))
|
||||
@ -101,13 +101,13 @@
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="path" HelpText="Optionally enter a url path for this page (ie. home ). If you do not provide a url path, the page name will be used. If the page is intended to be the root path specify '/'." ResourceKey="UrlPath">Url Path: </Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="path" class="form-control" @bind="@_path" />
|
||||
<input id="path" class="form-control" @bind="@_path" maxlength="256" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="url" HelpText="Optionally enter a url which this page should redirect to when a user navigates to it" ResourceKey="Redirect">Redirect: </Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="url" class="form-control" @bind="@_url" />
|
||||
<input id="url" class="form-control" @bind="@_url" maxlength="500" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
@ -147,7 +147,7 @@
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="title" HelpText="Optionally enter the page title. If you do not provide a page title, the page name will be used." ResourceKey="Title">Title: </Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="title" class="form-control" @bind="@_title" />
|
||||
<input id="title" class="form-control" @bind="@_title" maxlength="200" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
@ -186,13 +186,13 @@
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="headcontent" HelpText="Optionally enter content to be included in the page head (ie. meta, link, or script tags)" ResourceKey="HeadContent">Head Content: </Label>
|
||||
<div class="col-sm-9">
|
||||
<textarea id="headcontent" class="form-control" @bind="@_headcontent" rows="3"></textarea>
|
||||
<textarea id="headcontent" class="form-control" @bind="@_headcontent" rows="3" maxlength="4000"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="bodycontent" HelpText="Optionally enter content to be included in the page body (ie. script tags)" ResourceKey="BodyContent">Body Content: </Label>
|
||||
<div class="col-sm-9">
|
||||
<textarea id="bodycontent" class="form-control" @bind="@_bodycontent" rows="3"></textarea>
|
||||
<textarea id="bodycontent" class="form-control" @bind="@_bodycontent" rows="3" maxlength="4000"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -205,13 +205,13 @@
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="headcontent" HelpText="Optionally enter content to be included in the page head (ie. meta, link, or script tags)" ResourceKey="HeadContent">Head Content: </Label>
|
||||
<div class="col-sm-9">
|
||||
<textarea id="headcontent" class="form-control" @bind="@_headcontent" rows="3"></textarea>
|
||||
<textarea id="headcontent" class="form-control" @bind="@_headcontent" rows="3" maxlength="4000"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="bodycontent" HelpText="Optionally enter content to be included in the page body (ie. script tags)" ResourceKey="BodyContent">Body Content: </Label>
|
||||
<div class="col-sm-9">
|
||||
<textarea id="bodycontent" class="form-control" @bind="@_bodycontent" rows="3"></textarea>
|
||||
<textarea id="bodycontent" class="form-control" @bind="@_bodycontent" rows="3" maxlength="4000"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -14,6 +14,7 @@
|
||||
@inject IStringLocalizer<Index> Localizer
|
||||
@inject INotificationService NotificationService
|
||||
@inject IStringLocalizer<SharedResources> SharedLocalizer
|
||||
@inject IOutputCacheService CacheService
|
||||
|
||||
@if (_initialized)
|
||||
{
|
||||
@ -50,11 +51,12 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="sitemap" HelpText="The site map url for this site which can be submitted to search engines for indexing" ResourceKey="SiteMap">Site Map: </Label>
|
||||
<Label Class="col-sm-3" For="sitemap" HelpText="The site map url for this site which can be submitted to search engines for indexing. The sitemap is cached for 5 minutes and the cache can be manually cleared." ResourceKey="SiteMap">Site Map: </Label>
|
||||
<div class="col-sm-9">
|
||||
<div class="input-group">
|
||||
<input id="sitemap" class="form-control" @bind="@_sitemap" disabled />
|
||||
<a href="@_sitemap" class="btn btn-secondary" target="_new">@Localizer["Browse"]</a>
|
||||
<button type="button" class="btn btn-danger" @onclick="EvictSitemapOutputCache">@Localizer["SiteMap.EvictCache"]</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -72,20 +74,8 @@
|
||||
</div>
|
||||
</div>
|
||||
<br />
|
||||
<Section Name="Appearance" Heading="Appearance" ResourceKey="Appearance">
|
||||
<Section Name="Theme" Heading="Theme" ResourceKey="Theme">
|
||||
<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">
|
||||
<FileManager FileId="@_logofileid" Filter="@_imageFiles" @ref="_logofilemanager" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="favicon" HelpText="Specify a Favicon" ResourceKey="FavoriteIcon">Favicon: </Label>
|
||||
<div class="col-sm-9">
|
||||
<FileManager FileId="@_faviconfileid" Filter="ico,png,gif" @ref="_faviconfilemanager" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="defaultTheme" HelpText="Select the sites default theme" ResourceKey="DefaultTheme">Default Theme: </Label>
|
||||
<div class="col-sm-9">
|
||||
@ -126,6 +116,32 @@
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="cookieconsent" HelpText="Specify if cookie consent is enabled on this site. Please note this option must be used in conjunction with a Theme which supports cookie consent." ResourceKey="CookieConsent">Cookie Consent: </Label>
|
||||
<div class="col-sm-9">
|
||||
<select id="cookieconsent" class="form-select" @bind="@_cookieconsent">
|
||||
<option value="">@SharedLocalizer["Disabled"]</option>
|
||||
<option value="optin">@Localizer["OptIn"]</option>
|
||||
<option value="optout">@Localizer["OptOut"]</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Section>
|
||||
<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">
|
||||
<FileManager FileId="@_logofileid" Filter="@_imageFiles" @ref="_logofilemanager" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="favicon" HelpText="Specify a Favicon" ResourceKey="FavoriteIcon">Favicon: </Label>
|
||||
<div class="col-sm-9">
|
||||
<FileManager FileId="@_faviconfileid" Filter="ico,png,gif" @ref="_faviconfilemanager" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Section>
|
||||
<Section Name="Functionality" Heading="Functionality" ResourceKey="Functionality">
|
||||
@ -415,6 +431,7 @@
|
||||
private string _themetype = "";
|
||||
private string _containertype = "";
|
||||
private string _admincontainertype = "";
|
||||
private string _cookieconsent = "";
|
||||
|
||||
private Dictionary<string, string> _textEditors = new Dictionary<string, string>();
|
||||
private string _textEditor = "";
|
||||
@ -505,6 +522,7 @@
|
||||
_containers = ThemeService.GetContainerControls(PageState.Site.Themes, _themetype);
|
||||
_containertype = (!string.IsNullOrEmpty(site.DefaultContainerType)) ? site.DefaultContainerType : Constants.DefaultContainer;
|
||||
_admincontainertype = (!string.IsNullOrEmpty(site.AdminContainerType)) ? site.AdminContainerType : Constants.DefaultAdminContainer;
|
||||
_cookieconsent = SettingService.GetSetting(settings, "CookieConsent", string.Empty);
|
||||
|
||||
// functionality
|
||||
var textEditors = ServiceProvider.GetServices<ITextEditor>();
|
||||
@ -717,6 +735,9 @@
|
||||
settings = SettingService.SetSetting(settings, "SiteGuid", _siteguid, true);
|
||||
settings = SettingService.SetSetting(settings, "NotificationRetention", _retention.ToString(), true);
|
||||
|
||||
//cookie consent
|
||||
settings = SettingService.SetSetting(settings, "CookieConsent", _cookieconsent);
|
||||
|
||||
// functionality
|
||||
settings = SettingService.SetSetting(settings, "TextEditor", _textEditor);
|
||||
|
||||
@ -913,4 +934,9 @@
|
||||
_aliasname = "";
|
||||
StateHasChanged();
|
||||
}
|
||||
|
||||
private async Task EvictSitemapOutputCache() {
|
||||
await CacheService.EvictByTag(Constants.SitemapOutputCacheTag);
|
||||
AddModuleMessage(Localizer["Success.SiteMap.CacheEvicted"], MessageType.Success);
|
||||
}
|
||||
}
|
||||
|
@ -153,8 +153,10 @@
|
||||
</div>
|
||||
<br /><br />
|
||||
<button type="button" class="btn btn-success" @onclick="SaveConfig">@SharedLocalizer["Save"]</button>
|
||||
<a class="btn btn-primary" href="swagger/index.html" target="_new">@Localizer["Swagger"]</a>
|
||||
<ActionDialog Header="Restart Application" Message="Are You Sure You Wish To Restart The Application?" Action="Restart Application" Security="SecurityAccessLevel.Host" Class="btn btn-danger" OnClick="@(async () => await RestartApplication())" ResourceKey="RestartApplication" />
|
||||
<br /><br />
|
||||
<a class="btn btn-primary" href="swagger/index.html" target="_new">@Localizer["Swagger"]</a>
|
||||
<a class="btn btn-secondary" href="api/endpoint" target="_new">@Localizer["Endpoints"]</a>
|
||||
</TabPanel>
|
||||
<TabPanel Name="Log" Heading="Log" ResourceKey="Log">
|
||||
<div class="container">
|
||||
|
@ -45,24 +45,7 @@
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="packagename" HelpText="The unique name of the package from which this theme was installed. This value must be specified within the theme's ITheme interface specification." ResourceKey="PackageName">Package Name: </Label>
|
||||
<div class="col-sm-9">
|
||||
@if (!string.IsNullOrEmpty(_packagename))
|
||||
{
|
||||
<div class="input-group">
|
||||
<input id="packagename" class="form-control" @bind="@_packagename" disabled />
|
||||
@if (string.IsNullOrEmpty(_packageurl))
|
||||
{
|
||||
<button type="button" class="btn btn-secondary" @onclick="ValidatePackage">@Localizer["Validate"]</button>
|
||||
}
|
||||
else
|
||||
{
|
||||
<a href="@_packageurl" target="_blank" class="btn btn-primary">@SharedLocalizer["Download"]</a>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
else
|
||||
{
|
||||
<input id="packagename" class="form-control" @bind="@_packagename" disabled />
|
||||
}
|
||||
<input id="packagename" class="form-control" @bind="@_packagename" disabled />
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
@ -72,7 +55,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="url" HelpText="The reference url of the theme" ResourceKey="ReferenceUrl">Reference Url: </Label>
|
||||
<Label Class="col-sm-3" For="url" HelpText="The url of the theme" ResourceKey="Url">Url: </Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="url" class="form-control" @bind="@_url" disabled />
|
||||
</div>
|
||||
@ -116,7 +99,6 @@
|
||||
private string _name;
|
||||
private string _version;
|
||||
private string _packagename = "";
|
||||
private string _packageurl = "";
|
||||
private string _owner = "";
|
||||
private string _url = "";
|
||||
private string _contact = "";
|
||||
@ -185,27 +167,4 @@
|
||||
AddModuleMessage(SharedLocalizer["Message.InfoRequired"], MessageType.Warning);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task ValidatePackage()
|
||||
{
|
||||
try
|
||||
{
|
||||
var package = await PackageService.GetPackageAsync(_packagename, _version, true);
|
||||
if (package == null || string.IsNullOrEmpty(package.PackageUrl))
|
||||
{
|
||||
AddModuleMessage(Localizer["Message.Validate"], MessageType.Warning);
|
||||
}
|
||||
else
|
||||
{
|
||||
_packageurl = package.PackageUrl;
|
||||
AddModuleMessage(Localizer["Message.Download"], MessageType.Info);
|
||||
}
|
||||
StateHasChanged();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
await logger.LogError(ex, "Error Downloading Package {PackageId} {Version}", _packagename, _version);
|
||||
AddModuleMessage(Localizer["Error.Validate"], MessageType.Error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -4,6 +4,7 @@
|
||||
@inject NavigationManager NavigationManager
|
||||
@inject IThemeService ThemeService
|
||||
@inject IPackageService PackageService
|
||||
@inject ISiteService SiteService
|
||||
@inject IStringLocalizer<Index> Localizer
|
||||
@inject IStringLocalizer<SharedResources> SharedLocalizer
|
||||
|
||||
@ -14,11 +15,12 @@
|
||||
else
|
||||
{
|
||||
<ActionLink Action="Add" Text="Install Theme" ResourceKey="InstallTheme" />
|
||||
@((MarkupString)" ")
|
||||
<ActionLink Action="Create" Text="Create Theme" ResourceKey="CreateTheme" Class="btn btn-secondary" />
|
||||
<ActionLink Action="Create" Text="Create Theme" ResourceKey="CreateTheme" Class="btn btn-secondary ps-2" />
|
||||
<button type="button" class="btn btn-secondary pw-2" @onclick="@Synchronize">@Localizer["Synchronize"]</button>
|
||||
|
||||
<Pager Items="@_themes">
|
||||
<Header>
|
||||
<th style="width: 1px;"> </th>
|
||||
<th style="width: 1px;"> </th>
|
||||
<th style="width: 1px;"> </th>
|
||||
<th>@SharedLocalizer["Name"]</th>
|
||||
@ -32,10 +34,11 @@ else
|
||||
<td><ActionLink Action="Edit" Text="Edit" Parameters="@($"id=" + context.ThemeId.ToString())" ResourceKey="EditTheme" /></td>
|
||||
<td>
|
||||
@if (context.AssemblyName != Constants.ClientId)
|
||||
{
|
||||
{
|
||||
<ActionDialog Header="Delete Theme" Message="@string.Format(Localizer["Confirm.Theme.Delete"], context.Name)" Action="Delete" Security="SecurityAccessLevel.Host" Class="btn btn-danger" OnClick="@(async () => await DeleteTheme(context))" ResourceKey="DeleteTheme" />
|
||||
}
|
||||
}
|
||||
</td>
|
||||
<td><NavLink class="btn btn-secondary" href="@NavigateUrl("admin/site")">@Localizer["Assign"]</NavLink></td>
|
||||
<td>@context.Name</td>
|
||||
<td>@context.Version</td>
|
||||
<td>
|
||||
@ -170,4 +173,27 @@ else
|
||||
AddModuleMessage(Localizer["Error.Theme.Delete"], MessageType.Error);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task Synchronize()
|
||||
{
|
||||
try
|
||||
{
|
||||
ShowProgressIndicator();
|
||||
foreach (var theme in _themes)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(theme.PackageName) && !_packages.Any(item => item.PackageId == theme.PackageName))
|
||||
{
|
||||
await PackageService.GetPackageAsync(theme.PackageName, theme.Version, false);
|
||||
}
|
||||
}
|
||||
HideProgressIndicator();
|
||||
AddModuleMessage(Localizer["Success.Theme.Synchronize"], MessageType.Success);
|
||||
NavigationManager.NavigateTo(NavigateUrl(PageState.Page.Path, true));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
await logger.LogError(ex, "Error Synchronizing Themes {Error}", ex.Message);
|
||||
AddModuleMessage(Localizer["Error.Theme.Synchronize"], MessageType.Error);
|
||||
}
|
||||
}
|
||||
}
|
@ -13,9 +13,21 @@
|
||||
<TabPanel Name="Download" ResourceKey="Download">
|
||||
@if (_package != null && _upgradeavailable)
|
||||
{
|
||||
<ModuleMessage Type="MessageType.Info" Message="Select The Download Button To Download The Framework Upgrade Package And Then Select Upgrade"></ModuleMessage>
|
||||
<div class="container">
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" HelpText="Specify if you want to backup files during the upgrade process. Disabling this option will result in a better experience in some environments." ResourceKey="Backup">Backup Files? </Label>
|
||||
<div class="col-sm-9">
|
||||
<select id="backup" class="form-select" @bind="@_backup">
|
||||
<option value="True">@SharedLocalizer["Yes"]</option>
|
||||
<option value="False">@SharedLocalizer["No"]</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<button type="button" class="btn btn-primary" @onclick=@(async () => await Download(Constants.PackageId, @_package.Version))>@SharedLocalizer["Download"] @_package.Version</button>
|
||||
<button type="button" class="btn btn-success" @onclick="Upgrade">@SharedLocalizer["Upgrade"]</button>
|
||||
<br /><br />
|
||||
<ModuleMessage Type="MessageType.Info" Message="Select The Download Button To Download The Framework Upgrade Package And Then Select Upgrade"></ModuleMessage>
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -23,7 +35,6 @@
|
||||
}
|
||||
</TabPanel>
|
||||
<TabPanel Name="Upload" ResourceKey="Upload">
|
||||
<ModuleMessage Type="MessageType.Info" Message=@Localizer["MessageUpgrade.Text"]></ModuleMessage>
|
||||
<div class="container">
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" HelpText="Upload A Framework Package And Then Select Upgrade" ResourceKey="Framework">Framework: </Label>
|
||||
@ -31,8 +42,19 @@
|
||||
<FileManager Folder="@Constants.PackagesFolder" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" HelpText="Specify if you want to backup files during the upgrade process. Disabling this option will result in a better experience in some environments." ResourceKey="Backup">Backup Files? </Label>
|
||||
<div class="col-sm-9">
|
||||
<select id="backup" class="form-select" @bind="@_backup">
|
||||
<option value="True">@SharedLocalizer["Yes"]</option>
|
||||
<option value="False">@SharedLocalizer["No"]</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<button type="button" class="btn btn-success" @onclick="Upgrade">@SharedLocalizer["Upgrade"]</button>
|
||||
<br /><br />
|
||||
<ModuleMessage Type="MessageType.Info" Message=@Localizer["MessageUpgrade.Text"]></ModuleMessage>
|
||||
</TabPanel>
|
||||
</TabStrip>
|
||||
}
|
||||
@ -41,6 +63,7 @@
|
||||
private bool _initialized = false;
|
||||
private Package _package;
|
||||
private bool _upgradeavailable = false;
|
||||
private string _backup = "True";
|
||||
|
||||
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Host;
|
||||
|
||||
@ -86,7 +109,7 @@
|
||||
ShowProgressIndicator();
|
||||
var interop = new Interop(JSRuntime);
|
||||
await interop.RedirectBrowser(NavigateUrl(), 10);
|
||||
await InstallationService.Upgrade();
|
||||
await InstallationService.Upgrade(bool.Parse(_backup));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
@ -17,171 +17,180 @@ else
|
||||
{
|
||||
<TabStrip>
|
||||
<TabPanel Name="Users" Heading="Users" ResourceKey="Users">
|
||||
<ActionLink Action="Add" Text="Add User" Security="SecurityAccessLevel.Edit" ResourceKey="AddUser" />
|
||||
<ActionLink Action="Add" Text="Add User" Security="SecurityAccessLevel.Edit" ResourceKey="AddUser" />
|
||||
<ActionLink Text="Import Users" Class="btn btn-secondary ms-2" Action="Users" Security="SecurityAccessLevel.Admin" ResourceKey="ImportUsers"/>
|
||||
|
||||
<Pager Items="@users" RowClass="align-middle" SearchProperties="User.Username,User.Email,User.DisplayName">
|
||||
<Header>
|
||||
<th style="width: 1px;"> </th>
|
||||
<th style="width: 1px;"> </th>
|
||||
<th style="width: 1px;"> </th>
|
||||
<Header>
|
||||
<th style="width: 1px;"> </th>
|
||||
<th style="width: 1px;"> </th>
|
||||
<th style="width: 1px;"> </th>
|
||||
<th class="app-sort-th link-primary text-decoration-underline" @onclick="@(() => SortTable("Username"))">@Localizer["Username"]<i class="@(SetSortIcon("Username"))"></i></th>
|
||||
<th class="app-sort-th link-primary text-decoration-underline" @onclick="@(() => SortTable("DisplayName"))">@Localizer["Name"]<i class="@(SetSortIcon("DisplayName"))"></i></th>
|
||||
<th class="app-sort-th link-primary text-decoration-underline" @onclick="@(() => SortTable("Email"))">@Localizer["Email"]<i class="@(SetSortIcon("Email"))"></i></th>
|
||||
<th class="app-sort-th link-primary text-decoration-underline" @onclick="@(() => SortTable("LastLoginOn"))">@Localizer["LastLoginOn"]<i class="@(SetSortIcon("LastLoginOn"))"></i></th>
|
||||
</Header>
|
||||
<Row>
|
||||
<td>
|
||||
</Header>
|
||||
<Row>
|
||||
<td>
|
||||
<ActionLink Action="Edit" Text="Edit" Parameters="@($"id=" + context.UserId.ToString())" Security="SecurityAccessLevel.Edit" ResourceKey="EditUser" />
|
||||
</td>
|
||||
<td>
|
||||
</td>
|
||||
<td>
|
||||
<ActionDialog Header="Delete User" Message="@string.Format(Localizer["Confirm.User.Delete"], context.User.DisplayName)" Action="Delete" Security="SecurityAccessLevel.Edit" Class="btn btn-danger" OnClick="@(async () => await DeleteUser(context))" Disabled="@(context.UserId == PageState.User.UserId || context.User.IsDeleted)" ResourceKey="DeleteUser" />
|
||||
</td>
|
||||
<td>
|
||||
</td>
|
||||
<td>
|
||||
<ActionLink Action="Roles" Text="Roles" Parameters="@($"id=" + context.UserId.ToString())" Security="SecurityAccessLevel.Edit" ResourceKey="Roles" />
|
||||
</td>
|
||||
<td>@context.User.Username</td>
|
||||
<td>@context.User.DisplayName</td>
|
||||
<td>@((MarkupString)string.Format("<a href=\"mailto:{0}\">{1}</a>", @context.User.Email, @context.User.Email))</td>
|
||||
<td>@((context.User.LastLoginOn != DateTime.MinValue) ? string.Format("{0:dd-MMM-yyyy HH:mm:ss}", context.User.LastLoginOn) : "")</td>
|
||||
</Row>
|
||||
</Pager>
|
||||
</td>
|
||||
<td>@context.User.Username</td>
|
||||
<td>@context.User.DisplayName</td>
|
||||
<td>@((MarkupString)string.Format("<a href=\"mailto:{0}\">{1}</a>", @context.User.Email, @context.User.Email))</td>
|
||||
<td>@((context.User.LastLoginOn != DateTime.MinValue) ? string.Format("{0:dd-MMM-yyyy HH:mm:ss}", context.User.LastLoginOn) : "")</td>
|
||||
</Row>
|
||||
</Pager>
|
||||
</TabPanel>
|
||||
<TabPanel Name="Settings" Heading="Settings" ResourceKey="Settings" Security="SecurityAccessLevel.Admin">
|
||||
<div class="container">
|
||||
<Section Name="User" Heading="User Settings" ResourceKey="UserSettings">
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="allowregistration" HelpText="Do you want anonymous visitors to be able to register for an account on the site" ResourceKey="AllowRegistration">Allow User Registration?</Label>
|
||||
<div class="col-sm-9">
|
||||
<select id="allowregistration" class="form-select" @bind="@_allowregistration">
|
||||
<option value="True">@SharedLocalizer["Yes"]</option>
|
||||
<option value="False">@SharedLocalizer["No"]</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
@if (_providertype != "")
|
||||
{
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="allowsitelogin" HelpText="Do you want to allow users to sign in using a username and password that is managed locally on this site? Note that you should only disable this option if you have already sucessfully configured an external login provider, or else you may lock yourself out of the site." ResourceKey="AllowSiteLogin">Allow Login?</Label>
|
||||
<div class="col-sm-9">
|
||||
<select id="allowsitelogin" class="form-select" @bind="@_allowsitelogin">
|
||||
<option value="true">@SharedLocalizer["Yes"]</option>
|
||||
<option value="false">@SharedLocalizer["No"]</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
else
|
||||
{
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="allowsitelogin" HelpText="Do you want to allow users to sign in using a username and password that is managed locally on this site? Note that you should only disable this option if you have already sucessfully configured an external login provider, or else you may lock yourself out of the site." ResourceKey="AllowSiteLogin">Allow Login?</Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="allowsitelogin" class="form-control" value="@SharedLocalizer["Yes"]" readonly />
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
@if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host))
|
||||
{
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="twofactor" HelpText="Do you want users to use two factor authentication? Note that you should use the Disabled option until you have successfully verified that the Notification Job in Scheduled Jobs is enabled and your SMTP options in Site Settings are configured or else you will lock yourself out." ResourceKey="TwoFactor">Two Factor?</Label>
|
||||
<div class="col-sm-9">
|
||||
<select id="twofactor" class="form-select" @bind="@_twofactor">
|
||||
<option value="false">@Localizer["Disabled"]</option>
|
||||
<option value="true">@Localizer["Optional"]</option>
|
||||
<option value="required">@Localizer["Required"]</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="cookiename" HelpText="You can choose to use a custom authentication cookie name for each site. However please be aware that if you want to share an authentication cookie between sites on the same domain they need to use a consistent cookie name. Also be aware that changing the authentication cookie name will logout all current users." ResourceKey="CookieName">Cookie Name:</Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="cookiename" class="form-control" @bind="@_cookiename" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<div class="container">
|
||||
<Section Name="User" Heading="User Settings" ResourceKey="UserSettings">
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="allowregistration" HelpText="Do you want anonymous visitors to be able to register for an account on the site" ResourceKey="AllowRegistration">Allow User Registration?</Label>
|
||||
<div class="col-sm-9">
|
||||
<select id="allowregistration" class="form-select" @bind="@_allowregistration">
|
||||
<option value="true">@SharedLocalizer["Yes"]</option>
|
||||
<option value="false">@SharedLocalizer["No"]</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
@if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host))
|
||||
{
|
||||
@if (_providertype != "")
|
||||
{
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="allowsitelogin" HelpText="Do you want to allow users to sign in using a username and password that is managed locally on this site? Note that you should only disable this option if you have already sucessfully configured an external login provider, or else you may lock yourself out of the site." ResourceKey="AllowSiteLogin">Allow Login?</Label>
|
||||
<div class="col-sm-9">
|
||||
<select id="allowsitelogin" class="form-select" @bind="@_allowsitelogin">
|
||||
<option value="true">@SharedLocalizer["Yes"]</option>
|
||||
<option value="false">@SharedLocalizer["No"]</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
else
|
||||
{
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="allowsitelogin" HelpText="Do you want to allow users to sign in using a username and password that is managed locally on this site? Note that you should only disable this option if you have already sucessfully configured an external login provider, or else you may lock yourself out of the site." ResourceKey="AllowSiteLogin">Allow Login?</Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="allowsitelogin" class="form-control" value="@SharedLocalizer["Yes"]" readonly />
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="twofactor" HelpText="Do you want users to use two factor authentication? Note that you should use the Disabled option until you have successfully verified that the Notification Job in Scheduled Jobs is enabled and your SMTP options in Site Settings are configured or else you will lock yourself out." ResourceKey="TwoFactor">Two Factor?</Label>
|
||||
<div class="col-sm-9">
|
||||
<select id="twofactor" class="form-select" @bind="@_twofactor">
|
||||
<option value="false">@Localizer["Disabled"]</option>
|
||||
<option value="true">@Localizer["Optional"]</option>
|
||||
<option value="required">@Localizer["Required"]</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="cookiename" HelpText="You can choose to use a custom authentication cookie name for each site. However please be aware that if you want to share an authentication cookie between sites on the same domain they need to use a consistent cookie name. Also be aware that changing the authentication cookie name will logout all current users." ResourceKey="CookieName">Cookie Name:</Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="cookiename" class="form-control" @bind="@_cookiename" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="cookieexpiration" HelpText="You can choose to use a custom authentication cookie expiration timespan for each site (e.g. '08:00:00' for 8 hours). The default is 14 days if not specified." ResourceKey="CookieExpiration">Cookie Expiration Timespan:</Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="cookieexpiration" class="form-control" @bind="@_cookieexpiration" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<div class="col-sm-9">
|
||||
<input id="cookieexpiration" class="form-control" @bind="@_cookieexpiration" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="alwaysremember" HelpText="Enabling this option will set a permanent cookie in conjunction with the Cookie Expiration Timespan, which will automatically sign in users the next time they visit the site. By default the site will use session cookies." ResourceKey="AlwaysRemember">Always Remember User?</Label>
|
||||
<div class="col-sm-9">
|
||||
<div class="col-sm-9">
|
||||
<select id="alwaysremember" class="form-select" @bind="@_alwaysremember">
|
||||
<option value="true">@SharedLocalizer["Yes"]</option>
|
||||
<option value="false">@SharedLocalizer["No"]</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
</Section>
|
||||
@if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host))
|
||||
{
|
||||
<Section Name="Password" Heading="Password Settings" ResourceKey="PasswordSettings">
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="minimumlength" HelpText="The Minimum Length For A Password" ResourceKey="RequiredLength">Minimum Length:</Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="minimumlength" class="form-control" @bind="@_minimumlength" required />
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="uniquecharacters" HelpText="The Minimum Number Of Unique Characters Which A Password Must Contain" ResourceKey="UniqueCharacters">Unique Characters:</Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="uniquecharacters" class="form-control" @bind="@_uniquecharacters" required />
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="requiredigit" HelpText="Indicate If Passwords Must Contain A Digit" ResourceKey="RequireDigit">Require Digit?</Label>
|
||||
<div class="col-sm-9">
|
||||
<select id="requiredigit" class="form-select" @bind="@_requiredigit" required>
|
||||
<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="requireupper" HelpText="Indicate If Passwords Must Contain An Upper Case Character" ResourceKey="RequireUpper">Require Uppercase?</Label>
|
||||
<div class="col-sm-9">
|
||||
<select id="requireupper" class="form-select" @bind="@_requireupper" required>
|
||||
<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="requirelower" HelpText="Indicate If Passwords Must Contain A Lower Case Character" ResourceKey="RequireLower">Require Lowercase?</Label>
|
||||
<div class="col-sm-9">
|
||||
<select id="requirelower" class="form-select" @bind="@_requirelower" required>
|
||||
<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="requirepunctuation" HelpText="Indicate if Passwords Must Contain A Non-alphanumeric Character (ie. Punctuation)" ResourceKey="RequirePunctuation">Require Punctuation?</Label>
|
||||
<div class="col-sm-9">
|
||||
<select id="requirepunctuation" class="form-select" @bind="@_requirepunctuation" required>
|
||||
<option value="true">@SharedLocalizer["Yes"]</option>
|
||||
<option value="false">@SharedLocalizer["No"]</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</Section>
|
||||
<Section Name="Lockout" Heading="Lockout Settings" ResourceKey="LockoutSettings">
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="maximum" HelpText="The Maximum Number Of Sign In Attempts Before A User Is Locked Out" ResourceKey="MaximumFailures">Maximum Failures:</Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="maximum" class="form-control" @bind="@_maximumfailures" required />
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="lockoutduration" HelpText="The Number Of Minutes A User Should Be Locked Out" ResourceKey="LockoutDuration">Lockout Duration:</Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="lockoutduration" class="form-control" @bind="@_lockoutduration" required />
|
||||
</div>
|
||||
</div>
|
||||
</Section>
|
||||
<Section Name="ExternalLogin" Heading="External Login Settings" ResourceKey="ExternalLoginSettings">
|
||||
<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="logouteverywhere" HelpText="Do you want users to be logged out of every active session on any device, or only their current session?" ResourceKey="LogoutEverywhere">Logout Everywhere?</Label>
|
||||
<div class="col-sm-9">
|
||||
<select id="logouteverywhere" class="form-select" @bind="@_logouteverywhere">
|
||||
<option value="true">@SharedLocalizer["Yes"]</option>
|
||||
<option value="false">@SharedLocalizer["No"]</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
</Section>
|
||||
@if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host))
|
||||
{
|
||||
<Section Name="Password" Heading="Password Settings" ResourceKey="PasswordSettings">
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="minimumlength" HelpText="The Minimum Length For A Password" ResourceKey="RequiredLength">Minimum Length:</Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="minimumlength" class="form-control" @bind="@_minimumlength" required />
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="uniquecharacters" HelpText="The Minimum Number Of Unique Characters Which A Password Must Contain" ResourceKey="UniqueCharacters">Unique Characters:</Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="uniquecharacters" class="form-control" @bind="@_uniquecharacters" required />
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="requiredigit" HelpText="Indicate If Passwords Must Contain A Digit" ResourceKey="RequireDigit">Require Digit?</Label>
|
||||
<div class="col-sm-9">
|
||||
<select id="requiredigit" class="form-select" @bind="@_requiredigit" required>
|
||||
<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="requireupper" HelpText="Indicate If Passwords Must Contain An Upper Case Character" ResourceKey="RequireUpper">Require Uppercase?</Label>
|
||||
<div class="col-sm-9">
|
||||
<select id="requireupper" class="form-select" @bind="@_requireupper" required>
|
||||
<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="requirelower" HelpText="Indicate If Passwords Must Contain A Lower Case Character" ResourceKey="RequireLower">Require Lowercase?</Label>
|
||||
<div class="col-sm-9">
|
||||
<select id="requirelower" class="form-select" @bind="@_requirelower" required>
|
||||
<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="requirepunctuation" HelpText="Indicate if Passwords Must Contain A Non-alphanumeric Character (ie. Punctuation)" ResourceKey="RequirePunctuation">Require Punctuation?</Label>
|
||||
<div class="col-sm-9">
|
||||
<select id="requirepunctuation" class="form-select" @bind="@_requirepunctuation" required>
|
||||
<option value="true">@SharedLocalizer["Yes"]</option>
|
||||
<option value="false">@SharedLocalizer["No"]</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</Section>
|
||||
<Section Name="Lockout" Heading="Lockout Settings" ResourceKey="LockoutSettings">
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="maximum" HelpText="The Maximum Number Of Sign In Attempts Before A User Is Locked Out" ResourceKey="MaximumFailures">Maximum Failures:</Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="maximum" class="form-control" @bind="@_maximumfailures" required />
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="lockoutduration" HelpText="The Number Of Minutes A User Should Be Locked Out" ResourceKey="LockoutDuration">Lockout Duration:</Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="lockoutduration" class="form-control" @bind="@_lockoutduration" required />
|
||||
</div>
|
||||
</div>
|
||||
</Section>
|
||||
<Section Name="ExternalLogin" Heading="External Login Settings" ResourceKey="ExternalLoginSettings">
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="provider" HelpText="Select the external login provider" ResourceKey="Provider">Provider:</Label>
|
||||
<div class="col-sm-9">
|
||||
@ -201,77 +210,77 @@ else
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="providertype" HelpText="Select the external login provider type" ResourceKey="ProviderType">Provider Type:</Label>
|
||||
<div class="col-sm-9">
|
||||
<select id="providertype" class="form-select" value="@_providertype" @onchange="(e => ProviderTypeChanged(e))">
|
||||
<Label Class="col-sm-3" For="providertype" HelpText="Select the external login provider type" ResourceKey="ProviderType">Provider Type:</Label>
|
||||
<div class="col-sm-9">
|
||||
<select id="providertype" class="form-select" value="@_providertype" @onchange="(e => ProviderTypeChanged(e))">
|
||||
<option value="" selected><@Localizer["Not Specified"]></option>
|
||||
<option value="@AuthenticationProviderTypes.OpenIDConnect">@Localizer["OIDC"]</option>
|
||||
<option value="@AuthenticationProviderTypes.OAuth2">@Localizer["OAuth2"]</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
@if (_providertype != "")
|
||||
{
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="providername" HelpText="Specify a friendly name for the external login provider which will be displayed on the Login page" ResourceKey="ProviderName">Provider Name:</Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="providername" class="form-control" @bind="@_providername" />
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
@if (_providertype == AuthenticationProviderTypes.OpenIDConnect)
|
||||
{
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="authority" HelpText="The Authority Url or Issuer Url associated with the OpenID Connect provider" ResourceKey="Authority">Authority:</Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="authority" class="form-control" @bind="@_authority" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="metadataurl" HelpText="The discovery endpoint for obtaining metadata for this provider. Only specify if the OpenID Connect provider does not use the standard approach (ie. /.well-known/openid-configuration)" ResourceKey="MetadataUrl">Metadata Url:</Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="metadataurl" class="form-control" @bind="@_metadataurl" />
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
@if (_providertype == AuthenticationProviderTypes.OAuth2)
|
||||
{
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="authorizationurl" HelpText="The endpoint for obtaining an Authorization Code" ResourceKey="AuthorizationUrl">Authorization Url:</Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="authorizationurl" class="form-control" @bind="@_authorizationurl" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="tokenurl" HelpText="The endpoint for obtaining an Auth Token" ResourceKey="TokenUrl">Token Url:</Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="tokenurl" class="form-control" @bind="@_tokenurl" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="userinfourl" HelpText="The endpoint for obtaining user information. This should be an API or Page Url which contains the users email address." ResourceKey="UserInfoUrl">User Info Url:</Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="userinfourl" class="form-control" @bind="@_userinfourl" />
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
@if (_providertype != "")
|
||||
{
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="clientid" HelpText="The Client ID from the provider" ResourceKey="ClientID">Client ID:</Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="clientid" class="form-control" @bind="@_clientid" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="clientsecret" HelpText="The Client Secret from the provider" ResourceKey="ClientSecret">Client Secret:</Label>
|
||||
<div class="col-sm-9">
|
||||
<div class="input-group">
|
||||
<input type="@_clientsecrettype" id="clientsecret" class="form-control" @bind="@_clientsecret" />
|
||||
<button type="button" class="btn btn-secondary" @onclick="@ToggleClientSecret">@_toggleclientsecret</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<option value="@AuthenticationProviderTypes.OpenIDConnect">@Localizer["OIDC"]</option>
|
||||
<option value="@AuthenticationProviderTypes.OAuth2">@Localizer["OAuth2"]</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
@if (_providertype != "")
|
||||
{
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="providername" HelpText="Specify a friendly name for the external login provider which will be displayed on the Login page" ResourceKey="ProviderName">Provider Name:</Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="providername" class="form-control" @bind="@_providername" />
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
@if (_providertype == AuthenticationProviderTypes.OpenIDConnect)
|
||||
{
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="authority" HelpText="The Authority Url or Issuer Url associated with the OpenID Connect provider" ResourceKey="Authority">Authority:</Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="authority" class="form-control" @bind="@_authority" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="metadataurl" HelpText="The discovery endpoint for obtaining metadata for this provider. Only specify if the OpenID Connect provider does not use the standard approach (ie. /.well-known/openid-configuration)" ResourceKey="MetadataUrl">Metadata Url:</Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="metadataurl" class="form-control" @bind="@_metadataurl" />
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
@if (_providertype == AuthenticationProviderTypes.OAuth2)
|
||||
{
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="authorizationurl" HelpText="The endpoint for obtaining an Authorization Code" ResourceKey="AuthorizationUrl">Authorization Url:</Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="authorizationurl" class="form-control" @bind="@_authorizationurl" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="tokenurl" HelpText="The endpoint for obtaining an Auth Token" ResourceKey="TokenUrl">Token Url:</Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="tokenurl" class="form-control" @bind="@_tokenurl" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="userinfourl" HelpText="The endpoint for obtaining user information. This should be an API or Page Url which contains the users email address." ResourceKey="UserInfoUrl">User Info Url:</Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="userinfourl" class="form-control" @bind="@_userinfourl" />
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
@if (_providertype != "")
|
||||
{
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="clientid" HelpText="The Client ID from the provider" ResourceKey="ClientID">Client ID:</Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="clientid" class="form-control" @bind="@_clientid" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="clientsecret" HelpText="The Client Secret from the provider" ResourceKey="ClientSecret">Client Secret:</Label>
|
||||
<div class="col-sm-9">
|
||||
<div class="input-group">
|
||||
<input type="@_clientsecrettype" id="clientsecret" class="form-control" @bind="@_clientsecret" />
|
||||
<button type="button" class="btn btn-secondary" @onclick="@ToggleClientSecret">@_toggleclientsecret</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@if (_providertype == AuthenticationProviderTypes.OpenIDConnect)
|
||||
{
|
||||
<div class="row mb-1 align-items-center">
|
||||
@ -291,32 +300,32 @@ else
|
||||
</div>
|
||||
}
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="scopes" HelpText="A list of Scopes to request from the provider (separated by commas). If none are specified, standard Scopes will be used by default." ResourceKey="Scopes">Scopes:</Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="scopes" class="form-control" @bind="@_scopes" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="parameters" HelpText="Optionally specify any additional parameters as name/value pairs to send to the provider (separated by commas if there are multiple)." ResourceKey="Parameters">Parameters:</Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="parameters" class="form-control" @bind="@_parameters" />
|
||||
</div>
|
||||
</div>
|
||||
<Label Class="col-sm-3" For="scopes" HelpText="A list of Scopes to request from the provider (separated by commas). If none are specified, standard Scopes will be used by default." ResourceKey="Scopes">Scopes:</Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="scopes" class="form-control" @bind="@_scopes" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="pkce" HelpText="Indicate if the provider supports Proof Key for Code Exchange (PKCE)" ResourceKey="PKCE">Use PKCE?</Label>
|
||||
<div class="col-sm-9">
|
||||
<select id="pkce" class="form-select" @bind="@_pkce" required>
|
||||
<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="redirecturl" HelpText="The Redirect Url (or Callback Url) which usually needs to be registered with the provider" ResourceKey="RedirectUrl">Redirect Url:</Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="redirecturl" class="form-control" @bind="@_redirecturl" readonly />
|
||||
</div>
|
||||
</div>
|
||||
<Label Class="col-sm-3" For="parameters" HelpText="Optionally specify any additional parameters as name/value pairs to send to the provider (separated by commas if there are multiple)." ResourceKey="Parameters">Parameters:</Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="parameters" class="form-control" @bind="@_parameters" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="pkce" HelpText="Indicate if the provider supports Proof Key for Code Exchange (PKCE)" ResourceKey="PKCE">Use PKCE?</Label>
|
||||
<div class="col-sm-9">
|
||||
<select id="pkce" class="form-select" @bind="@_pkce" required>
|
||||
<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="redirecturl" HelpText="The Redirect Url (or Callback Url) which usually needs to be registered with the provider" ResourceKey="RedirectUrl">Redirect Url:</Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="redirecturl" class="form-control" @bind="@_redirecturl" readonly />
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="reviewclaims" HelpText="This option will record the full list of Claims returned by the Provider in the Event Log. It should only be used for testing purposes. External Login will be restricted when this option is enabled." ResourceKey="ReviewClaims">Review Claims?</Label>
|
||||
<div class="col-sm-9">
|
||||
@ -334,10 +343,10 @@ else
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="identifierclaimtype" HelpText="Specify the type name of the unique user identifier claim provided by the provider. The default value is 'sub'." ResourceKey="IdentifierClaimType">Identifier Claim:</Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="identifierclaimtype" class="form-control" @bind="@_identifierclaimtype" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-9">
|
||||
<input id="identifierclaimtype" class="form-control" @bind="@_identifierclaimtype" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="nameclaimtype" HelpText="Optionally specify the type name of the user's name claim provided by the provider. The typical value is 'name'." ResourceKey="NameClaimType">Name Claim:</Label>
|
||||
<div class="col-sm-9">
|
||||
@ -346,16 +355,16 @@ else
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="emailclaimtype" HelpText="Optionally specify the type name of the email address claim provided by the provider. The typical value is 'email'," ResourceKey="EmailClaimType">Email Claim:</Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="emailclaimtype" class="form-control" @bind="@_emailclaimtype" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="roleclaimtype" HelpText="The name of the roles claim provided by the provider" ResourceKey="RoleClaimType">Roles Claim:</Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="roleclaimtype" class="form-control" @bind="@_roleclaimtype" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-9">
|
||||
<input id="emailclaimtype" class="form-control" @bind="@_emailclaimtype" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="roleclaimtype" HelpText="The name of the roles claim provided by the provider" ResourceKey="RoleClaimType">Roles Claim:</Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="roleclaimtype" class="form-control" @bind="@_roleclaimtype" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="roleclaimmappings" HelpText="Optionally provide a comma delimited list of role names provided by the identity provider, as well as mappings to your site roles." ResourceKey="RoleClaimMappings">Role Claim Mappings:</Label>
|
||||
<div class="col-sm-9">
|
||||
@ -374,11 +383,11 @@ else
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="profileclaimtypes" HelpText="A comma delimited list of user profile claims provided by the provider, as well as mappings to your user profile definition. For example if the provider includes a 'given_name' claim and you have a 'FirstName' user profile definition you should specify 'given_name:FirstName'." ResourceKey="ProfileClaimTypes">User Profile Claims:</Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="profileclaimtypes" class="form-control" @bind="@_profileclaimtypes" />
|
||||
</div>
|
||||
</div>
|
||||
<Label Class="col-sm-3" For="profileclaimtypes" HelpText="A comma delimited list of user profile claims provided by the provider, as well as mappings to your user profile definition. For example if the provider includes a 'given_name' claim and you have a 'FirstName' user profile definition you should specify 'given_name:FirstName'." ResourceKey="ProfileClaimTypes">User Profile Claims:</Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="profileclaimtypes" class="form-control" @bind="@_profileclaimtypes" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="savetokens" HelpText="Specify whether access and refresh tokens should be saved after a successful login. The default is false to reduce the size of the authentication cookie." ResourceKey="SaveTokens">Save Tokens?</Label>
|
||||
<div class="col-sm-9">
|
||||
@ -389,20 +398,20 @@ else
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="domainfilter" HelpText="Provide any email domain filter criteria (separated by commas). Domains to exclude should be prefixed with an exclamation point (!). For example 'microsoft.com,!hotmail.com' would include microsoft.com email addresses but not hotmail.com email addresses." ResourceKey="DomainFilter">Domain Filter:</Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="domainfilter" class="form-control" @bind="@_domainfilter" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="createusers" HelpText="Do you want new users to be created automatically? If you disable this option, users must already be registered on the site in order to sign in with their external login." ResourceKey="CreateUsers">Create New Users?</Label>
|
||||
<div class="col-sm-9">
|
||||
<select id="createusers" class="form-select" @bind="@_createusers">
|
||||
<option value="true">@SharedLocalizer["Yes"]</option>
|
||||
<option value="false">@SharedLocalizer["No"]</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<Label Class="col-sm-3" For="domainfilter" HelpText="Provide any email domain filter criteria (separated by commas). Domains to exclude should be prefixed with an exclamation point (!). For example 'microsoft.com,!hotmail.com' would include microsoft.com email addresses but not hotmail.com email addresses." ResourceKey="DomainFilter">Domain Filter:</Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="domainfilter" class="form-control" @bind="@_domainfilter" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="createusers" HelpText="Do you want new users to be created automatically? If you disable this option, users must already be registered on the site in order to sign in with their external login." ResourceKey="CreateUsers">Create New Users?</Label>
|
||||
<div class="col-sm-9">
|
||||
<select id="createusers" class="form-select" @bind="@_createusers">
|
||||
<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="verifyusers" HelpText="Do you want existing users to perform an additional email verification step to link their external login? If you disable this option, existing users will be linked automatically." ResourceKey="VerifyUsers">Verify Existing Users?</Label>
|
||||
<div class="col-sm-9">
|
||||
@ -412,52 +421,64 @@ else
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
@if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host))
|
||||
{
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="allowhostrole" HelpText="Indicate if host roles are supported from the identity provider. Please use caution with this option as it allows the host user to administrate every site within your installation." ResourceKey="AllowHostRole">Allow Host Role?</Label>
|
||||
<div class="col-sm-9">
|
||||
<select id="allowhostrole" class="form-select" @bind="@_allowhostrole" required>
|
||||
<option value="true">@SharedLocalizer["Yes"]</option>
|
||||
<option value="false">@SharedLocalizer["No"]</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
</Section>
|
||||
<Section Name="Token" Heading="Token Settings" ResourceKey="TokenSettings">
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="jwtsecret" HelpText="If you want to want to provide API access, please specify a secret which will be used to encrypt your tokens. The secret should be 16 characters or more to ensure optimal security. Please note that if you change this secret, all existing tokens will become invalid and will need to be regenerated." ResourceKey="Secret">Secret:</Label>
|
||||
<div class="col-sm-9">
|
||||
<div class="input-group">
|
||||
<input type="@_secrettype" id="jwtsecret" class="form-control" @bind="@_secret" />
|
||||
<button type="button" class="btn btn-secondary" @onclick="@ToggleSecret">@_togglesecret</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="issuer" HelpText="Optionally provide the issuer of the token" ResourceKey="Issuer">Issuer:</Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="issuer" class="form-control" @bind="@_issuer" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="audience" HelpText="Optionally provide the audience for the token" ResourceKey="Audience">Audience:</Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="audience" class="form-control" @bind="@_audience" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="lifetime" HelpText="The number of minutes for which a token should be valid" ResourceKey="Lifetime">Lifetime:</Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="lifetime" class="form-control" @bind="@_lifetime" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="token" HelpText="Select the Create Token button to generate a long-lived access token (valid for 1 year). Be sure to store this token in a safe location as you will not be able to access it in the future." ResourceKey="Token">Access Token:</Label>
|
||||
<div class="col-sm-9">
|
||||
<div class="input-group">
|
||||
<input id="token" class="form-control" @bind="@_token" />
|
||||
<button type="button" class="btn btn-secondary" @onclick="@CreateToken">@Localizer["CreateToken"]</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Section>
|
||||
}
|
||||
</div>
|
||||
<br />
|
||||
<button type="button" class="btn btn-success" @onclick="SaveSiteSettings">@SharedLocalizer["Save"]</button>
|
||||
</Section>
|
||||
<Section Name="Token" Heading="Token Settings" ResourceKey="TokenSettings">
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="jwtsecret" HelpText="If you want to want to provide API access, please specify a secret which will be used to encrypt your tokens. The secret should be 16 characters or more to ensure optimal security. Please note that if you change this secret, all existing tokens will become invalid and will need to be regenerated." ResourceKey="Secret">Secret:</Label>
|
||||
<div class="col-sm-9">
|
||||
<div class="input-group">
|
||||
<input type="@_secrettype" id="jwtsecret" class="form-control" @bind="@_secret" />
|
||||
<button type="button" class="btn btn-secondary" @onclick="@ToggleSecret">@_togglesecret</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="issuer" HelpText="Optionally provide the issuer of the token" ResourceKey="Issuer">Issuer:</Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="issuer" class="form-control" @bind="@_issuer" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="audience" HelpText="Optionally provide the audience for the token" ResourceKey="Audience">Audience:</Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="audience" class="form-control" @bind="@_audience" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="lifetime" HelpText="The number of minutes for which a token should be valid" ResourceKey="Lifetime">Lifetime:</Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="lifetime" class="form-control" @bind="@_lifetime" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="token" HelpText="Select the Create Token button to generate a long-lived access token (valid for 1 year). Be sure to store this token in a safe location as you will not be able to access it in the future." ResourceKey="Token">Access Token:</Label>
|
||||
<div class="col-sm-9">
|
||||
<div class="input-group">
|
||||
<input id="token" class="form-control" @bind="@_token" />
|
||||
<button type="button" class="btn btn-secondary" @onclick="@CreateToken">@Localizer["CreateToken"]</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Section>
|
||||
}
|
||||
</div>
|
||||
<br />
|
||||
<button type="button" class="btn btn-success" @onclick="SaveSiteSettings">@SharedLocalizer["Save"]</button>
|
||||
</TabPanel>
|
||||
</TabStrip>
|
||||
</TabStrip>
|
||||
}
|
||||
|
||||
@code {
|
||||
@ -469,6 +490,7 @@ else
|
||||
private string _cookiename;
|
||||
private string _cookieexpiration;
|
||||
private string _alwaysremember;
|
||||
private string _logouteverywhere;
|
||||
|
||||
private string _minimumlength;
|
||||
private string _uniquecharacters;
|
||||
@ -510,6 +532,7 @@ else
|
||||
private string _domainfilter;
|
||||
private string _createusers;
|
||||
private string _verifyusers;
|
||||
private string _allowhostrole;
|
||||
|
||||
private string _secret;
|
||||
private string _secrettype = "password";
|
||||
@ -529,7 +552,7 @@ else
|
||||
await LoadUsersAsync(true);
|
||||
|
||||
var settings = await SettingService.GetSiteSettingsAsync(PageState.Site.SiteId);
|
||||
_allowregistration = PageState.Site.AllowRegistration.ToString();
|
||||
_allowregistration = PageState.Site.AllowRegistration.ToString().ToLower();
|
||||
_allowsitelogin = SettingService.GetSetting(settings, "LoginOptions:AllowSiteLogin", "true");
|
||||
|
||||
if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host))
|
||||
@ -538,6 +561,7 @@ else
|
||||
_cookiename = SettingService.GetSetting(settings, "LoginOptions:CookieName", ".AspNetCore.Identity.Application");
|
||||
_cookieexpiration = SettingService.GetSetting(settings, "LoginOptions:CookieExpiration", "");
|
||||
_alwaysremember = SettingService.GetSetting(settings, "LoginOptions:AlwaysRemember", "false");
|
||||
_logouteverywhere = SettingService.GetSetting(settings, "LoginOptions:LogoutEverywhere", "false");
|
||||
|
||||
_minimumlength = SettingService.GetSetting(settings, "IdentityOptions:Password:RequiredLength", "6");
|
||||
_uniquecharacters = SettingService.GetSetting(settings, "IdentityOptions:Password:RequiredUniqueChars", "1");
|
||||
@ -591,6 +615,7 @@ else
|
||||
_domainfilter = SettingService.GetSetting(settings, "ExternalLogin:DomainFilter", "");
|
||||
_createusers = SettingService.GetSetting(settings, "ExternalLogin:CreateUsers", "true");
|
||||
_verifyusers = SettingService.GetSetting(settings, "ExternalLogin:VerifyUsers", "true");
|
||||
_allowhostrole = SettingService.GetSetting(settings, "ExternalLogin:AllowHostRole", "false");
|
||||
}
|
||||
|
||||
private async Task LoadUsersAsync(bool load)
|
||||
@ -656,6 +681,7 @@ else
|
||||
settings = SettingService.SetSetting(settings, "LoginOptions:CookieName", _cookiename, true);
|
||||
settings = SettingService.SetSetting(settings, "LoginOptions:CookieExpiration", _cookieexpiration, true);
|
||||
settings = SettingService.SetSetting(settings, "LoginOptions:AlwaysRemember", _alwaysremember, false);
|
||||
settings = SettingService.SetSetting(settings, "LoginOptions:LogoutEverywhere", _logouteverywhere, false);
|
||||
|
||||
settings = SettingService.SetSetting(settings, "IdentityOptions:Password:RequiredLength", _minimumlength, true);
|
||||
settings = SettingService.SetSetting(settings, "IdentityOptions:Password:RequiredUniqueChars", _uniquecharacters, true);
|
||||
@ -693,6 +719,7 @@ else
|
||||
settings = SettingService.SetSetting(settings, "ExternalLogin:DomainFilter", _domainfilter, true);
|
||||
settings = SettingService.SetSetting(settings, "ExternalLogin:CreateUsers", _createusers, true);
|
||||
settings = SettingService.SetSetting(settings, "ExternalLogin:VerifyUsers", _verifyusers, true);
|
||||
settings = SettingService.SetSetting(settings, "ExternalLogin:AllowHostRole", _allowhostrole, true);
|
||||
|
||||
settings = SettingService.SetSetting(settings, "JwtOptions:Secret", _secret, true);
|
||||
settings = SettingService.SetSetting(settings, "JwtOptions:Issuer", _issuer, true);
|
||||
|
@ -100,6 +100,18 @@ else
|
||||
<br />
|
||||
<button type="button" class="btn btn-success" @onclick="SaveSiteSettings">@SharedLocalizer["Save"]</button>
|
||||
</TabPanel>
|
||||
<TabPanel Name="Robots" Heading="Robots.txt" ResourceKey="Robots">
|
||||
<div class="container">
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="robots" HelpText="Specify your robots.txt instructions to provide bots with guidance on which parts of your site should be indexed" ResourceKey="Robots">Instructions: </Label>
|
||||
<div class="col-sm-9">
|
||||
<textarea id="robots" class="form-control" @bind="@_robots" rows="3"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<br />
|
||||
<button type="button" class="btn btn-success" @onclick="SaveSiteSettings">@SharedLocalizer["Save"]</button>
|
||||
</TabPanel>
|
||||
</TabStrip>
|
||||
}
|
||||
|
||||
@ -113,6 +125,7 @@ else
|
||||
private string _filter = "";
|
||||
private int _retention = 30;
|
||||
private string _correlation = "true";
|
||||
private string _robots = "";
|
||||
|
||||
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Admin;
|
||||
|
||||
@ -139,7 +152,8 @@ else
|
||||
_filter = SettingService.GetSetting(settings, "VisitorFilter", Constants.DefaultVisitorFilter);
|
||||
_retention = int.Parse(SettingService.GetSetting(settings, "VisitorRetention", "30"));
|
||||
_correlation = SettingService.GetSetting(settings, "VisitorCorrelation", "true");
|
||||
}
|
||||
_robots = SettingService.GetSetting(settings, "Robots", "");
|
||||
}
|
||||
|
||||
private async void TypeChanged(ChangeEventArgs e)
|
||||
{
|
||||
@ -191,6 +205,7 @@ else
|
||||
settings = SettingService.SetSetting(settings, "VisitorFilter", _filter, true);
|
||||
settings = SettingService.SetSetting(settings, "VisitorRetention", _retention.ToString(), true);
|
||||
settings = SettingService.SetSetting(settings, "VisitorCorrelation", _correlation, true);
|
||||
settings = SettingService.SetSetting(settings, "Robots", _robots, true);
|
||||
await SettingService.UpdateSiteSettingsAsync(settings, PageState.Site.SiteId);
|
||||
|
||||
AddModuleMessage(Localizer["Success.SaveSiteSettings"], MessageType.Success);
|
||||
|
@ -163,6 +163,13 @@
|
||||
[Parameter]
|
||||
public EventCallback<int> OnUpload { get; set; } // optional - executes a method in the calling component when a file is uploaded
|
||||
|
||||
[Parameter]
|
||||
public EventCallback<int> OnSelectFolder { get; set; } // optional - executes a method in the calling component when a folder is selected
|
||||
|
||||
[Parameter]
|
||||
public EventCallback<int> OnSelectFile { get; set; } // optional - executes a method in the calling component when a file is selected
|
||||
|
||||
[Obsolete("Use OnSelectFile instead.")]
|
||||
[Parameter]
|
||||
public EventCallback<int> OnSelect { get; set; } // optional - executes a method in the calling component when a file is selected
|
||||
|
||||
@ -300,6 +307,8 @@
|
||||
FileId = -1;
|
||||
_file = null;
|
||||
_image = string.Empty;
|
||||
|
||||
await OnSelectFolder.InvokeAsync(FolderId);
|
||||
StateHasChanged();
|
||||
}
|
||||
catch (Exception ex)
|
||||
@ -315,7 +324,10 @@
|
||||
_message = string.Empty;
|
||||
FileId = int.Parse((string)e.Value);
|
||||
await SetImage();
|
||||
#pragma warning disable CS0618
|
||||
await OnSelect.InvokeAsync(FileId);
|
||||
#pragma warning restore CS0618
|
||||
await OnSelectFile.InvokeAsync(FileId);
|
||||
StateHasChanged();
|
||||
}
|
||||
|
||||
@ -438,7 +450,10 @@
|
||||
{
|
||||
FileId = file.FileId;
|
||||
await SetImage();
|
||||
#pragma warning disable CS0618
|
||||
await OnSelect.InvokeAsync(FileId);
|
||||
#pragma warning restore CS0618
|
||||
await OnSelectFile.InvokeAsync(FileId);
|
||||
await OnUpload.InvokeAsync(FileId);
|
||||
}
|
||||
await GetFiles();
|
||||
@ -489,7 +504,10 @@
|
||||
await GetFiles();
|
||||
FileId = -1;
|
||||
await SetImage();
|
||||
#pragma warning disable CS0618
|
||||
await OnSelect.InvokeAsync(FileId);
|
||||
#pragma warning restore CS0618
|
||||
await OnSelectFile.InvokeAsync(FileId);
|
||||
StateHasChanged();
|
||||
}
|
||||
catch (Exception ex)
|
||||
|
@ -60,7 +60,7 @@
|
||||
@foreach (User user in _users)
|
||||
{
|
||||
<tr>
|
||||
<td>@user.DisplayName</td>
|
||||
<td>@user.DisplayName (@user.Username)</td>
|
||||
@foreach (var permissionname in _permissionnames)
|
||||
{
|
||||
<td style="text-align: center; width: 1px;">
|
||||
@ -270,8 +270,8 @@
|
||||
private async Task<Dictionary<string, string>> GetUsers(string filter)
|
||||
{
|
||||
var users = await UserRoleService.GetUserRolesAsync(PageState.Site.SiteId, RoleNames.Registered);
|
||||
return users.Where(item => item.User.DisplayName.Contains(filter, StringComparison.OrdinalIgnoreCase))
|
||||
.ToDictionary(item => item.UserId.ToString(), item => item.User.DisplayName);
|
||||
return users.Where(item => item.User.DisplayName.Contains(filter, StringComparison.OrdinalIgnoreCase) || item.User.Username.Contains(filter, StringComparison.OrdinalIgnoreCase))
|
||||
.ToDictionary(item => item.UserId.ToString(), item => item.User.DisplayName + " (" + item.User.Username + ")");
|
||||
}
|
||||
|
||||
private async Task AddUser()
|
||||
|
@ -2,6 +2,7 @@
|
||||
@namespace Oqtane.Modules.HtmlText
|
||||
@inherits ModuleBase
|
||||
@inject IHtmlTextService HtmlTextService
|
||||
@inject ISettingService SettingService
|
||||
@inject IStringLocalizer<Index> Localizer
|
||||
|
||||
@if (PageState.EditMode)
|
||||
@ -36,6 +37,10 @@
|
||||
{
|
||||
content = htmltext.Content;
|
||||
content = Utilities.FormatContent(content, PageState.Alias, "render");
|
||||
if (bool.Parse(SettingService.GetSetting(ModuleState.Settings, "DynamicTokens", "false")))
|
||||
{
|
||||
content = ReplaceTokens(content);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -15,7 +15,7 @@ namespace Oqtane.Modules.HtmlText
|
||||
Version = "1.0.1",
|
||||
ServerManagerType = "Oqtane.Modules.HtmlText.Manager.HtmlTextManager, Oqtane.Server",
|
||||
ReleaseVersions = "1.0.0,1.0.1",
|
||||
SettingsType = string.Empty,
|
||||
SettingsType = "Oqtane.Modules.HtmlText.Settings, Oqtane.Client",
|
||||
Resources = new List<Resource>()
|
||||
{
|
||||
new Resource { ResourceType = ResourceType.Stylesheet, Url = "~/Module.css" }
|
||||
|
55
Oqtane.Client/Modules/HtmlText/Settings.razor
Normal file
55
Oqtane.Client/Modules/HtmlText/Settings.razor
Normal file
@ -0,0 +1,55 @@
|
||||
@namespace Oqtane.Modules.HtmlText
|
||||
@inherits ModuleBase
|
||||
@inject ISettingService SettingService
|
||||
@implements Oqtane.Interfaces.ISettingsControl
|
||||
@inject IStringLocalizer<Settings> Localizer
|
||||
@inject IStringLocalizer<SharedResources> SharedLocalizer
|
||||
|
||||
<form @ref="form" class="@(validated ? "was-validated" : "needs-validation")" novalidate>
|
||||
<div class="container">
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="dynamictokens" ResourceKey="DynamicTokens" ResourceType="@resourceType" HelpText="Do you wish to allow tokens to be dynamically replaced? Please note that this will affect the performance of your site.">Dynamic Tokens? </Label>
|
||||
<div class="col-sm-9">
|
||||
<select id="dynamictokens" class="form-select" @bind="@_dynamictokens">
|
||||
<option value="true">@SharedLocalizer["Yes"]</option>
|
||||
<option value="false">@SharedLocalizer["No"]</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
@code {
|
||||
private string resourceType = "Oqtane.Modules.HtmlText.Settings, Oqtane.Client"; // for localization
|
||||
|
||||
private ElementReference form;
|
||||
private bool validated = false;
|
||||
|
||||
private string _dynamictokens;
|
||||
|
||||
protected override void OnInitialized()
|
||||
{
|
||||
try
|
||||
{
|
||||
_dynamictokens = SettingService.GetSetting(ModuleState.Settings, "DynamicTokens", "false");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
AddModuleMessage(ex.Message, MessageType.Error);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task UpdateSettings()
|
||||
{
|
||||
try
|
||||
{
|
||||
var settings = await SettingService.GetModuleSettingsAsync(ModuleState.ModuleId);
|
||||
settings = SettingService.SetSetting(settings, "DynamicTokens", _dynamictokens);
|
||||
await SettingService.UpdateModuleSettingsAsync(settings, ModuleState.ModuleId);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
AddModuleMessage(ex.Message, MessageType.Error);
|
||||
}
|
||||
}
|
||||
}
|
@ -10,6 +10,7 @@ using System.Collections.Generic;
|
||||
using Microsoft.JSInterop;
|
||||
using System.Linq;
|
||||
using System.Dynamic;
|
||||
using System.Reflection;
|
||||
|
||||
namespace Oqtane.Modules
|
||||
{
|
||||
@ -35,7 +36,7 @@ namespace Oqtane.Modules
|
||||
protected PageState PageState { get; set; }
|
||||
|
||||
[CascadingParameter]
|
||||
protected Module ModuleState { get; set; }
|
||||
protected Models.Module ModuleState { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public RenderModeBoundary RenderModeBoundary { get; set; }
|
||||
@ -413,6 +414,79 @@ namespace Oqtane.Modules
|
||||
await interop.ScrollTo(0, 0, "smooth");
|
||||
}
|
||||
|
||||
public string ReplaceTokens(string content)
|
||||
{
|
||||
return ReplaceTokens(content, null);
|
||||
}
|
||||
|
||||
public string ReplaceTokens(string content, object obj)
|
||||
{
|
||||
var tokens = new List<string>();
|
||||
var pos = content.IndexOf("[");
|
||||
if (pos != -1)
|
||||
{
|
||||
if (content.IndexOf("]", pos) != -1)
|
||||
{
|
||||
var token = content.Substring(pos, content.IndexOf("]", pos) - pos + 1);
|
||||
if (token.Contains(":"))
|
||||
{
|
||||
tokens.Add(token.Substring(1, token.Length - 2));
|
||||
}
|
||||
}
|
||||
pos = content.IndexOf("[", pos + 1);
|
||||
}
|
||||
if (tokens.Count != 0)
|
||||
{
|
||||
foreach (string token in tokens)
|
||||
{
|
||||
var segments = token.Split(":");
|
||||
if (segments.Length >= 2 && segments.Length <= 3)
|
||||
{
|
||||
var objectName = string.Join(":", segments, 0, segments.Length - 1);
|
||||
var propertyName = segments[segments.Length - 1];
|
||||
var propertyValue = "";
|
||||
|
||||
switch (objectName)
|
||||
{
|
||||
case "ModuleState":
|
||||
propertyValue = ModuleState.GetType().GetProperty(propertyName)?.GetValue(ModuleState, null).ToString();
|
||||
break;
|
||||
case "PageState":
|
||||
propertyValue = PageState.GetType().GetProperty(propertyName)?.GetValue(PageState, null).ToString();
|
||||
break;
|
||||
case "PageState:Alias":
|
||||
propertyValue = PageState.Alias.GetType().GetProperty(propertyName)?.GetValue(PageState.Alias, null).ToString();
|
||||
break;
|
||||
case "PageState:Site":
|
||||
propertyValue = PageState.Site.GetType().GetProperty(propertyName)?.GetValue(PageState.Site, null).ToString();
|
||||
break;
|
||||
case "PageState:Page":
|
||||
propertyValue = PageState.Page.GetType().GetProperty(propertyName)?.GetValue(PageState.Page, null).ToString();
|
||||
break;
|
||||
case "PageState:User":
|
||||
propertyValue = PageState.User?.GetType().GetProperty(propertyName)?.GetValue(PageState.User, null).ToString();
|
||||
break;
|
||||
case "PageState:Route":
|
||||
propertyValue = PageState.Route.GetType().GetProperty(propertyName)?.GetValue(PageState.Route, null).ToString();
|
||||
break;
|
||||
default:
|
||||
if (obj != null && obj.GetType().Name == objectName)
|
||||
{
|
||||
propertyValue = obj.GetType().GetProperty(propertyName)?.GetValue(obj, null).ToString();
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (propertyValue != null)
|
||||
{
|
||||
content = content.Replace("[" + token + "]", propertyValue);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
return content;
|
||||
}
|
||||
|
||||
// logging methods
|
||||
public async Task Log(Alias alias, LogLevel level, string function, Exception exception, string message, params object[] args)
|
||||
{
|
||||
|
@ -4,7 +4,7 @@
|
||||
<TargetFramework>net9.0</TargetFramework>
|
||||
<OutputType>Exe</OutputType>
|
||||
<Configurations>Debug;Release</Configurations>
|
||||
<Version>6.1.0</Version>
|
||||
<Version>6.1.2</Version>
|
||||
<Product>Oqtane</Product>
|
||||
<Authors>Shaun Walker</Authors>
|
||||
<Company>.NET Foundation</Company>
|
||||
@ -12,7 +12,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/v6.1.0</PackageReleaseNotes>
|
||||
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v6.1.2</PackageReleaseNotes>
|
||||
<RepositoryUrl>https://github.com/oqtane/oqtane.framework</RepositoryUrl>
|
||||
<RepositoryType>Git</RepositoryType>
|
||||
<RootNamespace>Oqtane</RootNamespace>
|
||||
@ -22,16 +22,22 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="9.0.1" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Authentication" Version="9.0.1" />
|
||||
<PackageReference Include="Microsoft.Extensions.Localization" Version="9.0.1" />
|
||||
<PackageReference Include="Microsoft.Extensions.Http" Version="9.0.1" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="9.0.4" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Authentication" Version="9.0.4" />
|
||||
<PackageReference Include="Microsoft.Extensions.Localization" Version="9.0.4" />
|
||||
<PackageReference Include="Microsoft.Extensions.Http" Version="9.0.4" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Oqtane.Shared\Oqtane.Shared.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Content Update="Installer\Controls\AzureSqlConfig.razor">
|
||||
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
|
||||
</Content>
|
||||
</ItemGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<PublishTrimmed>false</PublishTrimmed>
|
||||
<BlazorEnableCompression>false</BlazorEnableCompression>
|
||||
|
@ -121,7 +121,7 @@
|
||||
<value>Server:</value>
|
||||
</data>
|
||||
<data name="Server.HelpText" xml:space="preserve">
|
||||
<value>Enter the database server name. This might include a port number as well if you are using a cloud service (ie. servername.database.windows.net,1433) </value>
|
||||
<value>Enter the database server name. This might include a port number as well if you are using a cloud service.</value>
|
||||
</data>
|
||||
<data name="Database.Text" xml:space="preserve">
|
||||
<value>Database:</value>
|
||||
|
@ -180,9 +180,6 @@
|
||||
<data name="Once" xml:space="preserve">
|
||||
<value>Execute Once</value>
|
||||
</data>
|
||||
<data name="Message.NoJobs" xml:space="preserve">
|
||||
<value>Please Note That After An Initial Installation You Must <a href={0}>Restart</a> The Application In Order To Activate The Default Scheduled Jobs.</value>
|
||||
</data>
|
||||
<data name="Refresh.Text" xml:space="preserve">
|
||||
<value>Refresh</value>
|
||||
</data>
|
||||
|
@ -147,8 +147,8 @@
|
||||
<data name="Owner.HelpText" xml:space="preserve">
|
||||
<value>The owner or creator of the module</value>
|
||||
</data>
|
||||
<data name="ReferenceUrl.HelpText" xml:space="preserve">
|
||||
<value>The reference url of the module</value>
|
||||
<data name="Url.HelpText" xml:space="preserve">
|
||||
<value>The url of the module</value>
|
||||
</data>
|
||||
<data name="Contact.HelpText" xml:space="preserve">
|
||||
<value>The contact for the module</value>
|
||||
@ -171,8 +171,8 @@
|
||||
<data name="Owner.Text" xml:space="preserve">
|
||||
<value>Owner: </value>
|
||||
</data>
|
||||
<data name="ReferenceUrl.Text" xml:space="preserve">
|
||||
<value>Reference Url: </value>
|
||||
<data name="Url.Text" xml:space="preserve">
|
||||
<value>Url: </value>
|
||||
</data>
|
||||
<data name="Contact.Text" xml:space="preserve">
|
||||
<value>Contact: </value>
|
||||
@ -228,18 +228,6 @@
|
||||
<data name="View License" xml:space="preserve">
|
||||
<value>View License</value>
|
||||
</data>
|
||||
<data name="Error.Validate" xml:space="preserve">
|
||||
<value>Error Validating Package</value>
|
||||
</data>
|
||||
<data name="Message.Download" xml:space="preserve">
|
||||
<value>Package Version Has Been Verified. Please Select The Download Button To Obtain The Package.</value>
|
||||
</data>
|
||||
<data name="Message.Validate" xml:space="preserve">
|
||||
<value>This Package Version Has Not Been Registered In The Oqtane Marketplace Or You Do Not Have The Right To Use It From This Installation</value>
|
||||
</data>
|
||||
<data name="Validate" xml:space="preserve">
|
||||
<value>Validate</value>
|
||||
</data>
|
||||
<data name="Browse" xml:space="preserve">
|
||||
<value>Browse</value>
|
||||
</data>
|
||||
|
@ -159,4 +159,13 @@
|
||||
<data name="Enabled" xml:space="preserve">
|
||||
<value>Enabled?</value>
|
||||
</data>
|
||||
<data name="Synchronize" xml:space="preserve">
|
||||
<value>Synchronize</value>
|
||||
</data>
|
||||
<data name="Success.Module.Synchronize" xml:space="preserve">
|
||||
<value>Modules Have Been Successfully Synchronized With The Marketplace</value>
|
||||
</data>
|
||||
<data name="Error.Module.Synchronize" xml:space="preserve">
|
||||
<value>Error Synchronizing Modules With The Marketplace</value>
|
||||
</data>
|
||||
</root>
|
@ -118,7 +118,7 @@
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<data name="NoCriteria" xml:space="preserve">
|
||||
<value>You Must Provide Some Search Criteria</value>
|
||||
<value>Please Enter Some Search Criteria</value>
|
||||
</data>
|
||||
<data name="NoResult" xml:space="preserve">
|
||||
<value>No Content Matches The Criteria Provided</value>
|
||||
|
@ -349,7 +349,7 @@
|
||||
<value>Relay Configured?</value>
|
||||
</data>
|
||||
<data name="SiteMap.HelpText" xml:space="preserve">
|
||||
<value>The site map url for this site which can be submitted to search engines for indexing</value>
|
||||
<value>The site map url for this site which can be submitted to search engines for indexing. The sitemap is cached for 5 minutes and the cache can be manually cleared.</value>
|
||||
</data>
|
||||
<data name="SiteMap.Text" xml:space="preserve">
|
||||
<value>Site Map:</value>
|
||||
@ -426,4 +426,25 @@
|
||||
<data name="System" xml:space="preserve">
|
||||
<value>System</value>
|
||||
</data>
|
||||
<data name="CookieConsent.HelpText" xml:space="preserve">
|
||||
<value>Specify if cookie consent is enabled on this site. Please note this option must be used in conjunction with a Theme which supports cookie consent.</value>
|
||||
</data>
|
||||
<data name="CookieConsent.Text" xml:space="preserve">
|
||||
<value>Cookie Consent:</value>
|
||||
</data>
|
||||
<data name="OptIn" xml:space="preserve">
|
||||
<value>Opt-In (GDPR)</value>
|
||||
</data>
|
||||
<data name="OptOut" xml:space="preserve">
|
||||
<value>Opt-Out (CCPA)</value>
|
||||
</data>
|
||||
<data name="Theme.Heading" xml:space="preserve">
|
||||
<value>Theme</value>
|
||||
</data>
|
||||
<data name="SiteMap.EvictCache" xml:space="preserve">
|
||||
<value>Clear Cache</value>
|
||||
</data>
|
||||
<data name="Success.SiteMap.CacheEvicted" xml:space="preserve">
|
||||
<value>Site Map Cache Cleared</value>
|
||||
</data>
|
||||
</root>
|
@ -118,7 +118,7 @@
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<data name="Swagger" xml:space="preserve">
|
||||
<value>Access Swagger UI</value>
|
||||
<value>Swagger UI</value>
|
||||
</data>
|
||||
<data name="FrameworkVersion.HelpText" xml:space="preserve">
|
||||
<value>Framework Version</value>
|
||||
@ -306,4 +306,7 @@
|
||||
<data name="CacheControl.HelpText" xml:space="preserve">
|
||||
<value>Provide a Cache-Control directive for static assets. For example 'public, max-age=60' indicates that static assets should be cached for 60 seconds. A blank value indicates caching is not enabled.</value>
|
||||
</data>
|
||||
<data name="Endpoints" xml:space="preserve">
|
||||
<value>API Endpoints</value>
|
||||
</data>
|
||||
</root>
|
@ -132,8 +132,8 @@
|
||||
<data name="Owner.Text" xml:space="preserve">
|
||||
<value>Owner: </value>
|
||||
</data>
|
||||
<data name="ReferenceUrl.Text" xml:space="preserve">
|
||||
<value>Reference Url: </value>
|
||||
<data name="Url.Text" xml:space="preserve">
|
||||
<value>Url: </value>
|
||||
</data>
|
||||
<data name="Contact.Text" xml:space="preserve">
|
||||
<value>Contact: </value>
|
||||
@ -153,8 +153,8 @@
|
||||
<data name="Owner.HelpText" xml:space="preserve">
|
||||
<value>The owner or creator of the theme</value>
|
||||
</data>
|
||||
<data name="ReferenceUrl.HelpText" xml:space="preserve">
|
||||
<value>The reference url of the theme</value>
|
||||
<data name="Url.HelpText" xml:space="preserve">
|
||||
<value>The url of the theme</value>
|
||||
</data>
|
||||
<data name="Contact.HelpText" xml:space="preserve">
|
||||
<value>The contact for the theme</value>
|
||||
@ -180,16 +180,4 @@
|
||||
<data name="View License" xml:space="preserve">
|
||||
<value>View License</value>
|
||||
</data>
|
||||
<data name="Error.Validate" xml:space="preserve">
|
||||
<value>Error Validating Package</value>
|
||||
</data>
|
||||
<data name="Message.Download" xml:space="preserve">
|
||||
<value>Package Version Has Been Verified. Please Select The Download Button To Obtain The Package.</value>
|
||||
</data>
|
||||
<data name="Message.Validate" xml:space="preserve">
|
||||
<value>This Package Version Has Not Been Registered In The Oqtane Marketplace Or You Do Not Have The Right To Use It From This Installation</value>
|
||||
</data>
|
||||
<data name="Validate" xml:space="preserve">
|
||||
<value>Validate</value>
|
||||
</data>
|
||||
</root>
|
@ -146,7 +146,7 @@
|
||||
</data>
|
||||
<data name="InstallTheme.Text" xml:space="preserve">
|
||||
<value>Install Theme</value>
|
||||
</data>
|
||||
</data>
|
||||
<data name="ViewTheme.Text" xml:space="preserve">
|
||||
<value>View</value>
|
||||
</data>
|
||||
@ -156,4 +156,16 @@
|
||||
<data name="Enabled" xml:space="preserve">
|
||||
<value>Enabled?</value>
|
||||
</data>
|
||||
<data name="Assign" xml:space="preserve">
|
||||
<value>Assign</value>
|
||||
</data>
|
||||
<data name="Synchronize" xml:space="preserve">
|
||||
<value>Synchronize</value>
|
||||
</data>
|
||||
<data name="Success.Theme.Synchronize" xml:space="preserve">
|
||||
<value>Themes Have Been Successfully Synchronized With The Marketplace</value>
|
||||
</data>
|
||||
<data name="Error.Theme.Synchronize" xml:space="preserve">
|
||||
<value>Error Synchronizing Themes With The Marketplace</value>
|
||||
</data>
|
||||
</root>
|
@ -151,6 +151,12 @@
|
||||
<value>You Cannot Perform A System Update In A Development Environment</value>
|
||||
</data>
|
||||
<data name="Disclaimer.Text" xml:space="preserve">
|
||||
<value>Please Note That The System Update Capability Is A Simplified Upgrade Process Intended For Small To Medium Sized Installations. For Larger Enterprise Installations You Will Want To Use A Manual Upgrade Process. Also Note That The System Update Capability Is Not Recommended When Using Microsoft Azure Due To Environmental Limitations.</value>
|
||||
<value>Please Note That The System Update Capability Is A Simplified Upgrade Process Intended For Small To Medium Sized Installations. For Larger Enterprise Installations You Will Want To Use A Manual Upgrade Process.</value>
|
||||
</data>
|
||||
<data name="Backup.Text" xml:space="preserve">
|
||||
<value>Backup Files?</value>
|
||||
</data>
|
||||
<data name="Backup.HelpText" xml:space="preserve">
|
||||
<value>Specify if you want to backup files during the upgrade process. Disabling this option will result in a better experience in some environments.</value>
|
||||
</data>
|
||||
</root>
|
@ -507,4 +507,16 @@
|
||||
<data name="Error.DeleteUser" xml:space="preserve">
|
||||
<value>Error Deleting User</value>
|
||||
</data>
|
||||
<data name="LogoutEverywhere.Text" xml:space="preserve">
|
||||
<value>Logout Everywhere?</value>
|
||||
</data>
|
||||
<data name="LogoutEverywhere.HelpText" xml:space="preserve">
|
||||
<value>Do you want users to be logged out of every active session on any device, or only their current session?</value>
|
||||
</data>
|
||||
<data name="AllowHostRole.Text" xml:space="preserve">
|
||||
<value>Allow Host Role?</value>
|
||||
</data>
|
||||
<data name="AllowHostRole.HelpText" xml:space="preserve">
|
||||
<value>Indicate if host roles are supported from the identity provider. Please use caution with this option as it allows the host user to administrate every site within your installation.</value>
|
||||
</data>
|
||||
</root>
|
@ -198,4 +198,13 @@
|
||||
<data name="Duration.Text" xml:space="preserve">
|
||||
<value>Session Duration:</value>
|
||||
</data>
|
||||
<data name="Robots.Heading" xml:space="preserve">
|
||||
<value>Robots.txt</value>
|
||||
</data>
|
||||
<data name="Robots.Text" xml:space="preserve">
|
||||
<value>Instructions:</value>
|
||||
</data>
|
||||
<data name="Robots.HelpText" xml:space="preserve">
|
||||
<value>Specify your robots.txt instructions to provide bots with guidance on which parts of your site should be indexed</value>
|
||||
</data>
|
||||
</root>
|
126
Oqtane.Client/Resources/Modules/HtmlText/Settings.resx
Normal file
126
Oqtane.Client/Resources/Modules/HtmlText/Settings.resx
Normal file
@ -0,0 +1,126 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<root>
|
||||
<!--
|
||||
Microsoft ResX Schema
|
||||
|
||||
Version 2.0
|
||||
|
||||
The primary goals of this format is to allow a simple XML format
|
||||
that is mostly human readable. The generation and parsing of the
|
||||
various data types are done through the TypeConverter classes
|
||||
associated with the data types.
|
||||
|
||||
Example:
|
||||
|
||||
... ado.net/XML headers & schema ...
|
||||
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||
<resheader name="version">2.0</resheader>
|
||||
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
|
||||
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
|
||||
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
|
||||
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
|
||||
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
|
||||
<value>[base64 mime encoded serialized .NET Framework object]</value>
|
||||
</data>
|
||||
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||
<comment>This is a comment</comment>
|
||||
</data>
|
||||
|
||||
There are any number of "resheader" rows that contain simple
|
||||
name/value pairs.
|
||||
|
||||
Each data row contains a name, and value. The row also contains a
|
||||
type or mimetype. Type corresponds to a .NET class that support
|
||||
text/value conversion through the TypeConverter architecture.
|
||||
Classes that don't support this are serialized and stored with the
|
||||
mimetype set.
|
||||
|
||||
The mimetype is used for serialized objects, and tells the
|
||||
ResXResourceReader how to depersist the object. This is currently not
|
||||
extensible. For a given mimetype the value must be set accordingly:
|
||||
|
||||
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||
that the ResXResourceWriter will generate, however the reader can
|
||||
read any of the formats listed below.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.binary.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.soap.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||
value : The object must be serialized into a byte array
|
||||
: using a System.ComponentModel.TypeConverter
|
||||
: and then encoded with base64 encoding.
|
||||
-->
|
||||
<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>
|
||||
<xsd:choice maxOccurs="unbounded">
|
||||
<xsd:element name="metadata">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||
<xsd:attribute name="type" type="xsd:string" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="assembly">
|
||||
<xsd:complexType>
|
||||
<xsd:attribute name="alias" type="xsd:string" />
|
||||
<xsd:attribute name="name" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="data">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="resheader">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:choice>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:schema>
|
||||
<resheader name="resmimetype">
|
||||
<value>text/microsoft-resx</value>
|
||||
</resheader>
|
||||
<resheader name="version">
|
||||
<value>2.0</value>
|
||||
</resheader>
|
||||
<resheader name="reader">
|
||||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<data name="DynamicTokens.HelpText" xml:space="preserve">
|
||||
<value>Do you wish to allow tokens to be dynamically replaced? Please note that this will affect the performance of your site.</value>
|
||||
</data>
|
||||
<data name="DynamicTokens.Text" xml:space="preserve">
|
||||
<value>Dynamic Tokens?</value>
|
||||
</data>
|
||||
</root>
|
129
Oqtane.Client/Resources/Themes/Controls/CookieConsent.resx
Normal file
129
Oqtane.Client/Resources/Themes/Controls/CookieConsent.resx
Normal file
@ -0,0 +1,129 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<root>
|
||||
<!--
|
||||
Microsoft ResX Schema
|
||||
|
||||
Version 2.0
|
||||
|
||||
The primary goals of this format is to allow a simple XML format
|
||||
that is mostly human readable. The generation and parsing of the
|
||||
various data types are done through the TypeConverter classes
|
||||
associated with the data types.
|
||||
|
||||
Example:
|
||||
|
||||
... ado.net/XML headers & schema ...
|
||||
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||
<resheader name="version">2.0</resheader>
|
||||
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
|
||||
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
|
||||
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
|
||||
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
|
||||
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
|
||||
<value>[base64 mime encoded serialized .NET Framework object]</value>
|
||||
</data>
|
||||
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||
<comment>This is a comment</comment>
|
||||
</data>
|
||||
|
||||
There are any number of "resheader" rows that contain simple
|
||||
name/value pairs.
|
||||
|
||||
Each data row contains a name, and value. The row also contains a
|
||||
type or mimetype. Type corresponds to a .NET class that support
|
||||
text/value conversion through the TypeConverter architecture.
|
||||
Classes that don't support this are serialized and stored with the
|
||||
mimetype set.
|
||||
|
||||
The mimetype is used for serialized objects, and tells the
|
||||
ResXResourceReader how to depersist the object. This is currently not
|
||||
extensible. For a given mimetype the value must be set accordingly:
|
||||
|
||||
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||
that the ResXResourceWriter will generate, however the reader can
|
||||
read any of the formats listed below.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.binary.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.soap.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||
value : The object must be serialized into a byte array
|
||||
: using a System.ComponentModel.TypeConverter
|
||||
: and then encoded with base64 encoding.
|
||||
-->
|
||||
<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>
|
||||
<xsd:choice maxOccurs="unbounded">
|
||||
<xsd:element name="metadata">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||
<xsd:attribute name="type" type="xsd:string" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="assembly">
|
||||
<xsd:complexType>
|
||||
<xsd:attribute name="alias" type="xsd:string" />
|
||||
<xsd:attribute name="name" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="data">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="resheader">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:choice>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:schema>
|
||||
<resheader name="resmimetype">
|
||||
<value>text/microsoft-resx</value>
|
||||
</resheader>
|
||||
<resheader name="version">
|
||||
<value>2.0</value>
|
||||
</resheader>
|
||||
<resheader name="reader">
|
||||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<data name="Confirm" xml:space="preserve">
|
||||
<value>Confirm</value>
|
||||
</data>
|
||||
<data name="ConsentNotice" xml:space="preserve">
|
||||
<value>I agree to use cookies to provide the best possible user experience for this site. I understand that I can change these preferences at any time.</value>
|
||||
</data>
|
||||
<data name="Privacy" xml:space="preserve">
|
||||
<value>Privacy</value>
|
||||
</data>
|
||||
</root>
|
@ -1,4 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<root>
|
||||
<!--
|
||||
Microsoft ResX Schema
|
||||
|
47
Oqtane.Client/Services/CookieConsentService.cs
Normal file
47
Oqtane.Client/Services/CookieConsentService.cs
Normal file
@ -0,0 +1,47 @@
|
||||
using Oqtane.Models;
|
||||
using System.Threading.Tasks;
|
||||
using System.Net.Http;
|
||||
using System;
|
||||
using Oqtane.Documentation;
|
||||
using Oqtane.Shared;
|
||||
using System.Globalization;
|
||||
|
||||
namespace Oqtane.Services
|
||||
{
|
||||
/// <inheritdoc cref="ICookieConsentService" />
|
||||
[PrivateApi("Don't show in the documentation, as everything should use the Interface")]
|
||||
public class CookieConsentService : ServiceBase, ICookieConsentService
|
||||
{
|
||||
public CookieConsentService(HttpClient http, SiteState siteState) : base(http, siteState) { }
|
||||
|
||||
private string ApiUrl => CreateApiUrl("CookieConsent");
|
||||
|
||||
public async Task<bool> IsActionedAsync()
|
||||
{
|
||||
return await GetJsonAsync<bool>($"{ApiUrl}/IsActioned");
|
||||
}
|
||||
|
||||
public async Task<bool> CanTrackAsync(bool optOut)
|
||||
{
|
||||
return await GetJsonAsync<bool>($"{ApiUrl}/CanTrack?optout=" + optOut);
|
||||
}
|
||||
|
||||
public async Task<string> CreateActionedCookieAsync()
|
||||
{
|
||||
var cookie = await GetStringAsync($"{ApiUrl}/CreateActionedCookie");
|
||||
return cookie ?? string.Empty;
|
||||
}
|
||||
|
||||
public async Task<string> CreateConsentCookieAsync()
|
||||
{
|
||||
var cookie = await GetStringAsync($"{ApiUrl}/CreateConsentCookie");
|
||||
return cookie ?? string.Empty;
|
||||
}
|
||||
|
||||
public async Task<string> WithdrawConsentCookieAsync()
|
||||
{
|
||||
var cookie = await GetStringAsync($"{ApiUrl}/WithdrawConsentCookie");
|
||||
return cookie ?? string.Empty;
|
||||
}
|
||||
}
|
||||
}
|
@ -47,9 +47,9 @@ namespace Oqtane.Services
|
||||
return await PostJsonAsync<InstallConfig,Installation>(ApiUrl, config);
|
||||
}
|
||||
|
||||
public async Task<Installation> Upgrade()
|
||||
public async Task<Installation> Upgrade(bool backup)
|
||||
{
|
||||
return await GetJsonAsync<Installation>($"{ApiUrl}/upgrade");
|
||||
return await GetJsonAsync<Installation>($"{ApiUrl}/upgrade/?backup={backup}");
|
||||
}
|
||||
|
||||
public async Task RestartAsync()
|
||||
|
42
Oqtane.Client/Services/Interfaces/ICookieConsentService.cs
Normal file
42
Oqtane.Client/Services/Interfaces/ICookieConsentService.cs
Normal file
@ -0,0 +1,42 @@
|
||||
using Oqtane.Models;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Oqtane.Services
|
||||
{
|
||||
/// <summary>
|
||||
/// Service to retrieve cookie consent information.
|
||||
/// </summary>
|
||||
public interface ICookieConsentService
|
||||
{
|
||||
/// <summary>
|
||||
/// Get cookie consent bar actioned status
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
Task<bool> IsActionedAsync();
|
||||
|
||||
/// <summary>
|
||||
/// Get cookie consent status
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
Task<bool> CanTrackAsync(bool optOut);
|
||||
|
||||
/// <summary>
|
||||
/// create actioned cookie
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
Task<string> CreateActionedCookieAsync();
|
||||
|
||||
/// <summary>
|
||||
/// create consent cookie
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
Task<string> CreateConsentCookieAsync();
|
||||
|
||||
/// <summary>
|
||||
/// widhdraw consent cookie
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
Task<string> WithdrawConsentCookieAsync();
|
||||
}
|
||||
}
|
@ -26,8 +26,9 @@ namespace Oqtane.Services
|
||||
/// <summary>
|
||||
/// Starts the upgrade process
|
||||
/// </summary>
|
||||
/// <param name="backup">indicates if files should be backed up during upgrade</param>
|
||||
/// <returns>internal status/message object</returns>
|
||||
Task<Installation> Upgrade();
|
||||
Task<Installation> Upgrade(bool backup);
|
||||
|
||||
/// <summary>
|
||||
/// Restarts the installation
|
||||
|
@ -10,10 +10,11 @@ namespace Oqtane.Services
|
||||
public interface IJobLogService
|
||||
{
|
||||
/// <summary>
|
||||
/// Return a list of all <see cref="JobLog"/> entries
|
||||
/// Return a list of <see cref="JobLog"/> entries
|
||||
/// </summary>
|
||||
/// <param name="jobId"></param>
|
||||
/// <returns></returns>
|
||||
Task<List<JobLog>> GetJobLogsAsync();
|
||||
Task<List<JobLog>> GetJobLogsAsync(int jobId);
|
||||
|
||||
/// <summary>
|
||||
/// Return a <see cref="JobLog"/> entry for the given Id
|
||||
|
18
Oqtane.Client/Services/Interfaces/IOutputCacheService.cs
Normal file
18
Oqtane.Client/Services/Interfaces/IOutputCacheService.cs
Normal file
@ -0,0 +1,18 @@
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Oqtane.Services
|
||||
{
|
||||
/// <summary>
|
||||
/// Service to manage cache
|
||||
/// </summary>
|
||||
public interface IOutputCacheService
|
||||
{
|
||||
/// <summary>
|
||||
/// Evicts the output cache for a specific tag
|
||||
/// </summary>
|
||||
/// <param name="tag"></param>
|
||||
/// <returns></returns>
|
||||
Task EvictByTag(string tag);
|
||||
}
|
||||
}
|
@ -15,10 +15,9 @@ namespace Oqtane.Services
|
||||
|
||||
private string Apiurl => CreateApiUrl("JobLog");
|
||||
|
||||
public async Task<List<JobLog>> GetJobLogsAsync()
|
||||
public async Task<List<JobLog>> GetJobLogsAsync(int jobId)
|
||||
{
|
||||
List<JobLog> joblogs = await GetJsonAsync<List<JobLog>>(Apiurl);
|
||||
return joblogs.OrderBy(item => item.StartDate).ToList();
|
||||
return await GetJsonAsync<List<JobLog>>($"{Apiurl}?jobid={jobId}");
|
||||
}
|
||||
|
||||
public async Task<JobLog> GetJobLogAsync(int jobLogId)
|
||||
|
23
Oqtane.Client/Services/OutputCacheService.cs
Normal file
23
Oqtane.Client/Services/OutputCacheService.cs
Normal file
@ -0,0 +1,23 @@
|
||||
using System.Net.Http;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using Oqtane.Documentation;
|
||||
using Oqtane.Shared;
|
||||
|
||||
namespace Oqtane.Services
|
||||
{
|
||||
/// <inheritdoc cref="IOutputCacheService" />
|
||||
[PrivateApi("Don't show in the documentation, as everything should use the Interface")]
|
||||
public class OutputCacheService : ServiceBase, IOutputCacheService
|
||||
{
|
||||
public OutputCacheService(HttpClient http, SiteState siteState) : base(http, siteState) { }
|
||||
|
||||
private string ApiUrl => CreateApiUrl("OutputCache");
|
||||
|
||||
public async Task EvictByTag(string tag)
|
||||
{
|
||||
await DeleteAsync($"{ApiUrl}/{tag}");
|
||||
}
|
||||
}
|
||||
}
|
@ -1,11 +1,6 @@
|
||||
@namespace Oqtane.Themes.BlazorTheme
|
||||
@inherits ThemeBase
|
||||
|
||||
|
||||
<div class="breadcrumbs">
|
||||
<Breadcrumbs />
|
||||
</div>
|
||||
|
||||
<div class="row flex-xl-nowrap gx-0">
|
||||
<div class="sidebar">
|
||||
<nav class="navbar">
|
||||
@ -22,13 +17,18 @@
|
||||
<Login />
|
||||
<ControlPanel LanguageDropdownAlignment="right" />
|
||||
</div>
|
||||
<div class="breadcrumbs">
|
||||
<Breadcrumbs />
|
||||
</div>
|
||||
</div>
|
||||
<div class="container">
|
||||
<div class="row px-4">
|
||||
<Pane Name="@PaneNames.Admin" />
|
||||
<CookieConsent />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
@code {
|
||||
|
@ -573,7 +573,7 @@
|
||||
else
|
||||
{
|
||||
// post to the Logout page to complete the logout process
|
||||
var fields = new { __RequestVerificationToken = SiteState.AntiForgeryToken, returnurl = url };
|
||||
var fields = new { __RequestVerificationToken = SiteState.AntiForgeryToken, returnurl = url, everywhere = bool.Parse(SettingService.GetSetting(PageState.Site.Settings, "LoginOptions:LogoutEverywhere", "false")) };
|
||||
var interop = new Interop(jsRuntime);
|
||||
await interop.SubmitForm(Utilities.TenantUrl(PageState.Alias, "/pages/logout/"), fields);
|
||||
}
|
||||
|
167
Oqtane.Client/Themes/Controls/Theme/CookieConsent.razor
Normal file
167
Oqtane.Client/Themes/Controls/Theme/CookieConsent.razor
Normal file
@ -0,0 +1,167 @@
|
||||
@namespace Oqtane.Themes.Controls
|
||||
@inherits ThemeControlBase
|
||||
@inject ISettingService SettingService
|
||||
@inject ICookieConsentService CookieConsentService
|
||||
@inject IStringLocalizer<CookieConsent> Localizer
|
||||
|
||||
@if (_enabled && !Hidden)
|
||||
{
|
||||
<div class="gdpr-consent-bar bg-light text-dark @(_showBanner ? "px-0 py-3 pt-5 pt-sm-3 pe-sm-5 ps-sm-3" : "p-0") fixed-bottom">
|
||||
<form method="post" @formname="CookieConsentForm" @onsubmit="async () => await AcceptPolicy()" data-enhance>
|
||||
@if (_showBanner)
|
||||
{
|
||||
<input type="hidden" name="@Constants.RequestVerificationToken" value="@SiteState.AntiForgeryToken" />
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="col-9 col-xl-10">
|
||||
@if (PageState.RenderMode == RenderModes.Static)
|
||||
{
|
||||
<input type="checkbox" name="cantrack" checked="@_canTrack" value="1" class="form-check-input me-2" />
|
||||
}
|
||||
else
|
||||
{
|
||||
<input type="checkbox" name="cantrack" @bind="@_canTrack" value="1" class="form-check-input me-2" />
|
||||
}
|
||||
@((MarkupString)Convert.ToString(Localizer["ConsentNotice"]))
|
||||
</div>
|
||||
<div class="col-3 col-xl-2">
|
||||
<div class="row">
|
||||
<div class="col-md-6 col-xs-6 text-center">
|
||||
<button class="btn btn-primary mb-1 px-0 w-100" type="submit">@((MarkupString)Convert.ToString(Localizer["Confirm"]))</button>
|
||||
</div>
|
||||
@if (ShowPrivacyLink)
|
||||
{
|
||||
<div class="col-md-6 col-xs-6 text-center">
|
||||
<a class="btn btn-secondary mb-1 px-0 w-100" href="/privacy" target="_blank">@((MarkupString)Convert.ToString(Localizer["Privacy"]))</a>
|
||||
</div>
|
||||
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
</form>
|
||||
<form method="post" @formname="CookieConsentToggleForm" @onsubmit="async () => await ToggleBanner()" data-enhance>
|
||||
<input type="hidden" name="@Constants.RequestVerificationToken" value="@SiteState.AntiForgeryToken" />
|
||||
@if (_showBanner)
|
||||
{
|
||||
<input type="hidden" name="showbanner" value="false" />
|
||||
<button type="submit" class="btn btn-light text-dark btn-sm position-absolute btn-hide">
|
||||
<i class="oi oi-chevron-bottom"></i>
|
||||
</button>
|
||||
}
|
||||
else
|
||||
{
|
||||
<input type="hidden" name="showbanner" value="true" />
|
||||
<button type="submit" class="btn btn-light text-dark btn-sm position-absolute btn-show">
|
||||
<i class="oi oi-chevron-top"></i>
|
||||
</button>
|
||||
}
|
||||
|
||||
</form>
|
||||
</div>
|
||||
}
|
||||
@code {
|
||||
private bool _showBanner;
|
||||
private bool _enabled;
|
||||
private bool _optout;
|
||||
private bool _actioned;
|
||||
private bool _canTrack;
|
||||
private bool _consentPostback;
|
||||
private bool _togglePostback;
|
||||
|
||||
[Parameter]
|
||||
public bool Hidden { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public bool ShowPrivacyLink { get; set; } = true;
|
||||
|
||||
[SupplyParameterFromForm(FormName = "CookieConsentToggleForm")]
|
||||
public string ShowBanner
|
||||
{
|
||||
get => "";
|
||||
set
|
||||
{
|
||||
_showBanner = bool.Parse(value);
|
||||
_togglePostback = true;
|
||||
}
|
||||
}
|
||||
|
||||
[SupplyParameterFromForm(FormName = "CookieConsentForm")]
|
||||
public string CanTrack
|
||||
{
|
||||
get => "";
|
||||
set
|
||||
{
|
||||
_canTrack = !string.IsNullOrEmpty(value);
|
||||
_consentPostback = true;
|
||||
}
|
||||
}
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
var cookieConsentSetting = SettingService.GetSetting(PageState.Site.Settings, "CookieConsent", string.Empty);
|
||||
_enabled = !string.IsNullOrEmpty(cookieConsentSetting);
|
||||
_optout = cookieConsentSetting == "optout";
|
||||
_actioned = await CookieConsentService.IsActionedAsync();
|
||||
|
||||
if (!_consentPostback)
|
||||
{
|
||||
_canTrack = await CookieConsentService.CanTrackAsync(_optout);
|
||||
}
|
||||
|
||||
if (!_togglePostback)
|
||||
{
|
||||
_showBanner = !_actioned;
|
||||
}
|
||||
}
|
||||
|
||||
private async Task AcceptPolicy()
|
||||
{
|
||||
var cookieString = string.Empty;
|
||||
if (_optout)
|
||||
{
|
||||
cookieString = _canTrack ? await CookieConsentService.WithdrawConsentCookieAsync() : await CookieConsentService.CreateConsentCookieAsync();
|
||||
}
|
||||
else
|
||||
{
|
||||
cookieString = _canTrack ? await CookieConsentService.CreateConsentCookieAsync() : await CookieConsentService.WithdrawConsentCookieAsync();
|
||||
}
|
||||
|
||||
//update the page state
|
||||
PageState.AllowCookies = _canTrack;
|
||||
|
||||
if (!string.IsNullOrEmpty(cookieString))
|
||||
{
|
||||
var interop = new Interop(JSRuntime);
|
||||
await interop.SetCookieString(cookieString);
|
||||
|
||||
_actioned = true;
|
||||
_showBanner = false;
|
||||
|
||||
StateHasChanged();
|
||||
}
|
||||
}
|
||||
|
||||
private async Task ToggleBanner()
|
||||
{
|
||||
if (!_actioned)
|
||||
{
|
||||
var cookieString = await CookieConsentService.CreateActionedCookieAsync();
|
||||
if (!string.IsNullOrEmpty(cookieString))
|
||||
{
|
||||
var interop = new Interop(JSRuntime);
|
||||
await interop.SetCookieString(cookieString);
|
||||
|
||||
_actioned = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (PageState.RenderMode == RenderModes.Interactive)
|
||||
{
|
||||
_showBanner = !_showBanner;
|
||||
StateHasChanged();
|
||||
}
|
||||
}
|
||||
}
|
@ -15,6 +15,7 @@
|
||||
<form method="post" class="app-form-inline" action="@logouturl" @formname="LogoutForm">
|
||||
<input type="hidden" name="@Constants.RequestVerificationToken" value="@SiteState.AntiForgeryToken" />
|
||||
<input type="hidden" name="returnurl" value="@returnurl" />
|
||||
<input type="hidden" name="everywhere" value="@everywhere" />
|
||||
<button type="submit" class="@CssClass">@Localizer["Logout"]</button>
|
||||
</form>
|
||||
}
|
||||
|
@ -4,7 +4,6 @@ using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using Microsoft.JSInterop;
|
||||
using Oqtane.Enums;
|
||||
using Oqtane.Models;
|
||||
using Oqtane.Providers;
|
||||
using Oqtane.Security;
|
||||
using Oqtane.Services;
|
||||
@ -26,6 +25,7 @@ namespace Oqtane.Themes.Controls
|
||||
protected string loginurl;
|
||||
protected string logouturl;
|
||||
protected string returnurl;
|
||||
protected string everywhere;
|
||||
|
||||
protected override void OnParametersSet()
|
||||
{
|
||||
@ -57,6 +57,7 @@ namespace Oqtane.Themes.Controls
|
||||
|
||||
// set logout url
|
||||
logouturl = Utilities.TenantUrl(PageState.Alias, "/pages/logout/");
|
||||
everywhere = SettingService.GetSetting(PageState.Site.Settings, "LoginOptions:LogoutEverywhere", "false");
|
||||
|
||||
// verify anonymous users can access current page
|
||||
if (UserSecurity.IsAuthorized(null, PermissionNames.View, PageState.Page.PermissionList) && Utilities.IsEffectiveAndNotExpired(PageState.Page.EffectiveDate, PageState.Page.ExpiryDate))
|
||||
@ -98,7 +99,7 @@ namespace Oqtane.Themes.Controls
|
||||
else // this condition is only valid for legacy Login button inheriting from LoginBase
|
||||
{
|
||||
// post to the Logout page to complete the logout process
|
||||
var fields = new { __RequestVerificationToken = SiteState.AntiForgeryToken, returnurl = returnurl };
|
||||
var fields = new { __RequestVerificationToken = SiteState.AntiForgeryToken, returnurl = returnurl, everywhere = bool.Parse(SettingService.GetSetting(PageState.Site.Settings, "LoginOptions:LogoutEverywhere", "false")) };
|
||||
var interop = new Interop(jsRuntime);
|
||||
await interop.SubmitForm(logouturl, fields);
|
||||
}
|
||||
|
@ -2,19 +2,18 @@
|
||||
@using System.Net
|
||||
@inherits ThemeControlBase
|
||||
@inject IStringLocalizer<UserProfile> Localizer
|
||||
|
||||
@inject NavigationManager NavigationManager
|
||||
|
||||
<span class="app-profile">
|
||||
@if (PageState.User != null)
|
||||
{
|
||||
<a href="@NavigateUrl("profile", "returnurl=" + _returnurl)" class="@CssClass">@PageState.User.Username</a>
|
||||
<a href="@_profileurl" class="@CssClass">@PageState.User.Username</a>
|
||||
}
|
||||
else
|
||||
{
|
||||
@if (ShowRegister && PageState.Site.AllowRegistration)
|
||||
{
|
||||
<a href="@NavigateUrl("register", "returnurl=" + _returnurl)" class="@CssClass">@Localizer["Register"]</a>
|
||||
<a href="@_registerurl" class="@CssClass">@Localizer["Register"]</a>
|
||||
}
|
||||
}
|
||||
</span>
|
||||
@ -23,23 +22,50 @@
|
||||
|
||||
[Parameter]
|
||||
public bool ShowRegister { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public string CssClass { get; set; } = "btn btn-primary";
|
||||
|
||||
[Parameter]
|
||||
public string RegisterUrl { get; set; } // optional parameter to specify a custom registration url
|
||||
|
||||
[Parameter]
|
||||
public string ProfileUrl { get; set; } // optional parameter to specify a custom user profile url
|
||||
|
||||
private string _registerurl = "";
|
||||
private string _profileurl = "";
|
||||
private string _returnurl = "";
|
||||
|
||||
protected override void OnParametersSet()
|
||||
{
|
||||
if (!PageState.QueryString.ContainsKey("returnurl"))
|
||||
{
|
||||
// remember current url
|
||||
_returnurl = WebUtility.UrlEncode(PageState.Route.PathAndQuery);
|
||||
}
|
||||
else
|
||||
{
|
||||
// use existing value
|
||||
_returnurl = PageState.QueryString["returnurl"];
|
||||
}
|
||||
{
|
||||
if (!PageState.QueryString.ContainsKey("returnurl"))
|
||||
{
|
||||
// remember current url
|
||||
_returnurl = WebUtility.UrlEncode(PageState.Route.PathAndQuery);
|
||||
}
|
||||
else
|
||||
{
|
||||
// use existing value
|
||||
_returnurl = PageState.QueryString["returnurl"];
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(RegisterUrl))
|
||||
{
|
||||
_registerurl = RegisterUrl + "?returnurl=" + (RegisterUrl.Contains("://") ? WebUtility.UrlEncode(PageState.Route.RootUrl) + _returnurl : _returnurl);
|
||||
}
|
||||
else
|
||||
{
|
||||
_registerurl = NavigateUrl("register", "returnurl=" + _returnurl);
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(ProfileUrl))
|
||||
{
|
||||
_registerurl = ProfileUrl + "?returnurl=" + (ProfileUrl.Contains("://") ? WebUtility.UrlEncode(PageState.Route.RootUrl) + _returnurl : _returnurl);
|
||||
}
|
||||
else
|
||||
{
|
||||
_registerurl = NavigateUrl("profile", "returnurl=" + _returnurl);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -22,7 +22,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<Pane Name="Top Full Width" />
|
||||
<Pane Name="Top Full Width" />
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
@ -107,13 +107,14 @@
|
||||
{
|
||||
<Pane Name="Footer" />
|
||||
}
|
||||
</div>
|
||||
<CookieConsent />
|
||||
</div>
|
||||
</main>
|
||||
|
||||
@code {
|
||||
public override string Name => "Default Theme";
|
||||
|
||||
public override string Panes => PaneNames.Default + ",Top Full Width,Top 100%,Left 50%,Right 50%,Left 33%,Center 33%,Right 33%,Left Outer 25%,Left Inner 25%,Right Inner 25%,Right Outer 25%,Left 25%,Center 50%,Right 25%,Left Sidebar 66%,Right Sidebar 33%,Left Sidebar 33%,Right Sidebar 66%,Bottom 100%,Bottom Full Width,Footer";
|
||||
public override string Panes => PaneNames.Default + ",Top Full Width,Top 100%,Left 50%,Right 50%,Left 33%,Center 33%,Right 33%,Left Outer 25%,Left Inner 25%,Right Inner 25%,Right Outer 25%,Left 25%,Center 50%,Right 25%,Left Sidebar 66%,Right Sidebar 33%,Left Sidebar 33%,Right Sidebar 66%,Bottom 100%,Bottom Full Width,Footer";
|
||||
|
||||
private bool _login = true;
|
||||
private bool _register = true;
|
||||
|
@ -13,9 +13,9 @@
|
||||
<select id="scope" class="form-select" value="@_scope" @onchange="(e => ScopeChanged(e))">
|
||||
@if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Admin))
|
||||
{
|
||||
<option value="site">@Localizer["Site"]</option>
|
||||
<option value="site">@Localizer["Site"]</option>
|
||||
}
|
||||
<option value="page">@Localizer["Page"]</option>
|
||||
<option value="page">@Localizer["Page"]</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
@ -92,7 +92,7 @@
|
||||
settings = SettingService.MergeSettings(PageState.Site.Settings, settings);
|
||||
_login = SettingService.GetSetting(settings, GetType().Namespace + ":Login", "-");
|
||||
_register = SettingService.GetSetting(settings, GetType().Namespace + ":Register", "-");
|
||||
_footer = SettingService.GetSetting(settings, GetType().Namespace + ":Footer", "-");
|
||||
_footer = SettingService.GetSetting(settings, GetType().Namespace + ":Footer", "-");
|
||||
}
|
||||
await Task.Yield();
|
||||
}
|
||||
|
@ -37,6 +37,19 @@ namespace Oqtane.UI
|
||||
}
|
||||
}
|
||||
|
||||
public Task SetCookieString(string cookieString)
|
||||
{
|
||||
try
|
||||
{
|
||||
_jsRuntime.InvokeVoidAsync("Oqtane.Interop.setCookieString", cookieString);
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
|
||||
public ValueTask<string> GetCookie(string name)
|
||||
{
|
||||
try
|
||||
|
@ -27,6 +27,7 @@ namespace Oqtane.UI
|
||||
public bool IsInternalNavigation { get; set; }
|
||||
public Guid RenderId { get; set; }
|
||||
public bool Refresh { get; set; }
|
||||
public bool AllowCookies { get; set; }
|
||||
|
||||
public List<Page> Pages
|
||||
{
|
||||
|
@ -14,6 +14,7 @@
|
||||
@inject IUrlMappingService UrlMappingService
|
||||
@inject ILogService LogService
|
||||
@inject ISettingService SettingService
|
||||
@inject ICookieConsentService CookieConsentService
|
||||
@inject IJSRuntime JSRuntime
|
||||
@implements IHandleAfterRender
|
||||
@implements IDisposable
|
||||
@ -293,6 +294,14 @@
|
||||
// load additional metadata for modules
|
||||
(page, modules) = ProcessModules(site, page, modules, moduleid, action, (!string.IsNullOrEmpty(page.DefaultContainerType)) ? page.DefaultContainerType : site.DefaultContainerType, SiteState.Alias);
|
||||
|
||||
//cookie consent
|
||||
var _allowCookies = PageState?.AllowCookies;
|
||||
if(!_allowCookies.HasValue)
|
||||
{
|
||||
var cookieConsentSettings = SettingService.GetSetting(site.Settings, "CookieConsent", string.Empty);
|
||||
_allowCookies = string.IsNullOrEmpty(cookieConsentSettings) || await CookieConsentService.CanTrackAsync(cookieConsentSettings == "optout");
|
||||
}
|
||||
|
||||
// populate page state (which acts as a client-side cache for subsequent requests)
|
||||
_pagestate = new PageState
|
||||
{
|
||||
@ -316,7 +325,8 @@
|
||||
ReturnUrl = returnurl,
|
||||
IsInternalNavigation = _isInternalNavigation,
|
||||
RenderId = Guid.NewGuid(),
|
||||
Refresh = false
|
||||
Refresh = false,
|
||||
AllowCookies = _allowCookies.GetValueOrDefault(true)
|
||||
};
|
||||
OnStateChange?.Invoke(_pagestate);
|
||||
|
||||
@ -613,7 +623,7 @@
|
||||
}
|
||||
|
||||
// ensure resource does not exist already
|
||||
if (!pageresources.Exists(item => item.Url.ToLower() == resource.Url.ToLower()))
|
||||
if (!pageresources.Exists(item => Utilities.GetUrlPath(item.Url).ToLower() == Utilities.GetUrlPath(resource.Url).ToLower()))
|
||||
{
|
||||
pageresources.Add(resource.Clone(level, name, fingerprint));
|
||||
}
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net9.0</TargetFramework>
|
||||
<Version>6.1.0</Version>
|
||||
<Version>6.1.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/v6.1.0</PackageReleaseNotes>
|
||||
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v6.1.2</PackageReleaseNotes>
|
||||
<RepositoryUrl>https://github.com/oqtane/oqtane.framework</RepositoryUrl>
|
||||
<RepositoryType>Git</RepositoryType>
|
||||
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
|
||||
@ -34,7 +34,7 @@
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="MySql.Data" Version="9.2.0" />
|
||||
<PackageReference Include="Pomelo.EntityFrameworkCore.MySql" Version="9.0.0-preview.2.efcore.9.0.0" />
|
||||
<PackageReference Include="Pomelo.EntityFrameworkCore.MySql" Version="9.0.0-preview.3.efcore.9.0.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
@ -42,7 +42,7 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<MySQLFiles Include="$(OutputPath)Oqtane.Database.MySQL.dll;$(OutputPath)Oqtane.Database.MySQL.pdb;$(OutputPath)Pomelo.EntityFrameworkCore.MySql.dll;$(OutputPath)MySql.Data.dll" DestinationPath="..\Oqtane.Server\bin\$(Configuration)\net9.0\%(Filename)%(Extension)" />
|
||||
<MySQLFiles Include="$(OutputPath)Oqtane.Database.MySQL.dll;$(OutputPath)Oqtane.Database.MySQL.pdb;$(OutputPath)Pomelo.EntityFrameworkCore.MySql.dll;$(OutputPath)MySqlConnector.dll;$(OutputPath)MySql.Data.dll" DestinationPath="..\Oqtane.Server\bin\$(Configuration)\net9.0\%(Filename)%(Extension)" />
|
||||
</ItemGroup>
|
||||
|
||||
<Target Name="PublishProvider" AfterTargets="PostBuildEvent" Inputs="@(MySQLFiles)" Outputs="@(MySQLFiles->'%(DestinationPath)')">
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net9.0</TargetFramework>
|
||||
<Version>6.1.0</Version>
|
||||
<Version>6.1.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/v6.1.0</PackageReleaseNotes>
|
||||
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v6.1.2</PackageReleaseNotes>
|
||||
<RepositoryUrl>https://github.com/oqtane/oqtane.framework</RepositoryUrl>
|
||||
<RepositoryType>Git</RepositoryType>
|
||||
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
|
||||
@ -34,8 +34,8 @@
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="EFCore.NamingConventions" Version="9.0.0" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="9.0.1" />
|
||||
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="9.0.3" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="9.0.4" />
|
||||
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="9.0.4" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net9.0</TargetFramework>
|
||||
<Version>6.1.0</Version>
|
||||
<Version>6.1.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/v6.1.0</PackageReleaseNotes>
|
||||
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v6.1.2</PackageReleaseNotes>
|
||||
<RepositoryUrl>https://github.com/oqtane/oqtane.framework</RepositoryUrl>
|
||||
<RepositoryType>Git</RepositoryType>
|
||||
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
|
||||
@ -33,7 +33,7 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="9.0.1" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="9.0.4" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net9.0</TargetFramework>
|
||||
<Version>6.1.0</Version>
|
||||
<Version>6.1.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/v6.1.0</PackageReleaseNotes>
|
||||
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v6.1.2</PackageReleaseNotes>
|
||||
<RepositoryUrl>https://github.com/oqtane/oqtane.framework</RepositoryUrl>
|
||||
<RepositoryType>Git</RepositoryType>
|
||||
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
|
||||
@ -33,7 +33,7 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="9.0.1" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="9.0.4" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
@ -6,7 +6,7 @@
|
||||
<!-- <TargetFrameworks>net9.0-android;net9.0-ios;net9.0-maccatalyst</TargetFrameworks> -->
|
||||
<!-- <TargetFrameworks>$(TargetFrameworks);net9.0-tizen</TargetFrameworks> -->
|
||||
<OutputType>Exe</OutputType>
|
||||
<Version>6.1.0</Version>
|
||||
<Version>6.1.2</Version>
|
||||
<Product>Oqtane</Product>
|
||||
<Authors>Shaun Walker</Authors>
|
||||
<Company>.NET Foundation</Company>
|
||||
@ -14,7 +14,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/v6.1.0</PackageReleaseNotes>
|
||||
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v6.1.2</PackageReleaseNotes>
|
||||
<RepositoryUrl>https://github.com/oqtane/oqtane.framework</RepositoryUrl>
|
||||
<RepositoryType>Git</RepositoryType>
|
||||
<RootNamespace>Oqtane.Maui</RootNamespace>
|
||||
@ -30,7 +30,7 @@
|
||||
<ApplicationId>com.oqtane.maui</ApplicationId>
|
||||
|
||||
<!-- Versions -->
|
||||
<ApplicationDisplayVersion>6.1.0</ApplicationDisplayVersion>
|
||||
<ApplicationDisplayVersion>6.1.2</ApplicationDisplayVersion>
|
||||
<ApplicationVersion>1</ApplicationVersion>
|
||||
|
||||
<!-- To develop, package, and publish an app to the Microsoft Store, see: https://aka.ms/MauiTemplateUnpackaged -->
|
||||
@ -67,14 +67,14 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.AspNetCore.Components.Authorization" Version="9.0.1" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="9.0.1" />
|
||||
<PackageReference Include="Microsoft.Extensions.Http" Version="9.0.1" />
|
||||
<PackageReference Include="Microsoft.Extensions.Localization" Version="9.0.1" />
|
||||
<PackageReference Include="System.Net.Http.Json" Version="9.0.1" />
|
||||
<PackageReference Include="Microsoft.Maui.Controls" Version="9.0.30" />
|
||||
<PackageReference Include="Microsoft.Maui.Controls.Compatibility" Version="9.0.30" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Components.WebView.Maui" Version="9.0.30" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Components.Authorization" Version="9.0.4" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="9.0.4" />
|
||||
<PackageReference Include="Microsoft.Extensions.Http" Version="9.0.4" />
|
||||
<PackageReference Include="Microsoft.Extensions.Localization" Version="9.0.4" />
|
||||
<PackageReference Include="System.Net.Http.Json" Version="9.0.4" />
|
||||
<PackageReference Include="Microsoft.Maui.Controls" Version="9.0.50" />
|
||||
<PackageReference Include="Microsoft.Maui.Controls.Compatibility" Version="9.0.50" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Components.WebView.Maui" Version="9.0.50" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
@ -14,6 +14,9 @@ Oqtane.Interop = {
|
||||
}
|
||||
document.cookie = cookieString;
|
||||
},
|
||||
setCookieString: function (cookieString) {
|
||||
document.cookie = cookieString;
|
||||
},
|
||||
getCookie: function (name) {
|
||||
name = name + "=";
|
||||
var decodedCookie = decodeURIComponent(document.cookie);
|
||||
|
@ -2,7 +2,7 @@
|
||||
<package>
|
||||
<metadata>
|
||||
<id>Oqtane.Client</id>
|
||||
<version>6.1.0</version>
|
||||
<version>6.1.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/v6.1.0</releaseNotes>
|
||||
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v6.1.2</releaseNotes>
|
||||
<readme>readme.md</readme>
|
||||
<icon>icon.png</icon>
|
||||
<tags>oqtane</tags>
|
||||
|
@ -2,7 +2,7 @@
|
||||
<package>
|
||||
<metadata>
|
||||
<id>Oqtane.Framework</id>
|
||||
<version>6.1.0</version>
|
||||
<version>6.1.2</version>
|
||||
<authors>Shaun Walker</authors>
|
||||
<owners>.NET Foundation</owners>
|
||||
<title>Oqtane Framework</title>
|
||||
@ -11,8 +11,8 @@
|
||||
<copyright>.NET Foundation</copyright>
|
||||
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
||||
<license type="expression">MIT</license>
|
||||
<projectUrl>https://github.com/oqtane/oqtane.framework/releases/download/v6.1.0/Oqtane.Framework.6.1.0.Upgrade.zip</projectUrl>
|
||||
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v6.1.0</releaseNotes>
|
||||
<projectUrl>https://github.com/oqtane/oqtane.framework/releases/download/v6.1.2/Oqtane.Framework.6.1.2.Upgrade.zip</projectUrl>
|
||||
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v6.1.2</releaseNotes>
|
||||
<readme>readme.md</readme>
|
||||
<icon>icon.png</icon>
|
||||
<tags>oqtane framework</tags>
|
||||
|
@ -2,7 +2,7 @@
|
||||
<package>
|
||||
<metadata>
|
||||
<id>Oqtane.Server</id>
|
||||
<version>6.1.0</version>
|
||||
<version>6.1.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/v6.1.0</releaseNotes>
|
||||
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v6.1.2</releaseNotes>
|
||||
<readme>readme.md</readme>
|
||||
<icon>icon.png</icon>
|
||||
<tags>oqtane</tags>
|
||||
|
@ -2,7 +2,7 @@
|
||||
<package>
|
||||
<metadata>
|
||||
<id>Oqtane.Shared</id>
|
||||
<version>6.1.0</version>
|
||||
<version>6.1.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/v6.1.0</releaseNotes>
|
||||
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v6.1.2</releaseNotes>
|
||||
<readme>readme.md</readme>
|
||||
<icon>icon.png</icon>
|
||||
<tags>oqtane</tags>
|
||||
|
@ -2,7 +2,7 @@
|
||||
<package>
|
||||
<metadata>
|
||||
<id>Oqtane.Updater</id>
|
||||
<version>6.1.0</version>
|
||||
<version>6.1.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/v6.1.0</releaseNotes>
|
||||
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v6.1.2</releaseNotes>
|
||||
<readme>readme.md</readme>
|
||||
<icon>icon.png</icon>
|
||||
<tags>oqtane</tags>
|
||||
|
@ -1 +1 @@
|
||||
Compress-Archive -Path "..\Oqtane.Server\bin\Release\net9.0\publish\*" -DestinationPath "Oqtane.Framework.6.1.0.Install.zip" -Force
|
||||
Compress-Archive -Path "..\Oqtane.Server\bin\Release\net9.0\publish\*" -DestinationPath "Oqtane.Framework.6.1.2.Install.zip" -Force
|
||||
|
@ -1 +1 @@
|
||||
Compress-Archive -Path "..\Oqtane.Server\bin\Release\net9.0\publish\*" -DestinationPath "Oqtane.Framework.6.1.0.Upgrade.zip" -Force
|
||||
Compress-Archive -Path "..\Oqtane.Server\bin\Release\net9.0\publish\*" -DestinationPath "Oqtane.Framework.6.1.2.Upgrade.zip" -Force
|
||||
|
@ -31,6 +31,8 @@
|
||||
@inject IUrlMappingRepository UrlMappingRepository
|
||||
@inject IVisitorRepository VisitorRepository
|
||||
@inject IJwtManager JwtManager
|
||||
@inject ICookieConsentService CookieConsentService
|
||||
@inject ISettingService SettingService
|
||||
|
||||
@if (_initialized)
|
||||
{
|
||||
@ -107,6 +109,7 @@
|
||||
private string _styleSheets = "";
|
||||
private string _scripts = "";
|
||||
private string _message = "";
|
||||
private bool _allowCookies;
|
||||
private PageState _pageState;
|
||||
|
||||
// CascadingParameter is required to access HttpContext
|
||||
@ -140,6 +143,9 @@
|
||||
_prerender = site.Prerender;
|
||||
_fingerprint = site.Fingerprint;
|
||||
|
||||
var cookieConsentSettings = SettingService.GetSetting(site.Settings, "CookieConsent", string.Empty);
|
||||
_allowCookies = string.IsNullOrEmpty(cookieConsentSettings) || await CookieConsentService.CanTrackAsync(cookieConsentSettings == "optout");
|
||||
|
||||
var modules = new List<Module>();
|
||||
|
||||
Route route = new Route(url, alias.Path);
|
||||
@ -170,7 +176,7 @@
|
||||
modules = await SiteService.GetModulesAsync(site.SiteId, page.PageId);
|
||||
}
|
||||
|
||||
if (site.VisitorTracking)
|
||||
if (site.VisitorTracking && _allowCookies)
|
||||
{
|
||||
TrackVisitor(site.SiteId);
|
||||
}
|
||||
@ -245,7 +251,8 @@
|
||||
ReturnUrl = "",
|
||||
IsInternalNavigation = false,
|
||||
RenderId = Guid.NewGuid(),
|
||||
Refresh = true
|
||||
Refresh = true,
|
||||
AllowCookies = _allowCookies
|
||||
};
|
||||
}
|
||||
else
|
||||
@ -757,7 +764,7 @@
|
||||
}
|
||||
|
||||
// ensure resource does not exist already
|
||||
if (!pageresources.Exists(item => item.Url.ToLower() == resource.Url.ToLower()))
|
||||
if (!pageresources.Exists(item => Utilities.GetUrlPath(item.Url).ToLower() == Utilities.GetUrlPath(resource.Url).ToLower()))
|
||||
{
|
||||
pageresources.Add(resource.Clone(level, name, fingerprint));
|
||||
}
|
||||
|
52
Oqtane.Server/Controllers/CookieConsentController.cs
Normal file
52
Oqtane.Server/Controllers/CookieConsentController.cs
Normal file
@ -0,0 +1,52 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Oqtane.Models;
|
||||
using Oqtane.Shared;
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using Oqtane.Infrastructure;
|
||||
using Oqtane.Services;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Oqtane.Controllers
|
||||
{
|
||||
[Route(ControllerRoutes.ApiRoute)]
|
||||
public class CookieConsentController : Controller
|
||||
{
|
||||
private readonly ICookieConsentService _cookieConsentService;
|
||||
|
||||
public CookieConsentController(ICookieConsentService cookieConsentService)
|
||||
{
|
||||
_cookieConsentService = cookieConsentService;
|
||||
}
|
||||
|
||||
[HttpGet("IsActioned")]
|
||||
public async Task<bool> IsActioned()
|
||||
{
|
||||
return await _cookieConsentService.IsActionedAsync();
|
||||
}
|
||||
|
||||
[HttpGet("CanTrack")]
|
||||
public async Task<bool> CanTrack(string optout)
|
||||
{
|
||||
return await _cookieConsentService.CanTrackAsync(bool.Parse(optout));
|
||||
}
|
||||
|
||||
[HttpGet("CreateActionedCookie")]
|
||||
public async Task<string> CreateActionedCookie()
|
||||
{
|
||||
return await _cookieConsentService.CreateActionedCookieAsync();
|
||||
}
|
||||
|
||||
[HttpGet("CreateConsentCookie")]
|
||||
public async Task<string> CreateConsentCookie()
|
||||
{
|
||||
return await _cookieConsentService.CreateConsentCookieAsync();
|
||||
}
|
||||
|
||||
[HttpGet("WithdrawConsentCookie")]
|
||||
public async Task<string> WithdrawConsentCookie()
|
||||
{
|
||||
return await _cookieConsentService.WithdrawConsentCookieAsync();
|
||||
}
|
||||
}
|
||||
}
|
56
Oqtane.Server/Controllers/EndpointController.cs
Normal file
56
Oqtane.Server/Controllers/EndpointController.cs
Normal file
@ -0,0 +1,56 @@
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Oqtane.Models;
|
||||
using Oqtane.Shared;
|
||||
using System.Linq;
|
||||
using Microsoft.AspNetCore.Mvc.Controllers;
|
||||
using Microsoft.AspNetCore.Routing;
|
||||
|
||||
namespace Oqtane.Controllers
|
||||
{
|
||||
[Route(ControllerRoutes.ApiRoute)]
|
||||
public class EndpointController : Controller
|
||||
{
|
||||
private readonly IEnumerable<EndpointDataSource> _endpointSources;
|
||||
|
||||
public EndpointController(IEnumerable<EndpointDataSource> endpointSources)
|
||||
{
|
||||
_endpointSources = endpointSources;
|
||||
}
|
||||
|
||||
// GET api/<controller>
|
||||
[HttpGet]
|
||||
[Authorize(Roles = RoleNames.Host)]
|
||||
public ActionResult Get()
|
||||
{
|
||||
var endpoints = _endpointSources
|
||||
.SelectMany(item => item.Endpoints)
|
||||
.OfType<RouteEndpoint>();
|
||||
|
||||
var output = endpoints.Select(
|
||||
item =>
|
||||
{
|
||||
var controller = item.Metadata
|
||||
.OfType<ControllerActionDescriptor>()
|
||||
.FirstOrDefault();
|
||||
var action = controller != null
|
||||
? $"{controller.ControllerName}.{controller.ActionName}"
|
||||
: null;
|
||||
var controllerMethod = controller != null
|
||||
? $"{controller.ControllerTypeInfo.FullName}:{controller.MethodInfo.Name}"
|
||||
: null;
|
||||
return new
|
||||
{
|
||||
Method = item.Metadata.OfType<HttpMethodMetadata>().FirstOrDefault()?.HttpMethods?[0],
|
||||
Route = $"/{item.RoutePattern.RawText.TrimStart('/')}",
|
||||
Action = action,
|
||||
ControllerMethod = controllerMethod
|
||||
};
|
||||
}
|
||||
).OrderBy(item => item.Route);
|
||||
|
||||
return Json(output);
|
||||
}
|
||||
}
|
||||
}
|
@ -23,6 +23,7 @@ using System.IO.Compression;
|
||||
using Oqtane.Services;
|
||||
using Microsoft.Extensions.Primitives;
|
||||
using Microsoft.AspNetCore.Http.HttpResults;
|
||||
using Microsoft.Net.Http.Headers;
|
||||
|
||||
// ReSharper disable StringIndexOfIsCultureSpecific.1
|
||||
|
||||
@ -735,6 +736,10 @@ namespace Oqtane.Controllers
|
||||
}
|
||||
if (!string.IsNullOrEmpty(imagepath))
|
||||
{
|
||||
if (!string.IsNullOrEmpty(file.Folder.CacheControl))
|
||||
{
|
||||
HttpContext.Response.Headers.Append(HeaderNames.CacheControl, value: file.Folder.CacheControl);
|
||||
}
|
||||
return PhysicalFile(imagepath, file.GetMimeType());
|
||||
}
|
||||
else
|
||||
|
@ -280,9 +280,14 @@ namespace Oqtane.Controllers
|
||||
var folder = _folders.GetFolder(id, false);
|
||||
if (folder != null && folder.SiteId == _alias.SiteId && _userPermissions.IsAuthorized(User, folder.SiteId, EntityNames.Folder, id, PermissionNames.Edit))
|
||||
{
|
||||
if (Directory.Exists(_folders.GetFolderPath(folder)))
|
||||
var folderPath = _folders.GetFolderPath(folder);
|
||||
if (Directory.Exists(folderPath))
|
||||
{
|
||||
Directory.Delete(_folders.GetFolderPath(folder));
|
||||
foreach (var filePath in Directory.GetFiles(folderPath))
|
||||
{
|
||||
System.IO.File.Delete(filePath);
|
||||
}
|
||||
Directory.Delete(folderPath);
|
||||
}
|
||||
_folders.DeleteFolder(id);
|
||||
_syncManager.AddSyncEvent(_alias, EntityNames.Folder, folder.FolderId, SyncEventActions.Delete);
|
||||
|
@ -88,10 +88,10 @@ namespace Oqtane.Controllers
|
||||
|
||||
[HttpGet("upgrade")]
|
||||
[Authorize(Roles = RoleNames.Host)]
|
||||
public Installation Upgrade()
|
||||
public Installation Upgrade(string backup)
|
||||
{
|
||||
var installation = new Installation { Success = true, Message = "" };
|
||||
_installationManager.UpgradeFramework();
|
||||
_installationManager.UpgradeFramework(bool.Parse(backup));
|
||||
return installation;
|
||||
}
|
||||
|
||||
@ -171,7 +171,8 @@ namespace Oqtane.Controllers
|
||||
}
|
||||
}
|
||||
return assemblyList;
|
||||
});
|
||||
}).ToList();
|
||||
|
||||
}
|
||||
|
||||
// GET api/<controller>/load?list=x,y
|
||||
|
@ -17,12 +17,12 @@ namespace Oqtane.Controllers
|
||||
_jobLogs = jobLogs;
|
||||
}
|
||||
|
||||
// GET: api/<controller>
|
||||
// GET: api/<controller>?jobid=x
|
||||
[HttpGet]
|
||||
[Authorize(Roles = RoleNames.Host)]
|
||||
public IEnumerable<JobLog> Get()
|
||||
public IEnumerable<JobLog> Get(string jobid)
|
||||
{
|
||||
return _jobLogs.GetJobLogs();
|
||||
return _jobLogs.GetJobLogs(int.Parse(jobid));
|
||||
}
|
||||
|
||||
// GET api/<controller>/5
|
||||
|
30
Oqtane.Server/Controllers/OutputCacheController.cs
Normal file
30
Oqtane.Server/Controllers/OutputCacheController.cs
Normal file
@ -0,0 +1,30 @@
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
using Oqtane.Models;
|
||||
using Oqtane.Services;
|
||||
using Oqtane.Shared;
|
||||
|
||||
namespace Oqtane.Controllers
|
||||
{
|
||||
[Route(ControllerRoutes.ApiRoute)]
|
||||
public class OutputCacheController : Controller
|
||||
{
|
||||
private readonly IOutputCacheService _cacheService;
|
||||
|
||||
public OutputCacheController(IOutputCacheService cacheService)
|
||||
{
|
||||
_cacheService = cacheService;
|
||||
}
|
||||
|
||||
// DELETE api/<controller>/{tag}
|
||||
[HttpDelete("{tag}")]
|
||||
[Authorize(Roles = RoleNames.Admin)]
|
||||
public async Task EvictByTag(string tag)
|
||||
{
|
||||
await _cacheService.EvictByTag(tag);
|
||||
}
|
||||
}
|
||||
}
|
@ -90,33 +90,26 @@ namespace Oqtane.Controllers
|
||||
package = await GetJson<Package>(client, url + $"/api/registry/package/?id={_configManager.GetInstallationId()}&package={packageid}&version={version}&download={download}&email={WebUtility.UrlEncode(GetPackageRegistryEmail())}");
|
||||
}
|
||||
|
||||
if (package != null)
|
||||
if (package != null && bool.Parse(install))
|
||||
{
|
||||
if (bool.Parse(install))
|
||||
using (var httpClient = new HttpClient())
|
||||
{
|
||||
using (var httpClient = new HttpClient())
|
||||
var folder = Path.Combine(_environment.ContentRootPath, Constants.PackagesFolder);
|
||||
var response = await httpClient.GetAsync(package.PackageUrl).ConfigureAwait(false);
|
||||
if (response.IsSuccessStatusCode)
|
||||
{
|
||||
var folder = Path.Combine(_environment.ContentRootPath, Constants.PackagesFolder);
|
||||
var response = await httpClient.GetAsync(package.PackageUrl).ConfigureAwait(false);
|
||||
if (response.IsSuccessStatusCode)
|
||||
string filename = packageid + "." + version + ".nupkg";
|
||||
using (var fileStream = new FileStream(Path.Combine(Constants.PackagesFolder, filename), FileMode.Create, FileAccess.Write, FileShare.None))
|
||||
{
|
||||
string filename = packageid + "." + version + ".nupkg";
|
||||
using (var fileStream = new FileStream(Path.Combine(Constants.PackagesFolder, filename), FileMode.Create, FileAccess.Write, FileShare.None))
|
||||
{
|
||||
await response.Content.CopyToAsync(fileStream).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.Log(LogLevel.Error, this, LogFunction.Create, "Could Not Download {PackageUrl}", package.PackageUrl);
|
||||
await response.Content.CopyToAsync(fileStream).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.Log(LogLevel.Error, this, LogFunction.Create, "Could Not Download {PackageUrl}", package.PackageUrl);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.Log(LogLevel.Error, this, LogFunction.Create, "Package {PackageId}.{Version} Is Not Registered In The Marketplace", packageid, version);
|
||||
}
|
||||
}
|
||||
return package;
|
||||
}
|
||||
|
@ -34,7 +34,7 @@ namespace Oqtane.Controllers
|
||||
int SiteId;
|
||||
if (int.TryParse(siteid, out SiteId) && SiteId == _alias.SiteId)
|
||||
{
|
||||
return _visitors.GetVisitors(SiteId, DateTime.ParseExact(fromdate, "yyyy-MM-dd", CultureInfo.InvariantCulture));
|
||||
return _visitors.GetVisitors(SiteId, DateTime.ParseExact(fromdate, "yyyy-MM-dd", CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal | DateTimeStyles.AdjustToUniversal));
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -34,6 +34,14 @@ namespace Oqtane.Extensions
|
||||
options.SetDefaultCulture(defaultCulture)
|
||||
.AddSupportedCultures(supportedCultures)
|
||||
.AddSupportedUICultures(supportedCultures);
|
||||
|
||||
foreach(var culture in options.SupportedCultures)
|
||||
{
|
||||
if (culture.TextInfo.IsRightToLeft)
|
||||
{
|
||||
RightToLeftCulture.ResolveFormat(culture);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return app;
|
||||
@ -47,6 +55,5 @@ namespace Oqtane.Extensions
|
||||
|
||||
public static IApplicationBuilder UseExceptionMiddleWare(this IApplicationBuilder builder)
|
||||
=> builder.UseMiddleware<ExceptionMiddleware>();
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -103,6 +103,7 @@ namespace Microsoft.Extensions.DependencyInjection
|
||||
services.AddScoped<ISearchService, SearchService>();
|
||||
services.AddScoped<ISearchProvider, DatabaseSearchProvider>();
|
||||
services.AddScoped<IImageService, ImageService>();
|
||||
services.AddScoped<ICookieConsentService, ServerCookieConsentService>();
|
||||
|
||||
// providers
|
||||
services.AddScoped<ITextEditor, Oqtane.Modules.Controls.QuillJSTextEditor>();
|
||||
@ -116,6 +117,7 @@ namespace Microsoft.Extensions.DependencyInjection
|
||||
// services
|
||||
services.AddTransient<ISiteService, ServerSiteService>();
|
||||
services.AddTransient<ILocalizationCookieService, ServerLocalizationCookieService>();
|
||||
services.AddTransient<IOutputCacheService, ServerOutputCacheService>();
|
||||
|
||||
// repositories
|
||||
services.AddTransient<IModuleDefinitionRepository, ModuleDefinitionRepository>();
|
||||
|
@ -532,8 +532,9 @@ namespace Oqtane.Extensions
|
||||
// external roles
|
||||
if (claimsPrincipal.Claims.Any(item => item.Type == httpContext.GetSiteSettings().GetValue("ExternalLogin:RoleClaimType", "")))
|
||||
{
|
||||
var _roles = httpContext.RequestServices.GetRequiredService<IRoleRepository>();
|
||||
var roles = _roles.GetRoles(user.SiteId).ToList(); // global roles excluded ie. host users cannot be added/deleted
|
||||
var _roles = httpContext.RequestServices.GetRequiredService<IRoleRepository>();
|
||||
var allowhostrole = bool.Parse(httpContext.GetSiteSettings().GetValue("ExternalLogin:AllowHostRole", "false"));
|
||||
var roles = _roles.GetRoles(user.SiteId, allowhostrole).ToList();
|
||||
|
||||
var mappings = httpContext.GetSiteSettings().GetValue("ExternalLogin:RoleClaimMappings", "").Split(',');
|
||||
foreach (var claim in claimsPrincipal.Claims.Where(item => item.Type == httpContext.GetSiteSettings().GetValue("ExternalLogin:RoleClaimType", "")))
|
||||
@ -583,8 +584,9 @@ namespace Oqtane.Extensions
|
||||
}
|
||||
}
|
||||
|
||||
var userrole = userRoles.FirstOrDefault(item => item.Role.Name == RoleNames.Registered);
|
||||
if (!user.IsDeleted && userrole != null && Utilities.IsEffectiveAndNotExpired(userrole.EffectiveDate, userrole.ExpiryDate))
|
||||
var host = userRoles.FirstOrDefault(item => item.Role.Name == RoleNames.Host);
|
||||
var registered = userRoles.FirstOrDefault(item => item.Role.Name == RoleNames.Registered);
|
||||
if (!user.IsDeleted && (host != null || registered != null && Utilities.IsEffectiveAndNotExpired(registered.EffectiveDate, registered.ExpiryDate)))
|
||||
{
|
||||
// update user
|
||||
user.LastLoginOn = DateTime.UtcNow;
|
||||
|
@ -731,6 +731,7 @@ namespace Oqtane.Infrastructure
|
||||
{
|
||||
_configManager.AddOrUpdateSetting($"{SettingKeys.DatabaseSection}:{SettingKeys.DatabaseTypeKey}", Constants.DefaultDBType, true);
|
||||
}
|
||||
|
||||
if (!_configManager.GetSection(SettingKeys.AvailableDatabasesSection).Exists())
|
||||
{
|
||||
string databases = "[";
|
||||
@ -742,6 +743,19 @@ namespace Oqtane.Infrastructure
|
||||
databases += "]";
|
||||
_configManager.AddOrUpdateSetting(SettingKeys.AvailableDatabasesSection, databases, true);
|
||||
}
|
||||
var availabledatabases = _configManager.GetSection(SettingKeys.AvailableDatabasesSection).GetChildren();
|
||||
if (!availabledatabases.Any(item => item.GetSection("Name").Value == "Azure SQL"))
|
||||
{
|
||||
// Azure SQL added in 6.1.2
|
||||
string databases = "[";
|
||||
foreach (var database in availabledatabases)
|
||||
{
|
||||
databases += "{ " + $"\"Name\": \"{database["Name"]}\", \"ControlType\": \"{database["ControlType"]}\", \"DBTYpe\": \"{database["DBType"]}\"" + " },";
|
||||
}
|
||||
databases += "{ \"Name\": \"Azure SQL\", \"ControlType\": \"Oqtane.Installer.Controls.AzureSqlConfig, Oqtane.Client\", \"DBTYpe\": \"Oqtane.Database.SqlServer.SqlServerDatabase, Oqtane.Database.SqlServer\" }";
|
||||
databases += "]";
|
||||
_configManager.AddOrUpdateSetting(SettingKeys.AvailableDatabasesSection, databases, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -380,7 +380,7 @@ namespace Oqtane.Infrastructure
|
||||
File.WriteAllText(assemblyLogPath, JsonSerializer.Serialize(assemblies, new JsonSerializerOptions { WriteIndented = true }));
|
||||
}
|
||||
|
||||
public async Task UpgradeFramework()
|
||||
public async Task UpgradeFramework(bool backup)
|
||||
{
|
||||
string folder = Path.Combine(_environment.ContentRootPath, Constants.PackagesFolder);
|
||||
if (Directory.Exists(folder))
|
||||
@ -448,14 +448,14 @@ namespace Oqtane.Infrastructure
|
||||
// install Oqtane.Upgrade zip package
|
||||
if (File.Exists(upgradepackage))
|
||||
{
|
||||
FinishUpgrade();
|
||||
FinishUpgrade(backup);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void FinishUpgrade()
|
||||
private void FinishUpgrade(bool backup)
|
||||
{
|
||||
// check if updater application exists
|
||||
string Updater = Constants.UpdaterPackageId + ".dll";
|
||||
@ -469,7 +469,7 @@ namespace Oqtane.Infrastructure
|
||||
{
|
||||
WorkingDirectory = folder,
|
||||
FileName = "dotnet",
|
||||
Arguments = Path.Combine(folder, Updater) + " \"" + _environment.ContentRootPath + "\" \"" + _environment.WebRootPath + "\"",
|
||||
Arguments = Path.Combine(folder, Updater) + " \"" + _environment.ContentRootPath + "\" \"" + _environment.WebRootPath + "\" \"" + backup.ToString() + "\"",
|
||||
UseShellExecute = false,
|
||||
ErrorDialog = false,
|
||||
CreateNoWindow = true,
|
||||
|
@ -7,7 +7,7 @@ namespace Oqtane.Infrastructure
|
||||
void InstallPackages();
|
||||
bool UninstallPackage(string PackageName);
|
||||
int RegisterAssemblies();
|
||||
Task UpgradeFramework();
|
||||
Task UpgradeFramework(bool backup);
|
||||
void RestartApplication();
|
||||
}
|
||||
}
|
||||
|
@ -3,6 +3,7 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Microsoft.Extensions.Logging;
|
||||
@ -51,104 +52,135 @@ namespace Oqtane.Infrastructure
|
||||
{
|
||||
using (var scope = _serviceScopeFactory.CreateScope())
|
||||
{
|
||||
IConfigurationRoot _config = scope.ServiceProvider.GetRequiredService<IConfigurationRoot>();
|
||||
ILogger<HostedServiceBase> _filelogger = scope.ServiceProvider.GetRequiredService<ILogger<HostedServiceBase>>();
|
||||
|
||||
try
|
||||
// if framework is installed
|
||||
if (IsInstalled(_config))
|
||||
{
|
||||
var jobs = scope.ServiceProvider.GetRequiredService<IJobRepository>();
|
||||
var jobLogs = scope.ServiceProvider.GetRequiredService<IJobLogRepository>();
|
||||
var tenantRepository = scope.ServiceProvider.GetRequiredService<ITenantRepository>();
|
||||
var tenantManager = scope.ServiceProvider.GetRequiredService<ITenantManager>();
|
||||
|
||||
// get name of job
|
||||
string jobType = Utilities.GetFullTypeName(GetType().AssemblyQualifiedName);
|
||||
|
||||
// load jobs and find current job
|
||||
Job job = jobs.GetJobs().Where(item => item.JobType == jobType).FirstOrDefault();
|
||||
if (job != null && job.IsEnabled && !job.IsExecuting)
|
||||
try
|
||||
{
|
||||
// get next execution date
|
||||
DateTime NextExecution;
|
||||
if (job.NextExecution == null)
|
||||
var jobs = scope.ServiceProvider.GetRequiredService<IJobRepository>();
|
||||
|
||||
// get name of job
|
||||
string jobTypeName = Utilities.GetFullTypeName(GetType().AssemblyQualifiedName);
|
||||
|
||||
// load jobs and find current job
|
||||
Job job = jobs.GetJobs().Where(item => item.JobType == jobTypeName).FirstOrDefault();
|
||||
|
||||
if (job == null)
|
||||
{
|
||||
if (job.StartDate != null)
|
||||
// auto registration
|
||||
job = new Job { JobType = jobTypeName };
|
||||
|
||||
// optional HostedServiceBase properties
|
||||
var jobType = Type.GetType(jobTypeName);
|
||||
var jobObject = ActivatorUtilities.CreateInstance(scope.ServiceProvider, jobType) as HostedServiceBase;
|
||||
if (jobObject.Name != "")
|
||||
{
|
||||
NextExecution = job.StartDate.Value;
|
||||
job.Name = jobObject.Name;
|
||||
}
|
||||
else
|
||||
{
|
||||
NextExecution = DateTime.UtcNow;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
NextExecution = job.NextExecution.Value;
|
||||
}
|
||||
|
||||
// determine if the job should be run
|
||||
if (NextExecution <= DateTime.UtcNow && (job.EndDate == null || job.EndDate >= DateTime.UtcNow))
|
||||
{
|
||||
// update the job to indicate it is running
|
||||
job.IsExecuting = true;
|
||||
jobs.UpdateJob(job);
|
||||
|
||||
// create a job log entry
|
||||
JobLog log = new JobLog();
|
||||
log.JobId = job.JobId;
|
||||
log.StartDate = DateTime.UtcNow;
|
||||
log.FinishDate = null;
|
||||
log.Succeeded = false;
|
||||
log.Notes = "";
|
||||
log = jobLogs.AddJobLog(log);
|
||||
|
||||
// execute the job
|
||||
try
|
||||
{
|
||||
var notes = "";
|
||||
foreach (var tenant in tenantRepository.GetTenants())
|
||||
{
|
||||
// set tenant and execute job
|
||||
tenantManager.SetTenant(tenant.TenantId);
|
||||
notes += ExecuteJob(scope.ServiceProvider);
|
||||
notes += await ExecuteJobAsync(scope.ServiceProvider);
|
||||
}
|
||||
log.Notes = notes;
|
||||
log.Succeeded = true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
log.Notes = ex.Message;
|
||||
log.Succeeded = false;
|
||||
}
|
||||
|
||||
// update the job log
|
||||
log.FinishDate = DateTime.UtcNow;
|
||||
jobLogs.UpdateJobLog(log);
|
||||
|
||||
// update the job
|
||||
job.NextExecution = CalculateNextExecution(NextExecution, job);
|
||||
if (job.Frequency == "O") // one time
|
||||
{
|
||||
job.EndDate = DateTime.UtcNow;
|
||||
job.NextExecution = null;
|
||||
job.Name = Utilities.GetTypeName(job.JobType);
|
||||
}
|
||||
job.Frequency = jobObject.Frequency;
|
||||
job.Interval = jobObject.Interval;
|
||||
job.StartDate = jobObject.StartDate;
|
||||
job.EndDate = jobObject.EndDate;
|
||||
job.RetentionHistory = jobObject.RetentionHistory;
|
||||
job.IsEnabled = jobObject.IsEnabled;
|
||||
job.IsStarted = true;
|
||||
job.IsExecuting = false;
|
||||
jobs.UpdateJob(job);
|
||||
job.NextExecution = null;
|
||||
|
||||
// trim the job log
|
||||
List<JobLog> logs = jobLogs.GetJobLogs().Where(item => item.JobId == job.JobId)
|
||||
.OrderByDescending(item => item.JobLogId).ToList();
|
||||
for (int i = logs.Count; i > job.RetentionHistory; i--)
|
||||
job = jobs.AddJob(job);
|
||||
}
|
||||
|
||||
if (job != null && job.IsEnabled && !job.IsExecuting)
|
||||
{
|
||||
var jobLogs = scope.ServiceProvider.GetRequiredService<IJobLogRepository>();
|
||||
var tenantRepository = scope.ServiceProvider.GetRequiredService<ITenantRepository>();
|
||||
var tenantManager = scope.ServiceProvider.GetRequiredService<ITenantManager>();
|
||||
|
||||
// get next execution date
|
||||
DateTime NextExecution;
|
||||
if (job.NextExecution == null)
|
||||
{
|
||||
jobLogs.DeleteJobLog(logs[i - 1].JobLogId);
|
||||
if (job.StartDate != null)
|
||||
{
|
||||
NextExecution = job.StartDate.Value;
|
||||
}
|
||||
else
|
||||
{
|
||||
NextExecution = DateTime.UtcNow;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
NextExecution = job.NextExecution.Value;
|
||||
}
|
||||
|
||||
// determine if the job should be run
|
||||
if (NextExecution <= DateTime.UtcNow && (job.EndDate == null || job.EndDate >= DateTime.UtcNow))
|
||||
{
|
||||
// update the job to indicate it is running
|
||||
job.IsExecuting = true;
|
||||
jobs.UpdateJob(job);
|
||||
|
||||
// create a job log entry
|
||||
JobLog log = new JobLog();
|
||||
log.JobId = job.JobId;
|
||||
log.StartDate = DateTime.UtcNow;
|
||||
log.FinishDate = null;
|
||||
log.Succeeded = false;
|
||||
log.Notes = "";
|
||||
log = jobLogs.AddJobLog(log);
|
||||
|
||||
// execute the job
|
||||
try
|
||||
{
|
||||
var notes = "";
|
||||
foreach (var tenant in tenantRepository.GetTenants())
|
||||
{
|
||||
// set tenant and execute job
|
||||
tenantManager.SetTenant(tenant.TenantId);
|
||||
notes += ExecuteJob(scope.ServiceProvider);
|
||||
notes += await ExecuteJobAsync(scope.ServiceProvider);
|
||||
}
|
||||
log.Notes = notes;
|
||||
log.Succeeded = true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
log.Notes = ex.Message;
|
||||
log.Succeeded = false;
|
||||
}
|
||||
|
||||
// update the job log
|
||||
log.FinishDate = DateTime.UtcNow;
|
||||
jobLogs.UpdateJobLog(log);
|
||||
|
||||
// update the job
|
||||
job.NextExecution = CalculateNextExecution(NextExecution, job);
|
||||
if (job.Frequency == "O") // one time
|
||||
{
|
||||
job.EndDate = DateTime.UtcNow;
|
||||
job.NextExecution = null;
|
||||
}
|
||||
job.IsExecuting = false;
|
||||
jobs.UpdateJob(job);
|
||||
|
||||
// trim the job log
|
||||
List<JobLog> logs = jobLogs.GetJobLogs(job.JobId).ToList();
|
||||
for (int i = logs.Count; i > job.RetentionHistory; i--)
|
||||
{
|
||||
jobLogs.DeleteJobLog(logs[i - 1].JobLogId);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// can occur during the initial installation because the database has not yet been created
|
||||
if (!ex.Message.Contains("No database provider has been configured for this DbContext"))
|
||||
catch (Exception ex)
|
||||
{
|
||||
_filelogger.LogError(Utilities.LogMessage(this, $"An Error Occurred Executing Scheduled Job: {Name} - {ex}"));
|
||||
}
|
||||
@ -208,55 +240,28 @@ namespace Oqtane.Infrastructure
|
||||
{
|
||||
using (var scope = _serviceScopeFactory.CreateScope())
|
||||
{
|
||||
IConfigurationRoot _config = scope.ServiceProvider.GetRequiredService<IConfigurationRoot>();
|
||||
ILogger<HostedServiceBase> _filelogger = scope.ServiceProvider.GetRequiredService<ILogger<HostedServiceBase>>();
|
||||
|
||||
try
|
||||
{
|
||||
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)
|
||||
if (IsInstalled(_config))
|
||||
{
|
||||
// reset in case this job was forcefully terminated previously
|
||||
job.IsStarted = true;
|
||||
job.IsExecuting = false;
|
||||
jobs.UpdateJob(job);
|
||||
}
|
||||
else
|
||||
{
|
||||
// auto registration - job will not run on initial installation due to no DBContext but will run after restart
|
||||
job = new Job { JobType = jobTypeName };
|
||||
|
||||
// optional HostedServiceBase properties
|
||||
var jobType = Type.GetType(jobTypeName);
|
||||
var jobObject = ActivatorUtilities.CreateInstance(scope.ServiceProvider, jobType) as HostedServiceBase;
|
||||
if (jobObject.Name != "")
|
||||
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)
|
||||
{
|
||||
job.Name = jobObject.Name;
|
||||
// reset in case this job was forcefully terminated previously
|
||||
job.IsStarted = true;
|
||||
job.IsExecuting = false;
|
||||
jobs.UpdateJob(job);
|
||||
}
|
||||
else
|
||||
{
|
||||
job.Name = Utilities.GetTypeName(job.JobType);
|
||||
}
|
||||
job.Frequency = jobObject.Frequency;
|
||||
job.Interval = jobObject.Interval;
|
||||
job.StartDate = jobObject.StartDate;
|
||||
job.EndDate = jobObject.EndDate;
|
||||
job.RetentionHistory = jobObject.RetentionHistory;
|
||||
job.IsEnabled = jobObject.IsEnabled;
|
||||
job.IsStarted = true;
|
||||
job.IsExecuting = false;
|
||||
job.NextExecution = null;
|
||||
jobs.AddJob(job);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// can occur during the initial installation because the database has not yet been created
|
||||
if (!ex.Message.Contains("No database provider has been configured for this DbContext"))
|
||||
{
|
||||
_filelogger.LogError(Utilities.LogMessage(this, $"An Error Occurred Starting Scheduled Job: {Name} - {ex}"));
|
||||
}
|
||||
_filelogger.LogError(Utilities.LogMessage(this, $"An Error Occurred Starting Scheduled Job: {Name} - {ex}"));
|
||||
}
|
||||
}
|
||||
|
||||
@ -314,6 +319,11 @@ namespace Oqtane.Infrastructure
|
||||
}
|
||||
}
|
||||
|
||||
private bool IsInstalled(IConfigurationRoot config)
|
||||
{
|
||||
return !string.IsNullOrEmpty(config.GetConnectionString(SettingKeys.ConnectionStringKey));
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_cancellationTokenSource.Cancel();
|
||||
|
@ -130,7 +130,12 @@ namespace Oqtane.Infrastructure
|
||||
mailMessage.Subject = notification.Subject;
|
||||
|
||||
//body
|
||||
mailMessage.Body = notification.Body.Replace("\n", "<br />");
|
||||
mailMessage.Body = notification.Body;
|
||||
if (!mailMessage.Body.Contains("<") || !mailMessage.Body.Contains(">"))
|
||||
{
|
||||
// plain text messages should convert line breaks to HTML tags to preserve formatting
|
||||
mailMessage.Body = mailMessage.Body.Replace("\n", "<br />");
|
||||
}
|
||||
|
||||
// encoding
|
||||
mailMessage.SubjectEncoding = System.Text.Encoding.UTF8;
|
||||
|
@ -39,7 +39,7 @@ namespace Oqtane.Infrastructure
|
||||
List<Site> sites = siteRepository.GetSites().ToList();
|
||||
foreach (Site site in sites)
|
||||
{
|
||||
log += "Processing Site: " + site.Name + "<br />";
|
||||
log += "<br />Processing Site: " + site.Name + "<br />";
|
||||
int retention;
|
||||
int count;
|
||||
|
||||
@ -118,11 +118,11 @@ namespace Oqtane.Infrastructure
|
||||
try
|
||||
{
|
||||
var assemblies = installationManager.RegisterAssemblies();
|
||||
log += assemblies.ToString() + " Assemblies Registered<br />";
|
||||
log += "<br />" + assemblies.ToString() + " Assemblies Registered<br />";
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
log += $"Error Registering Assemblies - {ex.Message}<br />";
|
||||
log += $"<br />Error Registering Assemblies - {ex.Message}<br />";
|
||||
}
|
||||
|
||||
return log;
|
||||
|
@ -0,0 +1,42 @@
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.Reflection;
|
||||
|
||||
namespace Oqtane.Infrastructure
|
||||
{
|
||||
public class RightToLeftCulture
|
||||
{
|
||||
public static CultureInfo ResolveFormat(CultureInfo cultureInfo)
|
||||
{
|
||||
SetNumberFormatInfo(cultureInfo.NumberFormat);
|
||||
SetCalenar(cultureInfo);
|
||||
|
||||
return cultureInfo;
|
||||
}
|
||||
|
||||
private static void SetCalenar(CultureInfo cultureInfo)
|
||||
{
|
||||
var calendar = new RightToLeftCultureCalendar();
|
||||
|
||||
var fieldInfo = cultureInfo.GetType().GetField("_calendar", BindingFlags.NonPublic | BindingFlags.Instance);
|
||||
if (fieldInfo != null)
|
||||
{
|
||||
fieldInfo.SetValue(cultureInfo, calendar);
|
||||
}
|
||||
|
||||
var info = cultureInfo.DateTimeFormat.GetType().GetField("calendar", BindingFlags.NonPublic | BindingFlags.Instance);
|
||||
if (info != null)
|
||||
{
|
||||
info.SetValue(cultureInfo.DateTimeFormat, calendar);
|
||||
}
|
||||
}
|
||||
|
||||
public static void SetNumberFormatInfo(NumberFormatInfo persianNumberFormatInfo)
|
||||
{
|
||||
persianNumberFormatInfo.NumberDecimalSeparator = ".";
|
||||
persianNumberFormatInfo.DigitSubstitution = DigitShapes.NativeNational;
|
||||
persianNumberFormatInfo.NumberNegativePattern = 0;
|
||||
persianNumberFormatInfo.NegativeSign = "-";
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,77 @@
|
||||
using System;
|
||||
|
||||
namespace Oqtane.Infrastructure
|
||||
{
|
||||
public class RightToLeftCultureCalendar : System.Globalization.PersianCalendar
|
||||
{
|
||||
public override int GetYear(DateTime time)
|
||||
{
|
||||
try
|
||||
{
|
||||
return base.GetYear(time);
|
||||
}
|
||||
catch
|
||||
{
|
||||
// ignore
|
||||
}
|
||||
|
||||
return time.Year;
|
||||
}
|
||||
|
||||
public override int GetMonth(DateTime time)
|
||||
{
|
||||
try
|
||||
{
|
||||
return base.GetMonth(time);
|
||||
}
|
||||
catch
|
||||
{
|
||||
// ignore
|
||||
}
|
||||
|
||||
return time.Month;
|
||||
}
|
||||
|
||||
public override int GetDayOfMonth(DateTime time)
|
||||
{
|
||||
try
|
||||
{
|
||||
return base.GetDayOfMonth(time);
|
||||
}
|
||||
catch
|
||||
{
|
||||
// ignore
|
||||
}
|
||||
|
||||
return time.Day;
|
||||
}
|
||||
|
||||
public override int GetDayOfYear(DateTime time)
|
||||
{
|
||||
try
|
||||
{
|
||||
return base.GetDayOfYear(time);
|
||||
}
|
||||
catch
|
||||
{
|
||||
// ignore
|
||||
}
|
||||
|
||||
return time.DayOfYear;
|
||||
}
|
||||
|
||||
public override DayOfWeek GetDayOfWeek(DateTime time)
|
||||
{
|
||||
try
|
||||
{
|
||||
return base.GetDayOfWeek(time);
|
||||
}
|
||||
catch
|
||||
{
|
||||
// ignore
|
||||
}
|
||||
|
||||
return time.DayOfWeek;
|
||||
}
|
||||
}
|
||||
}
|
@ -74,8 +74,21 @@ namespace Oqtane.Infrastructure
|
||||
// handle robots.txt root request (does not support subfolder aliases)
|
||||
if (context.Request.Path.StartsWithSegments("/robots.txt") && string.IsNullOrEmpty(alias.Path))
|
||||
{
|
||||
// allow all user agents and specify site map
|
||||
var robots = $"User-agent: *\n\nSitemap: {context.Request.Scheme}://{alias.Name}/sitemap.xml";
|
||||
string robots = "";
|
||||
if (sitesettings.ContainsKey("Robots") && !string.IsNullOrEmpty(sitesettings["Robots"]))
|
||||
{
|
||||
robots = sitesettings["Robots"];
|
||||
}
|
||||
else
|
||||
{
|
||||
// allow all user agents by default
|
||||
robots = $"User-agent: *";
|
||||
}
|
||||
if (!robots.ToLower().Contains("Sitemap:"))
|
||||
{
|
||||
// add sitemap if not specified
|
||||
robots += $"\n\nSitemap: {context.Request.Scheme}://{alias.Name}/sitemap.xml";
|
||||
}
|
||||
context.Response.ContentType = "text/plain";
|
||||
await context.Response.WriteAsync(robots);
|
||||
return;
|
||||
|
@ -1,14 +1,21 @@
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.Extensions.Localization;
|
||||
using Oqtane.Documentation;
|
||||
using Oqtane.Infrastructure;
|
||||
using Oqtane.Models;
|
||||
using Oqtane.Shared;
|
||||
|
||||
namespace Oqtane.SiteTemplates
|
||||
namespace Oqtane.Infrastructure.SiteTemplates
|
||||
{
|
||||
[PrivateApi("Mark Site-Template classes as private, since it's not very useful in the public docs")]
|
||||
public class AdminSiteTemplate : ISiteTemplate
|
||||
{
|
||||
private readonly IStringLocalizer<AdminSiteTemplate> _localizer;
|
||||
|
||||
public AdminSiteTemplate(IStringLocalizer<AdminSiteTemplate> localizer)
|
||||
{
|
||||
_localizer = localizer;
|
||||
}
|
||||
|
||||
public string Name
|
||||
{
|
||||
get { return "Admin Site Template"; }
|
||||
@ -169,12 +176,74 @@ namespace Oqtane.SiteTemplates
|
||||
}
|
||||
});
|
||||
|
||||
pageTemplates.Add(new PageTemplate
|
||||
{
|
||||
Name = "Privacy",
|
||||
Parent = "",
|
||||
Path = "privacy",
|
||||
Order = seed + 11,
|
||||
Icon = Icons.Eye,
|
||||
IsNavigation = false,
|
||||
IsPersonalizable = false,
|
||||
PermissionList = new List<Permission>
|
||||
{
|
||||
new Permission(PermissionNames.View, RoleNames.Everyone, true),
|
||||
new Permission(PermissionNames.View, RoleNames.Admin, true),
|
||||
new Permission(PermissionNames.Edit, RoleNames.Admin, true)
|
||||
},
|
||||
PageTemplateModules = new List<PageTemplateModule>
|
||||
{
|
||||
new PageTemplateModule { ModuleDefinitionName = "Oqtane.Modules.HtmlText, Oqtane.Client", Title = "Privacy Policy", Pane = PaneNames.Default,
|
||||
PermissionList = new List<Permission> {
|
||||
new Permission(PermissionNames.View, RoleNames.Everyone, true),
|
||||
new Permission(PermissionNames.View, RoleNames.Admin, true),
|
||||
new Permission(PermissionNames.Edit, RoleNames.Admin, true)
|
||||
},
|
||||
Settings = new List<Setting> {
|
||||
new Setting { SettingName = "DynamicTokens", SettingValue = "true" }
|
||||
},
|
||||
Content = _localizer["Privacy"]
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
pageTemplates.Add(new PageTemplate
|
||||
{
|
||||
Name = "Terms",
|
||||
Parent = "",
|
||||
Path = "terms",
|
||||
Order = seed + 13,
|
||||
Icon = Icons.List,
|
||||
IsNavigation = false,
|
||||
IsPersonalizable = false,
|
||||
PermissionList = new List<Permission>
|
||||
{
|
||||
new Permission(PermissionNames.View, RoleNames.Everyone, true),
|
||||
new Permission(PermissionNames.View, RoleNames.Admin, true),
|
||||
new Permission(PermissionNames.Edit, RoleNames.Admin, true)
|
||||
},
|
||||
PageTemplateModules = new List<PageTemplateModule>
|
||||
{
|
||||
new PageTemplateModule { ModuleDefinitionName = "Oqtane.Modules.HtmlText, Oqtane.Client", Title = "Terms of Use", Pane = PaneNames.Default,
|
||||
PermissionList = new List<Permission> {
|
||||
new Permission(PermissionNames.View, RoleNames.Everyone, true),
|
||||
new Permission(PermissionNames.View, RoleNames.Admin, true),
|
||||
new Permission(PermissionNames.Edit, RoleNames.Admin, true)
|
||||
},
|
||||
Settings = new List<Setting> {
|
||||
new Setting { SettingName = "DynamicTokens", SettingValue = "true" }
|
||||
},
|
||||
Content = _localizer["Terms"]
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
pageTemplates.Add(new PageTemplate
|
||||
{
|
||||
Name = "Not Found",
|
||||
Parent = "",
|
||||
Path = "404",
|
||||
Order = seed + 11,
|
||||
Order = seed + 15,
|
||||
Icon = Icons.X,
|
||||
IsNavigation = false,
|
||||
IsPersonalizable = false,
|
||||
|
@ -2,12 +2,11 @@ using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Oqtane.Documentation;
|
||||
using Oqtane.Infrastructure;
|
||||
using Oqtane.Models;
|
||||
using Oqtane.Repository;
|
||||
using Oqtane.Shared;
|
||||
|
||||
namespace Oqtane.SiteTemplates
|
||||
namespace Oqtane.Infrastructure.SiteTemplates
|
||||
{
|
||||
[PrivateApi("Mark Site-Template classes as private, since it's not very useful in the public docs")]
|
||||
public class DefaultSiteTemplate : ISiteTemplate
|
||||
|
@ -1,10 +1,9 @@
|
||||
using System.Collections.Generic;
|
||||
using Oqtane.Documentation;
|
||||
using Oqtane.Infrastructure;
|
||||
using Oqtane.Models;
|
||||
using Oqtane.Shared;
|
||||
|
||||
namespace Oqtane.SiteTemplates
|
||||
namespace Oqtane.Infrastructure.SiteTemplates
|
||||
{
|
||||
[PrivateApi("Mark Site-Template classes as private, since it's not very useful in the public docs")]
|
||||
public class EmptySiteTemplate : ISiteTemplate
|
||||
|
@ -1,7 +1,9 @@
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Microsoft.Extensions.Localization;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Oqtane.Infrastructure.SiteTemplates;
|
||||
using Oqtane.Models;
|
||||
using Oqtane.Repository;
|
||||
using Oqtane.Shared;
|
||||
@ -75,6 +77,9 @@ namespace Oqtane.Infrastructure
|
||||
case "6.1.0":
|
||||
Upgrade_6_1_0(tenant, scope);
|
||||
break;
|
||||
case "6.1.1":
|
||||
Upgrade_6_1_1(tenant, scope);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -457,6 +462,77 @@ namespace Oqtane.Infrastructure
|
||||
RemoveAssemblies(tenant, assemblies, "6.1.0");
|
||||
}
|
||||
|
||||
private void Upgrade_6_1_1(Tenant tenant, IServiceScope scope)
|
||||
{
|
||||
var localizer = scope.ServiceProvider.GetRequiredService<IStringLocalizer<AdminSiteTemplate>>();
|
||||
|
||||
var pageTemplates = new List<PageTemplate>
|
||||
{
|
||||
new PageTemplate
|
||||
{
|
||||
Name = "Privacy",
|
||||
Parent = "",
|
||||
Path = "privacy",
|
||||
Order = 1011,
|
||||
Icon = Icons.Eye,
|
||||
IsNavigation = false,
|
||||
IsPersonalizable = false,
|
||||
PermissionList = new List<Permission>
|
||||
{
|
||||
new Permission(PermissionNames.View, RoleNames.Everyone, true),
|
||||
new Permission(PermissionNames.View, RoleNames.Admin, true),
|
||||
new Permission(PermissionNames.Edit, RoleNames.Admin, true)
|
||||
},
|
||||
PageTemplateModules = new List<PageTemplateModule>
|
||||
{
|
||||
new PageTemplateModule { ModuleDefinitionName = "Oqtane.Modules.HtmlText, Oqtane.Client", Title = "Privacy Policy", Pane = PaneNames.Default,
|
||||
PermissionList = new List<Permission> {
|
||||
new Permission(PermissionNames.View, RoleNames.Everyone, true),
|
||||
new Permission(PermissionNames.View, RoleNames.Admin, true),
|
||||
new Permission(PermissionNames.Edit, RoleNames.Admin, true)
|
||||
},
|
||||
Settings = new List<Setting> {
|
||||
new Setting { SettingName = "DynamicTokens", SettingValue = "true" }
|
||||
},
|
||||
Content = localizer["Privacy"]
|
||||
}
|
||||
}
|
||||
},
|
||||
new PageTemplate
|
||||
{
|
||||
Name = "Terms",
|
||||
Parent = "",
|
||||
Path = "terms",
|
||||
Order = 1013,
|
||||
Icon = Icons.List,
|
||||
IsNavigation = false,
|
||||
IsPersonalizable = false,
|
||||
PermissionList = new List<Permission>
|
||||
{
|
||||
new Permission(PermissionNames.View, RoleNames.Everyone, true),
|
||||
new Permission(PermissionNames.View, RoleNames.Admin, true),
|
||||
new Permission(PermissionNames.Edit, RoleNames.Admin, true)
|
||||
},
|
||||
PageTemplateModules = new List<PageTemplateModule>
|
||||
{
|
||||
new PageTemplateModule { ModuleDefinitionName = "Oqtane.Modules.HtmlText, Oqtane.Client", Title = "Terms of Use", Pane = PaneNames.Default,
|
||||
PermissionList = new List<Permission> {
|
||||
new Permission(PermissionNames.View, RoleNames.Everyone, true),
|
||||
new Permission(PermissionNames.View, RoleNames.Admin, true),
|
||||
new Permission(PermissionNames.Edit, RoleNames.Admin, true)
|
||||
},
|
||||
Settings = new List<Setting> {
|
||||
new Setting { SettingName = "DynamicTokens", SettingValue = "true" }
|
||||
},
|
||||
Content = localizer["Terms"]
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
AddPagesToSites(scope, tenant, pageTemplates);
|
||||
}
|
||||
|
||||
private void AddPagesToSites(IServiceScope scope, Tenant tenant, List<PageTemplate> pageTemplates)
|
||||
{
|
||||
var tenants = scope.ServiceProvider.GetRequiredService<ITenantManager>();
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user