Compare commits
328 Commits
Author | SHA1 | Date | |
---|---|---|---|
c499acdc4a | |||
b24e3252d9 | |||
e15787b1e4 | |||
d5f19d97e2 | |||
5543a4aeed | |||
9c333232e2 | |||
d52b95ea23 | |||
ef4fbcbb8a | |||
aa454b411f | |||
543e9339c7 | |||
7fff5c0d18 | |||
fa79f3f6fa | |||
c098839881 | |||
8d0d88c1b9 | |||
2b6ba0f410 | |||
11235009c0 | |||
4b05f7fdad | |||
338b0ae509 | |||
a437082952 | |||
ca9aba7b3b | |||
fe9f189734 | |||
018ac612f4 | |||
5bde40ec2b | |||
4d5780c192 | |||
ff6a810ad5 | |||
feec01ba00 | |||
1f05d12ef5 | |||
31aba14507 | |||
bbd6f13f36 | |||
68edbbbdb9 | |||
eb5a0dc1c9 | |||
7cea4f1792 | |||
c57c6abb1b | |||
a25b706c7b | |||
5d077e843d | |||
65bf3e9899 | |||
51ba3a01f5 | |||
f9ca611b8b | |||
a49b8728fd | |||
9234b91089 | |||
f3fcef52dd | |||
7f8b741981 | |||
f1791a709c | |||
30307fb05e | |||
57d443be8d | |||
84844c5043 | |||
9000f05961 | |||
6bef9b5c6e | |||
ffef1f4820 | |||
10c7bdbcaa | |||
e8f9888a41 | |||
8bac702be6 | |||
128bcecfe3 | |||
1390b3c489 | |||
a0f41341ac | |||
8ffa7ef7ff | |||
deb4607081 | |||
49c62f80e8 | |||
139793f3c0 | |||
f237cb9655 | |||
9f18c460d8 | |||
306a41b442 | |||
b53f54295d | |||
7e4f066694 | |||
90d72489d9 | |||
045c455324 | |||
60da903360 | |||
6d2a71f37e | |||
c8f60a12a4 | |||
11b2d3aa43 | |||
3d9c81d850 | |||
8ccb1a24f8 | |||
48c6796128 | |||
fc403f920b | |||
ca19496b5c | |||
0ae38f8a40 | |||
ad868ba841 | |||
f2aa39aa85 | |||
e76e0fc351 | |||
a348913888 | |||
5507006c53 | |||
994429f098 | |||
8efdcb9c49 | |||
db9a40db2b | |||
25667499e6 | |||
a728cd2d91 | |||
3f5f3ef10b | |||
0d708124c2 | |||
d31f73df14 | |||
3fed45438b | |||
8aa967fa1b | |||
6eaa3e342c | |||
6fc9e60f62 | |||
c39ffcf51c | |||
6f60a91f4c | |||
8031df6f28 | |||
da1e859fda | |||
8d4d25f1d1 | |||
6aff27778d | |||
ca2dcbfec0 | |||
24b666a382 | |||
9e34295529 | |||
10c55d056b | |||
753ab3bdd7 | |||
feee8def6f | |||
c208f12f8c | |||
dc926bf838 | |||
55a76b5204 | |||
53b837e763 | |||
3a551cdf25 | |||
c0c7f87dc9 | |||
ac77fd138b | |||
30d6e9d67c | |||
47db4334a1 | |||
fce97179e1 | |||
4f16cd2d01 | |||
fa587691b1 | |||
e0044658f9 | |||
53de1ddb36 | |||
da7b046092 | |||
d888d83a98 | |||
430f83e8e9 | |||
e7acd14faa | |||
1b00fa74bc | |||
4d572d8173 | |||
dbda85d8d9 | |||
95cb5dd66c | |||
f64e1c3a6a | |||
26a686c412 | |||
2505383f53 | |||
1a1e9ac6be | |||
7f20a3179e | |||
46431f0187 | |||
1ff8ec78c9 | |||
7840230c62 | |||
523db0a005 | |||
cc906d49ba | |||
fc23af89d3 | |||
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,9 @@ namespace Microsoft.Extensions.DependencyInjection
|
||||
services.AddScoped<IVisitorService, VisitorService>();
|
||||
services.AddScoped<ISyncService, SyncService>();
|
||||
services.AddScoped<ILocalizationCookieService, LocalizationCookieService>();
|
||||
services.AddScoped<ICookieConsentService, CookieConsentService>();
|
||||
services.AddScoped<IOutputCacheService, OutputCacheService>();
|
||||
services.AddScoped<ITimeZoneService, TimeZoneService>();
|
||||
|
||||
// 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" />
|
||||
|
@ -15,22 +15,34 @@
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="parent" HelpText="Select the parent folder" ResourceKey="Parent">Parent: </Label>
|
||||
<div class="col-sm-9">
|
||||
<select id="parent" class="form-select" @bind="@_parentId" required>
|
||||
@if (PageState.QueryString.ContainsKey("id"))
|
||||
@if (_parentId == -1)
|
||||
{
|
||||
<select id="parent" class="form-select" @bind="@_parentId" required>
|
||||
<option value="-1"><@Localizer["NoParent"]></option>
|
||||
</select>
|
||||
}
|
||||
else
|
||||
{
|
||||
<select id="parent" class="form-select" @bind="@_parentId" required>
|
||||
@foreach (Folder folder in _folders)
|
||||
{
|
||||
<option value="@(folder.FolderId)">@(new string('-', folder.Level * 2))@(folder.Name)</option>
|
||||
}
|
||||
</select>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="name" HelpText="Enter the folder name" ResourceKey="Name">Name: </Label>
|
||||
<div class="col-sm-9">
|
||||
@if (_isSystem)
|
||||
{
|
||||
<input id="name" class="form-control" @bind="@_name" readonly />
|
||||
}
|
||||
else
|
||||
{
|
||||
<input id="name" class="form-control" @bind="@_name" maxlength="256" required />
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
@ -229,7 +241,6 @@
|
||||
|
||||
if (folder != null)
|
||||
{
|
||||
await FolderService.UpdateFolderOrderAsync(folder.SiteId, folder.FolderId, folder.ParentId);
|
||||
await logger.LogInformation("Folder Saved {Folder}", folder);
|
||||
NavigationManager.NavigateTo(NavigateUrl());
|
||||
}
|
||||
@ -264,20 +275,12 @@
|
||||
}
|
||||
}
|
||||
if (!isparent)
|
||||
{
|
||||
var files = await FileService.GetFilesAsync(_folderId);
|
||||
if (files.Count == 0)
|
||||
{
|
||||
await FolderService.DeleteFolderAsync(_folderId);
|
||||
await logger.LogInformation("Folder Deleted {Folder}", _folderId);
|
||||
NavigationManager.NavigateTo(NavigateUrl());
|
||||
}
|
||||
else
|
||||
{
|
||||
AddModuleMessage(Localizer["Message.Folder.Files.InvalidDelete"], MessageType.Warning);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
AddModuleMessage(Localizer["Message.Folder.Subfolders.InvalidDelete"], MessageType.Warning);
|
||||
}
|
||||
|
@ -53,7 +53,7 @@ else
|
||||
<td><ActionLink Action="Details" Text="Edit" Parameters="@($"id=" + context.FileId.ToString())" ResourceKey="Details" /></td>
|
||||
<td><ActionDialog Header="Delete File" Message="@string.Format(Localizer["Confirm.File.Delete"], context.Name)" Action="Delete" Security="SecurityAccessLevel.Admin" Class="btn btn-danger" OnClick="@(async () => await DeleteFile(context))" ResourceKey="DeleteFile" /></td>
|
||||
<td><a href="@context.Url" target="_new">@context.Name</a></td>
|
||||
<td>@context.ModifiedOn</td>
|
||||
<td>@UtcToLocal(context.ModifiedOn)</td>
|
||||
<td>@context.Extension.ToUpper() @SharedLocalizer["File"]</td>
|
||||
<td>@string.Format("{0:0.00}", ((decimal)context.Size / 1000)) KB</td>
|
||||
</Row>
|
||||
|
@ -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">
|
||||
@ -132,13 +132,13 @@
|
||||
_isEnabled = job.IsEnabled.ToString();
|
||||
_interval = job.Interval.ToString();
|
||||
_frequency = job.Frequency;
|
||||
_startDate = Utilities.UtcAsLocalDate(job.StartDate);
|
||||
_startTime = Utilities.UtcAsLocalDateTime(job.StartDate);
|
||||
_endDate = Utilities.UtcAsLocalDate(job.EndDate);
|
||||
_endTime = Utilities.UtcAsLocalDateTime(job.EndDate);
|
||||
_startDate = UtcToLocal(job.StartDate);
|
||||
_startTime = UtcToLocal(job.StartDate);
|
||||
_endDate = UtcToLocal(job.EndDate);
|
||||
_endTime = UtcToLocal(job.EndDate);
|
||||
_retentionHistory = job.RetentionHistory.ToString();
|
||||
_nextDate = Utilities.UtcAsLocalDate(job.NextExecution);
|
||||
_nextTime = Utilities.UtcAsLocalDateTime(job.NextExecution);
|
||||
_nextDate = UtcToLocal(job.NextExecution);
|
||||
_nextTime = UtcToLocal(job.NextExecution);
|
||||
createdby = job.CreatedBy;
|
||||
createdon = job.CreatedOn;
|
||||
modifiedby = job.ModifiedBy;
|
||||
@ -176,10 +176,10 @@
|
||||
{
|
||||
job.Interval = int.Parse(_interval);
|
||||
}
|
||||
job.StartDate = Utilities.LocalDateAndTimeAsUtc(_startDate, _startTime);
|
||||
job.EndDate = Utilities.LocalDateAndTimeAsUtc(_endDate, _endTime);
|
||||
job.StartDate = LocalToUtc(_startDate.Value.Date.Add(_startTime.Value.TimeOfDay));
|
||||
job.EndDate = LocalToUtc(_endDate.Value.Date.Add(_endTime.Value.TimeOfDay));
|
||||
job.RetentionHistory = int.Parse(_retentionHistory);
|
||||
job.NextExecution = Utilities.LocalDateAndTimeAsUtc(_nextDate, _nextTime);
|
||||
job.NextExecution = LocalToUtc(_nextDate.Value.Date.Add(_nextTime.Value.TimeOfDay));
|
||||
|
||||
try
|
||||
{
|
||||
|
@ -29,7 +29,7 @@ else
|
||||
<td>@context.Name</td>
|
||||
<td>@DisplayStatus(context.IsEnabled, context.IsExecuting)</td>
|
||||
<td>@DisplayFrequency(context.Interval, context.Frequency)</td>
|
||||
<td>@context.NextExecution?.ToLocalTime()</td>
|
||||
<td>@UtcToLocal(context.NextExecution)</td>
|
||||
<td>
|
||||
@if (context.IsStarted)
|
||||
{
|
||||
@ -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()
|
||||
|
@ -23,8 +23,8 @@ else
|
||||
<Row>
|
||||
<td>@context.Job.Name</td>
|
||||
<td>@DisplayStatus(context.Job.IsExecuting, context.Succeeded)</td>
|
||||
<td>@context.StartDate</td>
|
||||
<td>@context.FinishDate</td>
|
||||
<td>@UtcToLocal(context.StartDate)</td>
|
||||
<td>@UtcToLocal(context.FinishDate)</td>
|
||||
</Row>
|
||||
<Detail>
|
||||
<td colspan="4">@((MarkupString)context.Notes)</td>
|
||||
@ -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>
|
||||
@ -144,7 +144,7 @@ else
|
||||
user = await UserService.VerifyEmailAsync(user, PageState.QueryString["token"]);
|
||||
if (user != null)
|
||||
{
|
||||
await logger.LogInformation(LogFunction.Security, "Email Verified For For Username {Username}", _username);
|
||||
await logger.LogInformation(LogFunction.Security, "Email Verified For Username {Username}", _username);
|
||||
AddModuleMessage(Localizer["Success.Account.Verified"], MessageType.Info);
|
||||
}
|
||||
else
|
||||
|
@ -141,7 +141,7 @@
|
||||
var log = await LogService.GetLogAsync(_logId);
|
||||
if (log != null)
|
||||
{
|
||||
_logDate = log.LogDate.ToString(CultureInfo.CurrentCulture);
|
||||
_logDate = UtcToLocal(log.LogDate).Value.ToString(CultureInfo.CurrentCulture);
|
||||
_level = log.Level;
|
||||
_feature = log.Feature;
|
||||
_function = log.Function;
|
||||
|
@ -64,7 +64,7 @@ else
|
||||
</Header>
|
||||
<Row>
|
||||
<td class="@GetClass(context.Function)"><ActionLink Action="Detail" Text="Details" Parameters="@($"/{context.LogId}")" ReturnUrl="@(NavigateUrl(PageState.Page.Path, AddUrlParameters(_level, _function, _rows, _page)))" ResourceKey="LogDetails" /></td>
|
||||
<td class="@GetClass(context.Function)">@context.LogDate</td>
|
||||
<td class="@GetClass(context.Function)">@UtcToLocal(context.LogDate)</td>
|
||||
<td class="@GetClass(context.Function)">@context.Level</td>
|
||||
<td class="@GetClass(context.Function)">@context.Feature</td>
|
||||
<td class="@GetClass(context.Function)">@context.Function</td>
|
||||
|
@ -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 />
|
||||
}
|
||||
</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;
|
||||
}
|
||||
|
@ -17,8 +17,8 @@ else
|
||||
<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" />
|
||||
<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))">
|
||||
@ -220,4 +220,27 @@ else
|
||||
_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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -5,24 +5,57 @@
|
||||
@inject IStringLocalizer<Export> Localizer
|
||||
@inject IStringLocalizer<SharedResources> SharedLocalizer
|
||||
|
||||
<TabStrip>
|
||||
<TabPanel Name="Content" Heading="Content" ResourceKey="Content">
|
||||
<div class="container">
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="content" HelpText="The Exported Module Content" ResourceKey="Content">Content: </Label>
|
||||
<Label Class="col-sm-3" For="content" HelpText="Select the Export option and you will be able to view the module content" ResourceKey="Content">Content: </Label>
|
||||
<div class="col-sm-9">
|
||||
<textarea id="content" class="form-control" @bind="@_content" rows="5" readonly></textarea>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button type="button" class="btn btn-success" @onclick="ExportModule">@Localizer["Export"]</button>
|
||||
<br />
|
||||
<button type="button" class="btn btn-success" @onclick="ExportText">@Localizer["Export"]</button>
|
||||
<NavLink class="btn btn-secondary" href="@PageState.ReturnUrl">@SharedLocalizer["Cancel"]</NavLink>
|
||||
</TabPanel>
|
||||
<TabPanel Name="File" Heading="File" ResourceKey="File">
|
||||
<div class="container">
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="folder" HelpText="Select a folder where you wish to save the exported content" ResourceKey="Folder">Folder: </Label>
|
||||
<div class="col-sm-9">
|
||||
<FileManager ShowFiles="false" ShowUpload="false" @ref="_filemanager" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="filename" HelpText="Specify a name for the file (without an extension)" ResourceKey="Filename">Filename: </Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="content" type="text" class="form-control" @bind="@_filename" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<br />
|
||||
<button type="button" class="btn btn-success" @onclick="ExportFile">@Localizer["Export"]</button>
|
||||
<NavLink class="btn btn-secondary" href="@PageState.ReturnUrl">@SharedLocalizer["Cancel"]</NavLink>
|
||||
</TabPanel>
|
||||
</TabStrip>
|
||||
|
||||
|
||||
|
||||
@code {
|
||||
private string _content = string.Empty;
|
||||
private FileManager _filemanager;
|
||||
private string _filename = string.Empty;
|
||||
|
||||
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Edit;
|
||||
public override string Title => "Export Content";
|
||||
|
||||
private async Task ExportModule()
|
||||
protected override void OnInitialized()
|
||||
{
|
||||
_filename = Utilities.GetFriendlyUrl(ModuleState.Title);
|
||||
}
|
||||
|
||||
private async Task ExportText()
|
||||
{
|
||||
try
|
||||
{
|
||||
@ -35,4 +68,34 @@
|
||||
AddModuleMessage(Localizer["Error.Module.Export"], MessageType.Error);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task ExportFile()
|
||||
{
|
||||
try
|
||||
{
|
||||
var folderid = _filemanager.GetFolderId();
|
||||
if (folderid != -1 && !string.IsNullOrEmpty(_filename))
|
||||
{
|
||||
var fileid = await ModuleService.ExportModuleAsync(ModuleState.ModuleId, PageState.Page.PageId, folderid, _filename);
|
||||
if (fileid != -1)
|
||||
{
|
||||
AddModuleMessage(Localizer["Success.Content.Export"], MessageType.Success);
|
||||
}
|
||||
else
|
||||
{
|
||||
AddModuleMessage(Localizer["Error.Module.Export"], MessageType.Error);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
AddModuleMessage(Localizer["Message.Content.Export"], MessageType.Warning);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
await logger.LogError(ex, "Error Exporting Module {ModuleId} {Error}", ModuleState.ModuleId, ex.Message);
|
||||
AddModuleMessage(Localizer["Error.Module.Export"], MessageType.Error);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -2,20 +2,27 @@
|
||||
@inherits ModuleBase
|
||||
@inject NavigationManager NavigationManager
|
||||
@inject IModuleService ModuleService
|
||||
@inject IFileService FileService
|
||||
@inject IStringLocalizer<Import> 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="content" HelpText="Enter The Module Content To Import" ResourceKey="Content">Content: </Label>
|
||||
<Label Class="col-sm-3" For="file" HelpText="Optionally upload or select a file to import for this module" ResourceKey="File">File: </Label>
|
||||
<div class="col-sm-9">
|
||||
<FileManager Filter="json" OnSelectFile="OnSelectFile" />
|
||||
</div>
|
||||
</div>
|
||||
<hr />
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="content" HelpText="Provide the module content to import" ResourceKey="Content">Content: </Label>
|
||||
<div class="col-sm-9">
|
||||
<textarea id="content" class="form-control" @bind="@_content" rows="5" required></textarea>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<br />
|
||||
<button type="button" class="btn btn-success" @onclick="ImportModule">@Localizer["Import"]</button>
|
||||
<NavLink class="btn btn-secondary" href="@PageState.ReturnUrl">@SharedLocalizer["Cancel"]</NavLink>
|
||||
</form>
|
||||
@ -28,6 +35,12 @@
|
||||
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Edit;
|
||||
public override string Title => "Import Content";
|
||||
|
||||
private async Task OnSelectFile(int fileId)
|
||||
{
|
||||
var bytes = await FileService.DownloadFileAsync(fileId);
|
||||
_content = System.Text.Encoding.UTF8.GetString(bytes, 0, bytes.Length);
|
||||
}
|
||||
|
||||
private async Task ImportModule()
|
||||
{
|
||||
validated = true;
|
||||
|
@ -97,6 +97,23 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<br />
|
||||
<Section Name="ModuleContent" Heading="Content" ResourceKey="ModuleContent">
|
||||
<div class="container">
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="moduleheader" HelpText="Optionally provide content to be injected above the module instance" ResourceKey="Header">Header: </Label>
|
||||
<div class="col-sm-9">
|
||||
<textarea id="moduleheader" class="form-control" @bind="@_header" rows="3" maxlength="4000"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="modulefooter" HelpText="Optionally provide content to be injected below the module instance" ResourceKey="Footer">Footer: </Label>
|
||||
<div class="col-sm-9">
|
||||
<textarea id="modulefooter" class="form-control" @bind="@_footer" rows="3" maxlength="4000"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Section>
|
||||
}
|
||||
</TabPanel>
|
||||
<TabPanel Name="Permissions" Heading="Permissions" ResourceKey="Permissions">
|
||||
@ -144,6 +161,8 @@
|
||||
private string _pane;
|
||||
private string _containerType;
|
||||
private string _allPages = "false";
|
||||
private string _header = "";
|
||||
private string _footer = "";
|
||||
private string _permissionNames = "";
|
||||
private List<Permission> _permissions = null;
|
||||
private string _pageId;
|
||||
@ -167,37 +186,47 @@
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
SetModuleTitle(Localizer["ModuleSettings.Title"]);
|
||||
|
||||
_title = ModuleState.Title;
|
||||
_moduleSettingsTitle = Localizer["ModuleSettings.Heading"];
|
||||
_pane = ModuleState.Pane;
|
||||
|
||||
_containers = ThemeService.GetContainerControls(PageState.Site.Themes, PageState.Page.ThemeType);
|
||||
_containerType = ModuleState.ContainerType;
|
||||
_allPages = ModuleState.AllPages.ToString();
|
||||
_permissions = ModuleState.PermissionList;
|
||||
_pageId = ModuleState.PageId.ToString();
|
||||
createdby = ModuleState.CreatedBy;
|
||||
createdon = ModuleState.CreatedOn;
|
||||
modifiedby = ModuleState.ModifiedBy;
|
||||
modifiedon = ModuleState.ModifiedOn;
|
||||
_effectivedate = Utilities.UtcAsLocalDate(ModuleState.EffectiveDate);
|
||||
_expirydate = Utilities.UtcAsLocalDate(ModuleState.ExpiryDate);
|
||||
_pages = await PageService.GetPagesAsync(PageState.Site.SiteId);
|
||||
|
||||
if (ModuleState.ModuleDefinition != null)
|
||||
{
|
||||
_module = ModuleState.ModuleDefinition.Name;
|
||||
_permissionNames = ModuleState.ModuleDefinition?.PermissionNames;
|
||||
var pagemodule = await PageModuleService.GetPageModuleAsync(ModuleState.PageModuleId);
|
||||
|
||||
if (!string.IsNullOrEmpty(ModuleState.ModuleDefinition.SettingsType))
|
||||
_pageId = pagemodule.PageId.ToString();
|
||||
_title = pagemodule.Title;
|
||||
_pane = pagemodule.Pane;
|
||||
_containerType = pagemodule.ContainerType;
|
||||
if (string.IsNullOrEmpty(_containerType))
|
||||
{
|
||||
_containerType = (!string.IsNullOrEmpty(PageState.Page.DefaultContainerType)) ? PageState.Page.DefaultContainerType : PageState.Site.DefaultContainerType;
|
||||
}
|
||||
_header = pagemodule.Header;
|
||||
_footer = pagemodule.Footer;
|
||||
_effectivedate = Utilities.UtcAsLocalDate(pagemodule.EffectiveDate);
|
||||
_expirydate = Utilities.UtcAsLocalDate(pagemodule.ExpiryDate);
|
||||
|
||||
_allPages = pagemodule.Module.AllPages.ToString();
|
||||
createdby = pagemodule.Module.CreatedBy;
|
||||
createdon = pagemodule.Module.CreatedOn;
|
||||
modifiedby = pagemodule.Module.ModifiedBy;
|
||||
modifiedon = pagemodule.Module.ModifiedOn;
|
||||
_permissions = pagemodule.Module.PermissionList;
|
||||
|
||||
if (pagemodule.Module.ModuleDefinition != null)
|
||||
{
|
||||
_module = pagemodule.Module.ModuleDefinition.Name;
|
||||
_permissionNames = pagemodule.Module.ModuleDefinition?.PermissionNames;
|
||||
|
||||
if (!string.IsNullOrEmpty(pagemodule.Module.ModuleDefinition.SettingsType))
|
||||
{
|
||||
// module settings type explicitly declared in IModule interface
|
||||
_moduleSettingsType = Type.GetType(ModuleState.ModuleDefinition.SettingsType);
|
||||
_moduleSettingsType = Type.GetType(pagemodule.Module.ModuleDefinition.SettingsType);
|
||||
}
|
||||
else
|
||||
{
|
||||
// legacy support - module settings type determined by convention ( ie. existence of a "Settings.razor" component in module )
|
||||
_moduleSettingsType = Type.GetType(ModuleState.ModuleDefinition.ControlTypeTemplate.Replace(Constants.ActionToken, PageState.Action), false, true);
|
||||
_moduleSettingsType = Type.GetType(pagemodule.Module.ModuleDefinition.ControlTypeTemplate.Replace(Constants.ActionToken, PageState.Action), false, true);
|
||||
}
|
||||
if (_moduleSettingsType != null)
|
||||
{
|
||||
@ -218,7 +247,7 @@
|
||||
}
|
||||
else
|
||||
{
|
||||
AddModuleMessage(string.Format(Localizer["Error.Module.Load"], ModuleState.ModuleDefinitionName), MessageType.Error);
|
||||
AddModuleMessage(string.Format(Localizer["Error.Module.Load"], pagemodule.Module.ModuleDefinitionName), MessageType.Error);
|
||||
}
|
||||
|
||||
var theme = PageState.Site.Themes.FirstOrDefault(item => item.Containers.Any(themecontrol => themecontrol.TypeName.Equals(_containerType)));
|
||||
@ -270,10 +299,12 @@
|
||||
{
|
||||
pagemodule.ContainerType = string.Empty;
|
||||
}
|
||||
pagemodule.Header = _header;
|
||||
pagemodule.Footer = _footer;
|
||||
await PageModuleService.UpdatePageModuleAsync(pagemodule);
|
||||
await PageModuleService.UpdatePageModuleOrderAsync(pagemodule.PageId, pagemodule.Pane);
|
||||
|
||||
var module = ModuleState;
|
||||
var module = await ModuleService.GetModuleAsync(ModuleState.ModuleId);
|
||||
module.AllPages = bool.Parse(_allPages);
|
||||
module.PageModuleId = ModuleState.PageModuleId;
|
||||
module.PermissionList = _permissionGrid.GetPermissionList();
|
||||
|
@ -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,18 +205,21 @@
|
||||
<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>
|
||||
</Section>
|
||||
<br />
|
||||
<button type="button" class="btn btn-success" @onclick="SavePage">@SharedLocalizer["Save"]</button>
|
||||
<button type="button" class="btn btn-secondary" @onclick="Cancel">@SharedLocalizer["Cancel"]</button>
|
||||
<br />
|
||||
<br />
|
||||
<AuditInfo CreatedBy="@_createdby" CreatedOn="@_createdon" ModifiedBy="@_modifiedby" ModifiedOn="@_modifiedon" DeletedBy="@_deletedby" DeletedOn="@_deletedon"></AuditInfo>
|
||||
</TabPanel>
|
||||
@ -225,6 +228,19 @@
|
||||
<div class="row mb-1 align-items-center">
|
||||
<PermissionGrid EntityName="@EntityNames.Page" PermissionList="@_permissions" @ref="_permissionGrid" />
|
||||
</div>
|
||||
<br /><br />
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="updatemodulepermissions" HelpText="Specify if changes made to page permissions should be propagated to the modules on this page" ResourceKey="UpdateModulePermissions">Update Module Permissions? </Label>
|
||||
<div class="col-sm-9">
|
||||
<select id="updatemodulepermissions" class="form-select" @bind="@_updatemodulepermissions" required>
|
||||
<option value="True">@SharedLocalizer["Yes"]</option>
|
||||
<option value="False">@SharedLocalizer["No"]</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<br />
|
||||
<button type="button" class="btn btn-success" @onclick="SavePage">@SharedLocalizer["Save"]</button>
|
||||
<button type="button" class="btn btn-secondary" @onclick="Cancel">@SharedLocalizer["Cancel"]</button>
|
||||
</div>
|
||||
</TabPanel>
|
||||
<TabPanel Name="PageModules" Heading="Modules" ResourceKey="PageModules">
|
||||
@ -247,8 +263,10 @@
|
||||
{
|
||||
<TabPanel Name="ThemeSettings" Heading="Theme Settings" ResourceKey="ThemeSettings">
|
||||
@_themeSettingsComponent
|
||||
</TabPanel>
|
||||
<br />
|
||||
<button type="button" class="btn btn-success" @onclick="SavePage">@SharedLocalizer["Save"]</button>
|
||||
<button type="button" class="btn btn-secondary" @onclick="Cancel">@SharedLocalizer["Cancel"]</button>
|
||||
</TabPanel>
|
||||
}
|
||||
</TabStrip>
|
||||
}
|
||||
@ -299,19 +317,21 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<br />
|
||||
<button type="button" class="btn btn-success" @onclick="SavePage">@SharedLocalizer["Save"]</button>
|
||||
<button type="button" class="btn btn-secondary" @onclick="Cancel">@SharedLocalizer["Cancel"]</button>
|
||||
</TabPanel>
|
||||
@if (_themeSettingsType != null)
|
||||
{
|
||||
<TabPanel Name="ThemeSettings" Heading="Theme Settings" ResourceKey="ThemeSettings">
|
||||
@_themeSettingsComponent
|
||||
</TabPanel>
|
||||
<br />
|
||||
}
|
||||
</TabStrip>
|
||||
}
|
||||
<br />
|
||||
<button type="button" class="btn btn-success" @onclick="SavePage">@SharedLocalizer["Save"]</button>
|
||||
<button type="button" class="btn btn-secondary" @onclick="Cancel">@SharedLocalizer["Cancel"]</button>
|
||||
</TabPanel>
|
||||
}
|
||||
</TabStrip>
|
||||
}
|
||||
</form>
|
||||
}
|
||||
|
||||
@ -348,6 +368,7 @@
|
||||
private string _bodycontent;
|
||||
private List<Permission> _permissions = null;
|
||||
private PermissionGrid _permissionGrid;
|
||||
private string _updatemodulepermissions;
|
||||
private List<Module> _pageModules;
|
||||
private string _createdby;
|
||||
private DateTime _createdon;
|
||||
@ -436,6 +457,7 @@
|
||||
|
||||
// permissions
|
||||
_permissions = _page.PermissionList;
|
||||
_updatemodulepermissions = "True";
|
||||
|
||||
// page modules
|
||||
var modules = await ModuleService.GetModulesAsync(PageState.Site.SiteId);
|
||||
@ -651,6 +673,7 @@
|
||||
if (_page.UserId == null)
|
||||
{
|
||||
_page.PermissionList = _permissionGrid.GetPermissionList();
|
||||
_page.UpdateModulePermissions = bool.Parse(_updatemodulepermissions);
|
||||
}
|
||||
|
||||
_page = await PageService.UpdatePageAsync(_page);
|
||||
|
@ -3,10 +3,13 @@
|
||||
@inherits ModuleBase
|
||||
@inject NavigationManager NavigationManager
|
||||
@inject IUserService UserService
|
||||
@inject ITimeZoneService TimeZoneService
|
||||
@inject IStringLocalizer<Index> Localizer
|
||||
@inject IStringLocalizer<SharedResources> SharedLocalizer
|
||||
@inject ISettingService SettingService
|
||||
|
||||
@if (_initialized)
|
||||
{
|
||||
@if (PageState.Site.AllowRegistration)
|
||||
{
|
||||
if (!_userCreated)
|
||||
@ -56,6 +59,18 @@
|
||||
<input id="displayname" class="form-control" @bind="@_displayname" maxlength="50" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="timezone" HelpText="Your time zone" ResourceKey="TimeZone">Time Zone:</Label>
|
||||
<div class="col-sm-9">
|
||||
<select id="timezone" class="form-select" @bind="@_timezoneid">
|
||||
<option value=""><@SharedLocalizer["Not Specified"]></option>
|
||||
@foreach (var timezone in _timezones)
|
||||
{
|
||||
<option value="@timezone.Id">@timezone.DisplayName</option>
|
||||
}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<br />
|
||||
<button type="button" class="btn btn-primary" @onclick="Register">@Localizer["Register"]</button>
|
||||
@ -75,8 +90,11 @@ else
|
||||
{
|
||||
<ModuleMessage Message="@Localizer["Info.Registration.Disabled"]" Type="MessageType.Info" />
|
||||
}
|
||||
}
|
||||
|
||||
@code {
|
||||
private bool _initialized = false;
|
||||
private List<Models.TimeZone> _timezones;
|
||||
private string _passwordrequirements;
|
||||
private string _username = string.Empty;
|
||||
private ElementReference form;
|
||||
@ -87,6 +105,7 @@ else
|
||||
private string _confirm = string.Empty;
|
||||
private string _email = string.Empty;
|
||||
private string _displayname = string.Empty;
|
||||
private string _timezoneid = string.Empty;
|
||||
private bool _userCreated = false;
|
||||
private bool _allowsitelogin = true;
|
||||
|
||||
@ -96,6 +115,9 @@ else
|
||||
{
|
||||
_passwordrequirements = await UserService.GetPasswordRequirementsAsync(PageState.Site.SiteId);
|
||||
_allowsitelogin = bool.Parse(SettingService.GetSetting(PageState.Site.Settings, "LoginOptions:AllowSiteLogin", "true"));
|
||||
_timezones = await TimeZoneService.GetTimeZonesAsync();
|
||||
_timezoneid = PageState.Site.TimeZoneId;
|
||||
_initialized = true;
|
||||
}
|
||||
|
||||
protected override void OnParametersSet()
|
||||
@ -124,6 +146,7 @@ else
|
||||
Password = _password,
|
||||
Email = _email,
|
||||
DisplayName = (_displayname == string.Empty ? _username : _displayname),
|
||||
TimeZoneId = _timezoneid,
|
||||
PhotoFileId = null
|
||||
};
|
||||
user = await UserService.AddUserAsync(user);
|
||||
|
@ -10,10 +10,12 @@
|
||||
@inject IAliasService AliasService
|
||||
@inject IThemeService ThemeService
|
||||
@inject ISettingService SettingService
|
||||
@inject ITimeZoneService TimeZoneService
|
||||
@inject IServiceProvider ServiceProvider
|
||||
@inject IStringLocalizer<Index> Localizer
|
||||
@inject INotificationService NotificationService
|
||||
@inject IStringLocalizer<SharedResources> SharedLocalizer
|
||||
@inject IOutputCacheService CacheService
|
||||
|
||||
@if (_initialized)
|
||||
{
|
||||
@ -40,6 +42,18 @@
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="timezone" HelpText="Your time zone" ResourceKey="TimeZone">Time Zone:</Label>
|
||||
<div class="col-sm-9">
|
||||
<select id="timezone" class="form-select" @bind="@_timezoneid">
|
||||
<option value=""><@SharedLocalizer["Not Specified"]></option>
|
||||
@foreach (var timezone in _timezones)
|
||||
{
|
||||
<option value="@timezone.Id">@timezone.DisplayName</option>
|
||||
}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="isDeleted" HelpText="Is this site deleted?" ResourceKey="IsDeleted">Deleted? </Label>
|
||||
<div class="col-sm-9">
|
||||
@ -50,11 +64,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 +87,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 +129,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">
|
||||
@ -400,9 +429,11 @@
|
||||
private List<ThemeControl> _themes = new List<ThemeControl>();
|
||||
private List<ThemeControl> _containers = new List<ThemeControl>();
|
||||
private List<Page> _pages;
|
||||
private List<Models.TimeZone> _timezones;
|
||||
|
||||
private string _name = string.Empty;
|
||||
private string _homepageid = "-";
|
||||
private string _timezoneid = string.Empty;
|
||||
private string _isdeleted;
|
||||
private string _sitemap = "";
|
||||
private string _siteguid = "";
|
||||
@ -415,10 +446,11 @@
|
||||
private string _themetype = "";
|
||||
private string _containertype = "";
|
||||
private string _admincontainertype = "";
|
||||
private string _cookieconsent = "";
|
||||
|
||||
private Dictionary<string, string> _textEditors = new Dictionary<string, string>();
|
||||
private string _textEditor = "";
|
||||
private string _imageFiles = string.Empty;
|
||||
private string _imagefiles = string.Empty;
|
||||
|
||||
private string _headcontent = string.Empty;
|
||||
private string _bodycontent = string.Empty;
|
||||
@ -476,11 +508,13 @@
|
||||
Site site = await SiteService.GetSiteAsync(PageState.Site.SiteId);
|
||||
if (site != null)
|
||||
{
|
||||
_timezones = await TimeZoneService.GetTimeZonesAsync();
|
||||
var settings = await SettingService.GetSiteSettingsAsync(site.SiteId);
|
||||
|
||||
_pages = await PageService.GetPagesAsync(PageState.Site.SiteId);
|
||||
|
||||
_name = site.Name;
|
||||
_timezoneid = site.TimeZoneId;
|
||||
if (site.HomePageId != null)
|
||||
{
|
||||
_homepageid = site.HomePageId.Value.ToString();
|
||||
@ -505,6 +539,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>();
|
||||
@ -513,8 +548,8 @@
|
||||
_textEditors.Add(textEditor.Name, Utilities.GetFullTypeName(textEditor.GetType().AssemblyQualifiedName));
|
||||
}
|
||||
_textEditor = SettingService.GetSetting(settings, "TextEditor", Constants.DefaultTextEditor);
|
||||
_imageFiles = SettingService.GetSetting(settings, "ImageFiles", Constants.ImageFiles);
|
||||
_imageFiles = (string.IsNullOrEmpty(_imageFiles)) ? Constants.ImageFiles : _imageFiles;
|
||||
_imagefiles = SettingService.GetSetting(settings, "ImageFiles", Constants.ImageFiles);
|
||||
_imagefiles = (string.IsNullOrEmpty(_imagefiles)) ? Constants.ImageFiles : _imagefiles;
|
||||
|
||||
// page content
|
||||
_headcontent = site.HeadContent;
|
||||
@ -632,6 +667,7 @@
|
||||
if (site != null)
|
||||
{
|
||||
site.Name = _name;
|
||||
site.TimeZoneId = _timezoneid;
|
||||
site.HomePageId = (_homepageid != "-" ? int.Parse(_homepageid) : null);
|
||||
site.IsDeleted = (_isdeleted == null ? true : Boolean.Parse(_isdeleted));
|
||||
|
||||
@ -717,6 +753,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 +952,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 />
|
||||
}
|
||||
</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>
|
||||
@ -36,6 +38,7 @@ else
|
||||
<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,17 +13,33 @@
|
||||
<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 reduce the time required for the upgrade." 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>
|
||||
<br />
|
||||
@if (!_downloaded)
|
||||
{
|
||||
<button type="button" class="btn btn-primary" @onclick=@(async () => await Download(Constants.PackageId, @_package.Version))>@SharedLocalizer["Download"] @_package.Version</button>
|
||||
}
|
||||
else
|
||||
{
|
||||
<button type="button" class="btn btn-success" @onclick="Upgrade">@SharedLocalizer["Upgrade"]</button>
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
<ModuleMessage Type="MessageType.Info" Message=@Localizer["Message.Text"]></ModuleMessage>
|
||||
}
|
||||
</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,7 +47,17 @@
|
||||
<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 reduce the time required for the upgrade." 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>
|
||||
<br />
|
||||
<button type="button" class="btn btn-success" @onclick="Upgrade">@SharedLocalizer["Upgrade"]</button>
|
||||
</TabPanel>
|
||||
</TabStrip>
|
||||
@ -39,8 +65,10 @@
|
||||
|
||||
@code {
|
||||
private bool _initialized = false;
|
||||
private bool _downloaded = false;
|
||||
private Package _package;
|
||||
private bool _upgradeavailable = false;
|
||||
private string _backup = "True";
|
||||
|
||||
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Host;
|
||||
|
||||
@ -86,7 +114,7 @@
|
||||
ShowProgressIndicator();
|
||||
var interop = new Interop(JSRuntime);
|
||||
await interop.RedirectBrowser(NavigateUrl(), 10);
|
||||
await InstallationService.Upgrade();
|
||||
await InstallationService.Upgrade(bool.Parse(_backup));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@ -102,6 +130,7 @@
|
||||
ShowProgressIndicator();
|
||||
await PackageService.DownloadPackageAsync(packageid, version);
|
||||
await PackageService.DownloadPackageAsync(Constants.UpdaterPackageId, version);
|
||||
_downloaded = true;
|
||||
HideProgressIndicator();
|
||||
AddModuleMessage(Localizer["Success.Framework.Download"], MessageType.Success);
|
||||
}
|
||||
|
@ -8,13 +8,16 @@
|
||||
<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="url" HelpText="The fully qualified Url for this site" ResourceKey="Url">Url:</Label>
|
||||
<Label Class="col-sm-3" For="url" HelpText="A Url identifying a path to a specific page in the site (absolute or relative)" ResourceKey="Url">Url:</Label>
|
||||
<div class="col-sm-9">
|
||||
<div class="input-group">
|
||||
<input id="url" class="form-control" @bind="@_url" maxlength="500" required />
|
||||
<button type="button" class="btn btn-primary" @onclick="GenerateUrl">@Localizer["Generate"]</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="mappedurl" HelpText="A fully qualified Url where the user will be redirected" ResourceKey="MappedUrl">Redirect To:</Label>
|
||||
<Label Class="col-sm-3" For="mappedurl" HelpText="A Url where the user will be redirected (absolute or relative). Use '/' for site root path." ResourceKey="MappedUrl">Redirect To:</Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="mappedurl" class="form-control" @bind="@_mappedurl" maxlength="500" required />
|
||||
</div>
|
||||
@ -48,13 +51,15 @@
|
||||
_url = (_url.StartsWith("/")) ? _url.Substring(1) : _url;
|
||||
_url = (!_url.StartsWith("http")) ? url + _url : _url;
|
||||
|
||||
_mappedurl = _mappedurl.Replace(url, "");
|
||||
_mappedurl = (_mappedurl.StartsWith("/") && _mappedurl != "/") ? _mappedurl.Substring(1) : _mappedurl;
|
||||
|
||||
if (_url.StartsWith(url))
|
||||
{
|
||||
var urlmapping = new UrlMapping();
|
||||
urlmapping.SiteId = PageState.Site.SiteId;
|
||||
var route = new Route(_url, PageState.Alias.Path);
|
||||
urlmapping.Url = route.PagePath;
|
||||
urlmapping.MappedUrl = _mappedurl.Replace(url, "");
|
||||
urlmapping.Url = new Route(_url, PageState.Alias.Path).PagePath;
|
||||
urlmapping.MappedUrl = _mappedurl;
|
||||
urlmapping.Requests = 0;
|
||||
urlmapping.CreatedOn = DateTime.UtcNow;
|
||||
urlmapping.RequestedOn = DateTime.UtcNow;
|
||||
@ -86,4 +91,18 @@
|
||||
AddModuleMessage(SharedLocalizer["Message.InfoRequired"], MessageType.Warning);
|
||||
}
|
||||
}
|
||||
|
||||
private void GenerateUrl()
|
||||
{
|
||||
var url = PageState.Uri.Scheme + "://" + PageState.Uri.Authority + "/";
|
||||
url = url + (!string.IsNullOrEmpty(PageState.Alias.Path) ? PageState.Alias.Path + "/" : "");
|
||||
|
||||
var chars = "abcdefghijklmnopqrstuvwxyz";
|
||||
Random rnd = new Random();
|
||||
for (int i = 0; i < 5; i++)
|
||||
{
|
||||
url += chars.Substring(rnd.Next(0, chars.Length - 1), 1);
|
||||
}
|
||||
_url = url;
|
||||
}
|
||||
}
|
||||
|
@ -8,13 +8,13 @@
|
||||
<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="url" HelpText="A fully qualified Url for this site" ResourceKey="Url">Url:</Label>
|
||||
<Label Class="col-sm-3" For="url" HelpText="A Url identifying a path to a specific page in the site (absolute or relative)" ResourceKey="Url">Url:</Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="url" class="form-control" @bind="@_url" maxlength="500" readonly />
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="mappedurl" HelpText="A fully qualified Url where the user will be redirected" ResourceKey="MappedUrl">Redirect To:</Label>
|
||||
<Label Class="col-sm-3" For="mappedurl" HelpText="A Url where the user will be redirected (absolute or relative). Use '/' for site root path." ResourceKey="MappedUrl">Redirect To:</Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="mappedurl" class="form-control" @bind="@_mappedurl" maxlength="500" required />
|
||||
</div>
|
||||
@ -67,8 +67,11 @@
|
||||
var url = PageState.Uri.Scheme + "://" + PageState.Uri.Authority + "/";
|
||||
url = url + (!string.IsNullOrEmpty(PageState.Alias.Path) ? PageState.Alias.Path + "/" : "");
|
||||
|
||||
_mappedurl = _mappedurl.Replace(url, "");
|
||||
_mappedurl = (_mappedurl.StartsWith("/") && _mappedurl != "/") ? _mappedurl.Substring(1) : _mappedurl;
|
||||
|
||||
var urlmapping = await UrlMappingService.GetUrlMappingAsync(_urlmappingid);
|
||||
urlmapping.MappedUrl = _mappedurl.Replace(url, "");
|
||||
urlmapping.MappedUrl = _mappedurl;
|
||||
urlmapping = await UrlMappingService.UpdateUrlMappingAsync(urlmapping);
|
||||
await logger.LogInformation("UrlMapping Saved {UrlMapping}", urlmapping);
|
||||
NavigationManager.NavigateTo(NavigateUrl());
|
||||
|
@ -48,7 +48,7 @@ else
|
||||
}
|
||||
</td>
|
||||
<td>@context.Requests</td>
|
||||
<td>@context.RequestedOn</td>
|
||||
<td>@UtcToLocal(context.RequestedOn)</td>
|
||||
</Row>
|
||||
</Pager>
|
||||
</TabPanel>
|
||||
|
@ -9,6 +9,7 @@
|
||||
@inject INotificationService NotificationService
|
||||
@inject IFileService FileService
|
||||
@inject IFolderService FolderService
|
||||
@inject ITimeZoneService TimeZoneService
|
||||
@inject IJSRuntime jsRuntime
|
||||
@inject IServiceProvider ServiceProvider
|
||||
@inject IStringLocalizer<Index> Localizer
|
||||
@ -16,9 +17,9 @@
|
||||
|
||||
@if (_initialized)
|
||||
{
|
||||
@if (PageState.User != null && photo != null)
|
||||
@if (PageState.User != null && _photo != null)
|
||||
{
|
||||
<img src="@ImageUrl(photofileid, 400, 400)" alt="@displayname" style="max-width: 400px" class="rounded-circle mx-auto d-block">
|
||||
<img src="@ImageUrl(_photofileid, 400, 400)" alt="@_displayname" style="max-width: 400px" class="rounded-circle mx-auto d-block">
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -31,7 +32,7 @@
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="username" HelpText="Your username. Note that this field can not be modified." ResourceKey="Username"></Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="username" class="form-control" @bind="@username" readonly />
|
||||
<input id="username" class="form-control" @bind="@_username" readonly />
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
@ -47,17 +48,17 @@
|
||||
<Label Class="col-sm-3" For="confirm" HelpText="If you are changing your password you must enter it again to confirm it matches" ResourceKey="Confirm"></Label>
|
||||
<div class="col-sm-9">
|
||||
<div class="input-group">
|
||||
<input id="confirm" type="@_passwordtype" class="form-control" @bind="@confirm" autocomplete="new-password" />
|
||||
<input id="confirm" type="@_passwordtype" class="form-control" @bind="@_confirm" autocomplete="new-password" />
|
||||
<button type="button" class="btn btn-secondary" @onclick="@TogglePassword" tabindex="-1">@_togglepassword</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@if (allowtwofactor)
|
||||
@if (_allowtwofactor)
|
||||
{
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="twofactor" HelpText="Indicates if you are using two factor authentication. Two factor authentication requires you to enter a verification code sent via email after you sign in." ResourceKey="TwoFactor"></Label>
|
||||
<div class="col-sm-9">
|
||||
<select id="twofactor" class="form-select" @bind="@twofactor" required>
|
||||
<select id="twofactor" class="form-select" @bind="@_twofactor" required>
|
||||
<option value="True">@SharedLocalizer["Yes"]</option>
|
||||
<option value="False">@SharedLocalizer["No"]</option>
|
||||
</select>
|
||||
@ -67,19 +68,31 @@
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="email" HelpText="Your email address where you wish to receive notifications" ResourceKey="Email"></Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="email" class="form-control" @bind="@email" />
|
||||
<input id="email" class="form-control" @bind="@_email" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="displayname" HelpText="Your full name" ResourceKey="DisplayName"></Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="displayname" class="form-control" @bind="@displayname" />
|
||||
<input id="displayname" class="form-control" @bind="@_displayname" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="@photofileid.ToString()" HelpText="A photo of yourself" ResourceKey="Photo"></Label>
|
||||
<Label Class="col-sm-3" For="timezone" HelpText="Your time zone" ResourceKey="TimeZone">Time Zone:</Label>
|
||||
<div class="col-sm-9">
|
||||
<FileManager FileId="@photofileid" Filter="@PageState.Site.ImageFiles" ShowFolders="false" ShowFiles="true" UploadMultiple="false" FolderId="@folderid" @ref="filemanager" />
|
||||
<select id="timezone" class="form-select" @bind="@_timezoneid">
|
||||
<option value=""><@SharedLocalizer["Not Specified"]></option>
|
||||
@foreach (var timezone in _timezones)
|
||||
{
|
||||
<option value="@timezone.Id">@timezone.DisplayName</option>
|
||||
}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="@_photofileid.ToString()" HelpText="A photo of yourself" ResourceKey="Photo"></Label>
|
||||
<div class="col-sm-9">
|
||||
<FileManager FileId="@_photofileid" Filter="@PageState.Site.ImageFiles" ShowFolders="false" ShowFiles="true" UploadMultiple="false" FolderId="@_folderid" @ref="_filemanager" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -91,17 +104,17 @@
|
||||
<TabPanel Name="Profile" ResourceKey="Profile">
|
||||
<div class="container">
|
||||
<div class="row mb-1 align-items-center">
|
||||
@foreach (Profile profile in profiles)
|
||||
@foreach (Profile profile in _profiles)
|
||||
{
|
||||
var p = profile;
|
||||
if (!p.IsPrivate || UserSecurity.IsAuthorized(PageState.User, RoleNames.Admin))
|
||||
{
|
||||
if (p.Category != category)
|
||||
if (p.Category != _category)
|
||||
{
|
||||
<div class="col text-center pb-2">
|
||||
@p.Category
|
||||
</div>
|
||||
category = p.Category;
|
||||
_category = p.Category;
|
||||
}
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="@p.Name" HelpText="@p.Description">@p.Title</Label>
|
||||
@ -220,11 +233,11 @@
|
||||
<option value="from">@Localizer["Items.Sent"]</option>
|
||||
</select>
|
||||
<br />
|
||||
@if (filter == "to")
|
||||
@if (_filter == "to")
|
||||
{
|
||||
@if (notifications.Any())
|
||||
@if (_notifications.Any())
|
||||
{
|
||||
<Pager Items="@notifications">
|
||||
<Pager Items="@_notifications">
|
||||
<Header>
|
||||
<th style="width: 1px;"> </th>
|
||||
<th style="width: 1px;"> </th>
|
||||
@ -260,15 +273,15 @@
|
||||
context.Body = context.Body.Replace("\n", "");
|
||||
context.Body = context.Body.Replace("\r", "");
|
||||
}
|
||||
notificationSummary = context.Body.Length > 100 ? (context.Body.Substring(0, 97) + "...") : context.Body;
|
||||
_notificationSummary = context.Body.Length > 100 ? (context.Body.Substring(0, 97) + "...") : context.Body;
|
||||
}
|
||||
@if (context.IsRead)
|
||||
{
|
||||
@notificationSummary
|
||||
@_notificationSummary
|
||||
}
|
||||
else
|
||||
{
|
||||
<b>@notificationSummary</b>
|
||||
<b>@_notificationSummary</b>
|
||||
}
|
||||
</td>
|
||||
</Detail>
|
||||
@ -285,9 +298,9 @@
|
||||
}
|
||||
else
|
||||
{
|
||||
@if (notifications.Any())
|
||||
@if (_notifications.Any())
|
||||
{
|
||||
<Pager Items="@notifications">
|
||||
<Pager Items="@_notifications">
|
||||
<Header>
|
||||
<th style="width: 1px;"></th>
|
||||
<th style="width: 1px;"></th>
|
||||
@ -324,15 +337,15 @@
|
||||
context.Body = context.Body.Replace("\n", "");
|
||||
context.Body = context.Body.Replace("\r", "");
|
||||
}
|
||||
notificationSummary = context.Body.Length > 100 ? (context.Body.Substring(0, 97) + "...") : context.Body;
|
||||
_notificationSummary = context.Body.Length > 100 ? (context.Body.Substring(0, 97) + "...") : context.Body;
|
||||
}
|
||||
@if (context.IsRead)
|
||||
{
|
||||
@notificationSummary
|
||||
@_notificationSummary
|
||||
}
|
||||
else
|
||||
{
|
||||
<b>@notificationSummary</b>
|
||||
<b>@_notificationSummary</b>
|
||||
}
|
||||
</td>
|
||||
</Detail>
|
||||
@ -354,29 +367,32 @@
|
||||
}
|
||||
|
||||
@code {
|
||||
private List<Models.TimeZone> _timezones;
|
||||
private bool _initialized = false;
|
||||
private string _passwordrequirements;
|
||||
private string username = string.Empty;
|
||||
private string _username = string.Empty;
|
||||
private string _password = string.Empty;
|
||||
private string _passwordtype = "password";
|
||||
private string _togglepassword = string.Empty;
|
||||
private string confirm = string.Empty;
|
||||
private bool allowtwofactor = false;
|
||||
private string twofactor = "False";
|
||||
private string email = string.Empty;
|
||||
private string displayname = string.Empty;
|
||||
private FileManager filemanager;
|
||||
private int folderid = -1;
|
||||
private int photofileid = -1;
|
||||
private File photo = null;
|
||||
private string _ImageFiles = string.Empty;
|
||||
private List<Profile> profiles;
|
||||
private Dictionary<string, string> userSettings;
|
||||
private string category = string.Empty;
|
||||
private string _confirm = string.Empty;
|
||||
private bool _allowtwofactor = false;
|
||||
private string _twofactor = "False";
|
||||
private string _email = string.Empty;
|
||||
private string _displayname = string.Empty;
|
||||
private FileManager _filemanager;
|
||||
private int _folderid = -1;
|
||||
private string _timezoneid = string.Empty;
|
||||
private int _photofileid = -1;
|
||||
private File _photo = null;
|
||||
private string _imagefiles = string.Empty;
|
||||
|
||||
private string filter = "to";
|
||||
private List<Notification> notifications;
|
||||
private string notificationSummary = string.Empty;
|
||||
private List<Profile> _profiles;
|
||||
private Dictionary<string, string> _userSettings;
|
||||
private string _category = string.Empty;
|
||||
|
||||
private string _filter = "to";
|
||||
private List<Notification> _notifications;
|
||||
private string _notificationSummary = string.Empty;
|
||||
|
||||
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.View;
|
||||
|
||||
@ -386,17 +402,19 @@
|
||||
{
|
||||
_passwordrequirements = await UserService.GetPasswordRequirementsAsync(PageState.Site.SiteId);
|
||||
_togglepassword = SharedLocalizer["ShowPassword"];
|
||||
allowtwofactor = (SettingService.GetSetting(PageState.Site.Settings, "LoginOptions:TwoFactor", "false") == "true");
|
||||
profiles = await ProfileService.GetProfilesAsync(ModuleState.SiteId);
|
||||
_allowtwofactor = (SettingService.GetSetting(PageState.Site.Settings, "LoginOptions:TwoFactor", "false") == "true");
|
||||
_profiles = await ProfileService.GetProfilesAsync(ModuleState.SiteId);
|
||||
_timezones = await TimeZoneService.GetTimeZonesAsync();
|
||||
|
||||
if (PageState.User != null)
|
||||
{
|
||||
username = PageState.User.Username;
|
||||
twofactor = PageState.User.TwoFactorRequired.ToString();
|
||||
email = PageState.User.Email;
|
||||
displayname = PageState.User.DisplayName;
|
||||
_username = PageState.User.Username;
|
||||
_twofactor = PageState.User.TwoFactorRequired.ToString();
|
||||
_email = PageState.User.Email;
|
||||
_displayname = PageState.User.DisplayName;
|
||||
_timezoneid = PageState.User.TimeZoneId;
|
||||
|
||||
if (string.IsNullOrEmpty(email))
|
||||
if (string.IsNullOrEmpty(_email))
|
||||
{
|
||||
AddModuleMessage(Localizer["Message.User.NoEmail"], MessageType.Warning);
|
||||
}
|
||||
@ -405,24 +423,24 @@
|
||||
var folder = await FolderService.GetFolderAsync(ModuleState.SiteId, PageState.User.FolderPath);
|
||||
if (folder != null)
|
||||
{
|
||||
folderid = folder.FolderId;
|
||||
_folderid = folder.FolderId;
|
||||
}
|
||||
|
||||
if (PageState.User.PhotoFileId != null)
|
||||
{
|
||||
photofileid = PageState.User.PhotoFileId.Value;
|
||||
photo = await FileService.GetFileAsync(photofileid);
|
||||
_photofileid = PageState.User.PhotoFileId.Value;
|
||||
_photo = await FileService.GetFileAsync(_photofileid);
|
||||
}
|
||||
else
|
||||
{
|
||||
photofileid = -1;
|
||||
photo = null;
|
||||
_photofileid = -1;
|
||||
_photo = null;
|
||||
}
|
||||
|
||||
userSettings = PageState.User.Settings;
|
||||
_userSettings = PageState.User.Settings;
|
||||
var sitesettings = await SettingService.GetSiteSettingsAsync(PageState.Site.SiteId);
|
||||
_ImageFiles = SettingService.GetSetting(userSettings, "ImageFiles", Constants.ImageFiles);
|
||||
_ImageFiles = (string.IsNullOrEmpty(_ImageFiles)) ? Constants.ImageFiles : _ImageFiles;
|
||||
_imagefiles = SettingService.GetSetting(_userSettings, "ImageFiles", Constants.ImageFiles);
|
||||
_imagefiles = (string.IsNullOrEmpty(_imagefiles)) ? Constants.ImageFiles : _imagefiles;
|
||||
|
||||
await LoadNotificationsAsync();
|
||||
|
||||
@ -442,13 +460,13 @@
|
||||
|
||||
private async Task LoadNotificationsAsync()
|
||||
{
|
||||
notifications = await NotificationService.GetNotificationsAsync(PageState.Site.SiteId, filter, PageState.User.UserId);
|
||||
notifications = notifications.Where(item => item.DeletedBy != PageState.User.Username).ToList();
|
||||
_notifications = await NotificationService.GetNotificationsAsync(PageState.Site.SiteId, _filter, PageState.User.UserId);
|
||||
_notifications = _notifications.Where(item => item.DeletedBy != PageState.User.Username).ToList();
|
||||
}
|
||||
|
||||
private string GetProfileValue(string SettingName, string DefaultValue)
|
||||
{
|
||||
string value = SettingService.GetSetting(userSettings, SettingName, DefaultValue);
|
||||
string value = SettingService.GetSetting(_userSettings, SettingName, DefaultValue);
|
||||
if (value.Contains("]"))
|
||||
{
|
||||
value = value.Substring(value.IndexOf("]") + 1);
|
||||
@ -460,38 +478,39 @@
|
||||
{
|
||||
try
|
||||
{
|
||||
if (username != string.Empty && email != string.Empty)
|
||||
if (_username != string.Empty && _email != string.Empty)
|
||||
{
|
||||
if (_password == confirm)
|
||||
if (_password == _confirm)
|
||||
{
|
||||
if (ValidateProfiles())
|
||||
{
|
||||
var user = PageState.User;
|
||||
user.Username = username;
|
||||
user.Username = _username;
|
||||
user.Password = _password;
|
||||
user.TwoFactorRequired = bool.Parse(twofactor);
|
||||
user.Email = email;
|
||||
user.DisplayName = (displayname == string.Empty ? username : displayname);
|
||||
user.PhotoFileId = filemanager.GetFileId();
|
||||
user.TwoFactorRequired = bool.Parse(_twofactor);
|
||||
user.Email = _email;
|
||||
user.DisplayName = (_displayname == string.Empty ? _username : _displayname);
|
||||
user.TimeZoneId = _timezoneid;
|
||||
user.PhotoFileId = _filemanager.GetFileId();
|
||||
if (user.PhotoFileId == -1)
|
||||
{
|
||||
user.PhotoFileId = null;
|
||||
}
|
||||
if (user.PhotoFileId != null)
|
||||
{
|
||||
photofileid = user.PhotoFileId.Value;
|
||||
photo = await FileService.GetFileAsync(photofileid);
|
||||
_photofileid = user.PhotoFileId.Value;
|
||||
_photo = await FileService.GetFileAsync(_photofileid);
|
||||
}
|
||||
else
|
||||
{
|
||||
photofileid = -1;
|
||||
photo = null;
|
||||
_photofileid = -1;
|
||||
_photo = null;
|
||||
}
|
||||
|
||||
user = await UserService.UpdateUserAsync(user);
|
||||
if (user != null)
|
||||
{
|
||||
await SettingService.UpdateUserSettingsAsync(userSettings, PageState.User.UserId);
|
||||
await SettingService.UpdateUserSettingsAsync(_userSettings, PageState.User.UserId);
|
||||
await logger.LogInformation("User Profile Saved");
|
||||
|
||||
if (!string.IsNullOrEmpty(PageState.ReturnUrl))
|
||||
@ -557,12 +576,12 @@
|
||||
|
||||
private bool ValidateProfiles()
|
||||
{
|
||||
foreach (Profile profile in profiles)
|
||||
foreach (Profile profile in _profiles)
|
||||
{
|
||||
var value = GetProfileValue(profile.Name, string.Empty);
|
||||
if (string.IsNullOrEmpty(value) && !string.IsNullOrEmpty(profile.DefaultValue))
|
||||
{
|
||||
userSettings = SettingService.SetSetting(userSettings, profile.Name, profile.DefaultValue);
|
||||
_userSettings = SettingService.SetSetting(_userSettings, profile.Name, profile.DefaultValue);
|
||||
}
|
||||
if (!profile.IsPrivate || UserSecurity.IsAuthorized(PageState.User, RoleNames.Admin))
|
||||
{
|
||||
@ -594,7 +613,7 @@
|
||||
private void ProfileChanged(ChangeEventArgs e, string SettingName)
|
||||
{
|
||||
var value = (string)e.Value;
|
||||
userSettings = SettingService.SetSetting(userSettings, SettingName, value);
|
||||
_userSettings = SettingService.SetSetting(_userSettings, SettingName, value);
|
||||
}
|
||||
|
||||
private async Task Delete(Notification Notification)
|
||||
@ -624,7 +643,7 @@
|
||||
|
||||
private async void FilterChanged(ChangeEventArgs e)
|
||||
{
|
||||
filter = (string)e.Value;
|
||||
_filter = (string)e.Value;
|
||||
await LoadNotificationsAsync();
|
||||
StateHasChanged();
|
||||
}
|
||||
@ -634,7 +653,7 @@
|
||||
try
|
||||
{
|
||||
ShowProgressIndicator();
|
||||
foreach(var Notification in notifications)
|
||||
foreach(var Notification in _notifications)
|
||||
{
|
||||
if (!Notification.IsDeleted)
|
||||
{
|
||||
|
@ -5,6 +5,7 @@
|
||||
@inject IUserService UserService
|
||||
@inject IProfileService ProfileService
|
||||
@inject ISettingService SettingService
|
||||
@inject ITimeZoneService TimeZoneService
|
||||
@inject IStringLocalizer<Add> Localizer
|
||||
@inject IStringLocalizer<SharedResources> SharedLocalizer
|
||||
|
||||
@ -12,7 +13,7 @@
|
||||
{
|
||||
<TabStrip>
|
||||
<TabPanel Name="Identity" ResourceKey="Identity">
|
||||
@if (profiles != null)
|
||||
@if (_profiles != null)
|
||||
{
|
||||
<div class="container">
|
||||
<div class="row mb-1 align-items-center">
|
||||
@ -33,6 +34,18 @@
|
||||
<input id="displayname" class="form-control" @bind="@_displayname" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="timezone" HelpText="Your time zone" ResourceKey="TimeZone">Time Zone:</Label>
|
||||
<div class="col-sm-9">
|
||||
<select id="timezone" class="form-select" @bind="@_timezoneid">
|
||||
<option value=""><@SharedLocalizer["Not Specified"]></option>
|
||||
@foreach (var timezone in _timezones)
|
||||
{
|
||||
<option value="@timezone.Id">@timezone.DisplayName</option>
|
||||
}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="notify" HelpText="Indicate if new users should receive an email notification" ResourceKey="Notify">Notify? </Label>
|
||||
<div class="col-sm-9">
|
||||
@ -48,15 +61,15 @@
|
||||
<TabPanel Name="Profile" ResourceKey="Profile">
|
||||
<div class="container">
|
||||
<div class="row mb-1 align-items-center">
|
||||
@foreach (Profile profile in profiles)
|
||||
@foreach (Profile profile in _profiles)
|
||||
{
|
||||
var p = profile;
|
||||
if (p.Category != category)
|
||||
if (p.Category != _category)
|
||||
{
|
||||
<div class="col text-center pb-2">
|
||||
<strong>@p.Category</strong>
|
||||
</div>
|
||||
category = p.Category;
|
||||
_category = p.Category;
|
||||
}
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="@p.Name" HelpText="@p.Description">@p.Title</Label>
|
||||
@ -103,14 +116,16 @@
|
||||
|
||||
|
||||
@code {
|
||||
private List<Models.TimeZone> _timezones;
|
||||
private bool _initialized = false;
|
||||
private string _username = string.Empty;
|
||||
private string _email = string.Empty;
|
||||
private string _displayname = string.Empty;
|
||||
private string _timezoneid = string.Empty;
|
||||
private string _notify = "True";
|
||||
private List<Profile> profiles;
|
||||
private Dictionary<string, string> settings;
|
||||
private string category = string.Empty;
|
||||
private List<Profile> _profiles;
|
||||
private Dictionary<string, string> _settings;
|
||||
private string _category = string.Empty;
|
||||
|
||||
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Edit;
|
||||
|
||||
@ -118,8 +133,10 @@
|
||||
{
|
||||
try
|
||||
{
|
||||
profiles = await ProfileService.GetProfilesAsync(ModuleState.SiteId);
|
||||
settings = new Dictionary<string, string>();
|
||||
_timezones = await TimeZoneService.GetTimeZonesAsync();
|
||||
_profiles = await ProfileService.GetProfilesAsync(ModuleState.SiteId);
|
||||
_settings = new Dictionary<string, string>();
|
||||
_timezoneid = PageState.Site.TimeZoneId;
|
||||
_initialized = true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
@ -131,7 +148,7 @@
|
||||
|
||||
private string GetProfileValue(string SettingName, string DefaultValue)
|
||||
{
|
||||
string value = SettingService.GetSetting(settings, SettingName, DefaultValue);
|
||||
string value = SettingService.GetSetting(_settings, SettingName, DefaultValue);
|
||||
if (value.Contains("]"))
|
||||
{
|
||||
value = value.Substring(value.IndexOf("]") + 1);
|
||||
@ -153,6 +170,7 @@
|
||||
user.Password = ""; // will be auto generated
|
||||
user.Email = _email;
|
||||
user.DisplayName = string.IsNullOrWhiteSpace(_displayname) ? _username : _displayname;
|
||||
user.TimeZoneId = _timezoneid;
|
||||
user.PhotoFileId = null;
|
||||
user.SuppressNotification = !bool.Parse(_notify);
|
||||
|
||||
@ -160,7 +178,7 @@
|
||||
|
||||
if (user != null)
|
||||
{
|
||||
await SettingService.UpdateUserSettingsAsync(settings, user.UserId);
|
||||
await SettingService.UpdateUserSettingsAsync(_settings, user.UserId);
|
||||
await logger.LogInformation("User Created {User}", user);
|
||||
NavigationManager.NavigateTo(NavigateUrl());
|
||||
}
|
||||
@ -185,12 +203,12 @@
|
||||
|
||||
private bool ValidateProfiles()
|
||||
{
|
||||
foreach (Profile profile in profiles)
|
||||
foreach (Profile profile in _profiles)
|
||||
{
|
||||
var value = GetProfileValue(profile.Name, string.Empty);
|
||||
if (string.IsNullOrEmpty(value) && !string.IsNullOrEmpty(profile.DefaultValue))
|
||||
{
|
||||
settings = SettingService.SetSetting(settings, profile.Name, profile.DefaultValue);
|
||||
_settings = SettingService.SetSetting(_settings, profile.Name, profile.DefaultValue);
|
||||
}
|
||||
if (!profile.IsPrivate || UserSecurity.IsAuthorized(PageState.User, RoleNames.Admin))
|
||||
{
|
||||
@ -217,6 +235,6 @@
|
||||
private void ProfileChanged(ChangeEventArgs e, string SettingName)
|
||||
{
|
||||
var value = (string)e.Value;
|
||||
settings = SettingService.SetSetting(settings, SettingName, value);
|
||||
_settings = SettingService.SetSetting(_settings, SettingName, value);
|
||||
}
|
||||
}
|
||||
|
@ -6,6 +6,7 @@
|
||||
@inject IProfileService ProfileService
|
||||
@inject ISettingService SettingService
|
||||
@inject IFileService FileService
|
||||
@inject ITimeZoneService TimeZoneService
|
||||
@inject IServiceProvider ServiceProvider
|
||||
@inject IStringLocalizer<Edit> Localizer
|
||||
@inject IStringLocalizer<SharedResources> SharedLocalizer
|
||||
@ -17,13 +18,13 @@
|
||||
<ModuleMessage Message="@_passwordrequirements" Type="MessageType.Info" />
|
||||
<div class="container">
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="username" HelpText="The unique username for a user. Note that this field can not be modified." ResourceKey="Username"></Label>
|
||||
<Label Class="col-sm-3" For="username" HelpText="The unique username for a user. Note that this field can not be modified." ResourceKey="Username">Username:</Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="username" class="form-control" @bind="@username" readonly />
|
||||
<input id="username" class="form-control" @bind="@_username" readonly />
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="password" HelpText="The user's password. Please choose a password which is sufficiently secure." ResourceKey="Password"></Label>
|
||||
<Label Class="col-sm-3" For="password" HelpText="The user's password. Please choose a password which is sufficiently secure." ResourceKey="Password">Password:</Label>
|
||||
<div class="col-sm-9">
|
||||
<div class="input-group">
|
||||
<input id="password" type="@_passwordtype" class="form-control" @bind="@_password" autocomplete="new-password" />
|
||||
@ -32,32 +33,53 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="confirm" HelpText="Please enter the password again to confirm it matches with the value above" ResourceKey="Confirm"></Label>
|
||||
<Label Class="col-sm-3" For="confirm" HelpText="Please enter the password again to confirm it matches with the value above" ResourceKey="Confirm">Confirm Password:</Label>
|
||||
<div class="col-sm-9">
|
||||
<div class="input-group">
|
||||
<input id="confirm" type="@_passwordtype" class="form-control" @bind="@confirm" autocomplete="new-password" />
|
||||
<input id="confirm" type="@_passwordtype" class="form-control" @bind="@_confirm" 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="email" HelpText="The email address where the user will receive notifications" ResourceKey="Email"></Label>
|
||||
<Label Class="col-sm-3" For="email" HelpText="The email address where the user will receive notifications" ResourceKey="Email">Email:</Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="email" class="form-control" @bind="@email" />
|
||||
<input id="email" class="form-control" @bind="@_email" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="displayname" HelpText="The full name of the user" ResourceKey="DisplayName"></Label>
|
||||
<Label Class="col-sm-3" For="confirmed" HelpText="Indicates if the user's email is verified" ResourceKey="Confirmed">Confirmed?</Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="displayname" class="form-control" @bind="@displayname" />
|
||||
<select id="confirmed" class="form-select" @bind="@_confirmed">
|
||||
<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="displayname" HelpText="The full name of the user" ResourceKey="DisplayName">Full Name:</Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="displayname" class="form-control" @bind="@_displayname" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="timezone" HelpText="Your time zone" ResourceKey="TimeZone">Time Zone:</Label>
|
||||
<div class="col-sm-9">
|
||||
<select id="timezone" class="form-select" @bind="@_timezoneid">
|
||||
<option value=""><@SharedLocalizer["Not Specified"]></option>
|
||||
@foreach (var timezone in _timezones)
|
||||
{
|
||||
<option value="@timezone.Id">@timezone.DisplayName</option>
|
||||
}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
@if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host))
|
||||
{
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="isdeleted" HelpText="Indicate if the user is active" ResourceKey="IsDeleted"></Label>
|
||||
<Label Class="col-sm-3" For="isdeleted" HelpText="Indicate if the user is active" ResourceKey="IsDeleted">Deleted?</Label>
|
||||
<div class="col-sm-9">
|
||||
<select id="isdeleted" class="form-select" @bind="@isdeleted">
|
||||
<select id="isdeleted" class="form-select" @bind="@_isdeleted">
|
||||
<option value="True">@SharedLocalizer["Yes"]</option>
|
||||
<option value="False">@SharedLocalizer["No"]</option>
|
||||
</select>
|
||||
@ -65,15 +87,15 @@
|
||||
</div>
|
||||
}
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="lastlogin" HelpText="The date and time when the user last signed in" ResourceKey="LastLogin"></Label>
|
||||
<Label Class="col-sm-3" For="lastlogin" HelpText="The date and time when the user last signed in" ResourceKey="LastLogin">Last Login:</Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="lastlogin" class="form-control" @bind="@lastlogin" readonly />
|
||||
<input id="lastlogin" class="form-control" @bind="@_lastlogin" readonly />
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="lastipaddress" HelpText="The IP Address of the user recorded during their last login" ResourceKey="LastIPAddress"></Label>
|
||||
<Label Class="col-sm-3" For="lastipaddress" HelpText="The IP Address of the user recorded during their last login" ResourceKey="LastIPAddress">Last IP Address:</Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="lastipaddress" class="form-control" @bind="@lastipaddress" readonly />
|
||||
<input id="lastipaddress" class="form-control" @bind="@_lastipaddress" readonly />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -81,15 +103,15 @@
|
||||
<TabPanel Name="Profile" ResourceKey="Profile">
|
||||
<div class="container">
|
||||
<div class="row mb-1 align-items-center">
|
||||
@foreach (Profile profile in profiles)
|
||||
@foreach (Profile profile in _profiles)
|
||||
{
|
||||
var p = profile;
|
||||
if (p.Category != category)
|
||||
if (p.Category != _category)
|
||||
{
|
||||
<div class="col text-center pb-2">
|
||||
<strong>@p.Category</strong>
|
||||
</div>
|
||||
category = p.Category;
|
||||
_category = p.Category;
|
||||
}
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="@p.Name" HelpText="@p.Description">@p.Title</Label>
|
||||
@ -128,47 +150,50 @@
|
||||
</div>
|
||||
</TabPanel>
|
||||
</TabStrip>
|
||||
|
||||
<br />
|
||||
<button type="button" class="btn btn-success" @onclick="SaveUser">@SharedLocalizer["Save"]</button>
|
||||
<NavLink class="btn btn-secondary" href="@NavigateUrl()">@SharedLocalizer["Cancel"]</NavLink>
|
||||
@if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Admin) && PageState.Runtime != Shared.Runtime.Hybrid && !ishost)
|
||||
@if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Admin) && PageState.Runtime != Shared.Runtime.Hybrid && !_ishost)
|
||||
{
|
||||
<button type="button" class="btn btn-primary ms-1" @onclick="ImpersonateUser">@Localizer["Impersonate"]</button>
|
||||
}
|
||||
@if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host) && isdeleted == "True")
|
||||
@if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host) && _isdeleted == "True")
|
||||
{
|
||||
<ActionDialog Header="Delete User" Message="Are You Sure You Wish To Permanently Delete This User?" Action="Delete" Security="SecurityAccessLevel.Host" Class="btn btn-danger" OnClick="@(async () => await DeleteUser())" ResourceKey="DeleteUser" />
|
||||
}
|
||||
<br /><br />
|
||||
<AuditInfo CreatedBy="@createdby" CreatedOn="@createdon" ModifiedBy="@modifiedby" ModifiedOn="@modifiedon" DeletedBy="@deletedby" DeletedOn="@deletedon"></AuditInfo>
|
||||
<AuditInfo CreatedBy="@_createdby" CreatedOn="@_createdon" ModifiedBy="@_modifiedby" ModifiedOn="@_modifiedon" DeletedBy="@_deletedby" DeletedOn="@_deletedon"></AuditInfo>
|
||||
}
|
||||
|
||||
@code {
|
||||
private List<Models.TimeZone> _timezones;
|
||||
private bool _initialized = false;
|
||||
private string _passwordrequirements;
|
||||
private int userid;
|
||||
private string username = string.Empty;
|
||||
private int _userid;
|
||||
private string _username = string.Empty;
|
||||
private string _password = string.Empty;
|
||||
private string _passwordtype = "password";
|
||||
private string _togglepassword = string.Empty;
|
||||
private string confirm = string.Empty;
|
||||
private string email = string.Empty;
|
||||
private string displayname = string.Empty;
|
||||
private string isdeleted;
|
||||
private string lastlogin;
|
||||
private string lastipaddress;
|
||||
private bool ishost = false;
|
||||
private string _confirm = string.Empty;
|
||||
private string _email = string.Empty;
|
||||
private string _confirmed = string.Empty;
|
||||
private string _displayname = string.Empty;
|
||||
private string _timezoneid = string.Empty;
|
||||
private string _isdeleted;
|
||||
private string _lastlogin;
|
||||
private string _lastipaddress;
|
||||
private bool _ishost = false;
|
||||
|
||||
private List<Profile> profiles;
|
||||
private Dictionary<string, string> userSettings;
|
||||
private string category = string.Empty;
|
||||
private List<Profile> _profiles;
|
||||
private Dictionary<string, string> _settings;
|
||||
private string _category = string.Empty;
|
||||
|
||||
private string createdby;
|
||||
private DateTime createdon;
|
||||
private string modifiedby;
|
||||
private DateTime modifiedon;
|
||||
private string deletedby;
|
||||
private DateTime? deletedon;
|
||||
private string _createdby;
|
||||
private DateTime _createdon;
|
||||
private string _modifiedby;
|
||||
private DateTime _modifiedon;
|
||||
private string _deletedby;
|
||||
private DateTime? _deletedon;
|
||||
|
||||
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Edit;
|
||||
|
||||
@ -178,29 +203,32 @@
|
||||
{
|
||||
_passwordrequirements = await UserService.GetPasswordRequirementsAsync(PageState.Site.SiteId);
|
||||
_togglepassword = SharedLocalizer["ShowPassword"];
|
||||
profiles = await ProfileService.GetProfilesAsync(PageState.Site.SiteId);
|
||||
_profiles = await ProfileService.GetProfilesAsync(PageState.Site.SiteId);
|
||||
_timezones = await TimeZoneService.GetTimeZonesAsync();
|
||||
|
||||
if (PageState.QueryString.ContainsKey("id") && int.TryParse(PageState.QueryString["id"], out int UserId))
|
||||
{
|
||||
userid = UserId;
|
||||
var user = await UserService.GetUserAsync(userid, PageState.Site.SiteId);
|
||||
_userid = UserId;
|
||||
var user = await UserService.GetUserAsync(_userid, PageState.Site.SiteId);
|
||||
if (user != null)
|
||||
{
|
||||
username = user.Username;
|
||||
email = user.Email;
|
||||
displayname = user.DisplayName;
|
||||
isdeleted = user.IsDeleted.ToString();
|
||||
lastlogin = string.Format("{0:MMM dd yyyy HH:mm:ss}", user.LastLoginOn);
|
||||
lastipaddress = user.LastIPAddress;
|
||||
ishost = UserSecurity.ContainsRole(user.Roles, RoleNames.Host);
|
||||
_username = user.Username;
|
||||
_email = user.Email;
|
||||
_confirmed = user.EmailConfirmed.ToString();
|
||||
_displayname = user.DisplayName;
|
||||
_timezoneid = PageState.User.TimeZoneId;
|
||||
_isdeleted = user.IsDeleted.ToString();
|
||||
_lastlogin = string.Format("{0:MMM dd yyyy HH:mm:ss}", UtcToLocal(user.LastLoginOn));
|
||||
_lastipaddress = user.LastIPAddress;
|
||||
_ishost = UserSecurity.ContainsRole(user.Roles, RoleNames.Host);
|
||||
|
||||
userSettings = user.Settings;
|
||||
createdby = user.CreatedBy;
|
||||
createdon = user.CreatedOn;
|
||||
modifiedby = user.ModifiedBy;
|
||||
modifiedon = user.ModifiedOn;
|
||||
deletedby = user.DeletedBy;
|
||||
deletedon = user.DeletedOn;
|
||||
_settings = user.Settings;
|
||||
_createdby = user.CreatedBy;
|
||||
_createdon = user.CreatedOn;
|
||||
_modifiedby = user.ModifiedBy;
|
||||
_modifiedon = user.ModifiedOn;
|
||||
_deletedby = user.DeletedBy;
|
||||
_deletedon = user.DeletedOn;
|
||||
}
|
||||
}
|
||||
|
||||
@ -208,14 +236,14 @@
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
await logger.LogError(ex, "Error Loading User {UserId} {Error}", userid, ex.Message);
|
||||
await logger.LogError(ex, "Error Loading User {UserId} {Error}", _userid, ex.Message);
|
||||
AddModuleMessage(Localizer["Error.User.Load"], MessageType.Error);
|
||||
}
|
||||
}
|
||||
|
||||
private string GetProfileValue(string SettingName, string DefaultValue)
|
||||
{
|
||||
string value = SettingService.GetSetting(userSettings, SettingName, DefaultValue);
|
||||
string value = SettingService.GetSetting(_settings, SettingName, DefaultValue);
|
||||
if (value.Contains("]"))
|
||||
{
|
||||
value = value.Substring(value.IndexOf("]") + 1);
|
||||
@ -227,27 +255,29 @@
|
||||
{
|
||||
try
|
||||
{
|
||||
if (username != string.Empty && email != string.Empty)
|
||||
if (_username != string.Empty && _email != string.Empty)
|
||||
{
|
||||
if (_password == confirm)
|
||||
if (_password == _confirm)
|
||||
{
|
||||
if (ValidateProfiles())
|
||||
{
|
||||
var user = await UserService.GetUserAsync(userid, PageState.Site.SiteId);
|
||||
var user = await UserService.GetUserAsync(_userid, PageState.Site.SiteId);
|
||||
user.SiteId = PageState.Site.SiteId;
|
||||
user.Username = username;
|
||||
user.Username = _username;
|
||||
user.Password = _password;
|
||||
user.Email = email;
|
||||
user.DisplayName = string.IsNullOrWhiteSpace(displayname) ? username : displayname;
|
||||
user.Email = _email;
|
||||
user.EmailConfirmed = bool.Parse(_confirmed);
|
||||
user.DisplayName = string.IsNullOrWhiteSpace(_displayname) ? _username : _displayname;
|
||||
user.TimeZoneId = _timezoneid;
|
||||
if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host))
|
||||
{
|
||||
user.IsDeleted = (isdeleted == null ? true : Boolean.Parse(isdeleted));
|
||||
user.IsDeleted = (_isdeleted == null ? true : Boolean.Parse(_isdeleted));
|
||||
}
|
||||
|
||||
user = await UserService.UpdateUserAsync(user);
|
||||
if (user != null)
|
||||
{
|
||||
await SettingService.UpdateUserSettingsAsync(userSettings, user.UserId);
|
||||
await SettingService.UpdateUserSettingsAsync(_settings, user.UserId);
|
||||
await logger.LogInformation("User Saved {User}", user);
|
||||
NavigationManager.NavigateTo(NavigateUrl());
|
||||
}
|
||||
@ -269,7 +299,7 @@
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
await logger.LogError(ex, "Error Saving User {Username} {Email} {Error}", username, email, ex.Message);
|
||||
await logger.LogError(ex, "Error Saving User {Username} {Email} {Error}", _username, _email, ex.Message);
|
||||
AddModuleMessage(Localizer["Error.User.Save"], MessageType.Error);
|
||||
}
|
||||
}
|
||||
@ -278,17 +308,17 @@
|
||||
{
|
||||
try
|
||||
{
|
||||
await logger.LogInformation(LogFunction.Security, "User {Username} Impersonated By Administrator {Administrator}", username, PageState.User.Username);
|
||||
await logger.LogInformation(LogFunction.Security, "User {Username} Impersonated By Administrator {Administrator}", _username, PageState.User.Username);
|
||||
|
||||
// post back to the server so that the cookies are set correctly
|
||||
var interop = new Interop(JSRuntime);
|
||||
var fields = new { __RequestVerificationToken = SiteState.AntiForgeryToken, username = username, returnurl = PageState.Alias.Path };
|
||||
var fields = new { __RequestVerificationToken = SiteState.AntiForgeryToken, username = _username, returnurl = PageState.Alias.Path };
|
||||
string url = Utilities.TenantUrl(PageState.Alias, "/pages/impersonate/");
|
||||
await interop.SubmitForm(url, fields);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
await logger.LogError(ex, "Error Impersonating User {Username} {Error}", username, ex.Message);
|
||||
await logger.LogError(ex, "Error Impersonating User {Username} {Error}", _username, ex.Message);
|
||||
AddModuleMessage(Localizer["Error.User.Impersonate"], MessageType.Error);
|
||||
}
|
||||
}
|
||||
@ -297,9 +327,9 @@
|
||||
{
|
||||
try
|
||||
{
|
||||
if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host) && userid != PageState.User.UserId)
|
||||
if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host) && _userid != PageState.User.UserId)
|
||||
{
|
||||
var user = await UserService.GetUserAsync(userid, PageState.Site.SiteId);
|
||||
var user = await UserService.GetUserAsync(_userid, PageState.Site.SiteId);
|
||||
await UserService.DeleteUserAsync(user.UserId, PageState.Site.SiteId);
|
||||
await logger.LogInformation("User Permanently Deleted {User}", user);
|
||||
NavigationManager.NavigateTo(NavigateUrl());
|
||||
@ -307,19 +337,19 @@
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
await logger.LogError(ex, "Error Permanently Deleting User {UserId} {Error}", userid, ex.Message);
|
||||
await logger.LogError(ex, "Error Permanently Deleting User {UserId} {Error}", _userid, ex.Message);
|
||||
AddModuleMessage(Localizer["Error.DeleteUser"], MessageType.Error);
|
||||
}
|
||||
}
|
||||
|
||||
private bool ValidateProfiles()
|
||||
{
|
||||
foreach (Profile profile in profiles)
|
||||
foreach (Profile profile in _profiles)
|
||||
{
|
||||
var value = GetProfileValue(profile.Name, string.Empty);
|
||||
if (string.IsNullOrEmpty(value) && !string.IsNullOrEmpty(profile.DefaultValue))
|
||||
{
|
||||
userSettings = SettingService.SetSetting(userSettings, profile.Name, profile.DefaultValue);
|
||||
_settings = SettingService.SetSetting(_settings, profile.Name, profile.DefaultValue);
|
||||
}
|
||||
if (!profile.IsPrivate || UserSecurity.IsAuthorized(PageState.User, RoleNames.Admin))
|
||||
{
|
||||
@ -346,7 +376,7 @@
|
||||
private void ProfileChanged(ChangeEventArgs e, string SettingName)
|
||||
{
|
||||
var value = (string)e.Value;
|
||||
userSettings = SettingService.SetSetting(userSettings, SettingName, value);
|
||||
_settings = SettingService.SetSetting(_settings, SettingName, value);
|
||||
}
|
||||
|
||||
private void TogglePassword()
|
||||
|
@ -43,7 +43,7 @@ else
|
||||
<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>
|
||||
<td>@((context.User.LastLoginOn != DateTime.MinValue) ? string.Format("{0:dd-MMM-yyyy HH:mm:ss}", UtcToLocal(context.User.LastLoginOn)) : "")</td>
|
||||
</Row>
|
||||
</Pager>
|
||||
</TabPanel>
|
||||
@ -54,32 +54,26 @@ else
|
||||
<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
|
||||
@if (_allowregistration == "true")
|
||||
{
|
||||
<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>
|
||||
<Label Class="col-sm-3" For="registerurl" HelpText="Optionally provide a custom registration url" ResourceKey="RegisterUrl">Register Url:</Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="allowsitelogin" class="form-control" value="@SharedLocalizer["Yes"]" readonly />
|
||||
<input id="registerurl" class="form-control" @bind="@_registerurl" />
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="profileurl" HelpText="Optionally provide a custom profile url" ResourceKey="ProfileUrl">Profile Url:</Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="profileurl" class="form-control" @bind="@_profileurl" />
|
||||
</div>
|
||||
</div>
|
||||
@if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host))
|
||||
{
|
||||
<div class="row mb-1 align-items-center">
|
||||
@ -113,6 +107,15 @@ else
|
||||
</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))
|
||||
@ -412,6 +415,27 @@ 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>
|
||||
<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 Local 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>
|
||||
}
|
||||
}
|
||||
</Section>
|
||||
<Section Name="Token" Heading="Token Settings" ResourceKey="TokenSettings">
|
||||
@ -464,11 +488,13 @@ else
|
||||
private List<UserRole> users;
|
||||
|
||||
private string _allowregistration;
|
||||
private string _allowsitelogin;
|
||||
private string _registerurl;
|
||||
private string _profileurl;
|
||||
private string _twofactor;
|
||||
private string _cookiename;
|
||||
private string _cookieexpiration;
|
||||
private string _alwaysremember;
|
||||
private string _logouteverywhere;
|
||||
|
||||
private string _minimumlength;
|
||||
private string _uniquecharacters;
|
||||
@ -510,6 +536,8 @@ else
|
||||
private string _domainfilter;
|
||||
private string _createusers;
|
||||
private string _verifyusers;
|
||||
private string _allowhostrole;
|
||||
private string _allowsitelogin;
|
||||
|
||||
private string _secret;
|
||||
private string _secrettype = "password";
|
||||
@ -529,8 +557,9 @@ else
|
||||
await LoadUsersAsync(true);
|
||||
|
||||
var settings = await SettingService.GetSiteSettingsAsync(PageState.Site.SiteId);
|
||||
_allowregistration = PageState.Site.AllowRegistration.ToString();
|
||||
_allowsitelogin = SettingService.GetSetting(settings, "LoginOptions:AllowSiteLogin", "true");
|
||||
_allowregistration = PageState.Site.AllowRegistration.ToString().ToLower();
|
||||
_registerurl = SettingService.GetSetting(settings, "LoginOptions:RegisterUrl", "");
|
||||
_profileurl = SettingService.GetSetting(settings, "LoginOptions:ProfileUrl", "");
|
||||
|
||||
if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host))
|
||||
{
|
||||
@ -538,6 +567,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 +621,8 @@ 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");
|
||||
_allowsitelogin = SettingService.GetSetting(settings, "LoginOptions:AllowSiteLogin", "true");
|
||||
}
|
||||
|
||||
private async Task LoadUsersAsync(bool load)
|
||||
@ -648,14 +680,16 @@ else
|
||||
await SiteService.UpdateSiteAsync(site);
|
||||
|
||||
var settings = await SettingService.GetSiteSettingsAsync(site.SiteId);
|
||||
settings = SettingService.SetSetting(settings, "LoginOptions:AllowSiteLogin", _allowsitelogin, false);
|
||||
|
||||
if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host))
|
||||
{
|
||||
settings = SettingService.SetSetting(settings, "LoginOptions:RegisterUrl", _registerurl, false);
|
||||
settings = SettingService.SetSetting(settings, "LoginOptions:ProfileUrl", _profileurl, false);
|
||||
settings = SettingService.SetSetting(settings, "LoginOptions:TwoFactor", _twofactor, false);
|
||||
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 +727,8 @@ 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, "LoginOptions:AllowSiteLogin", _allowsitelogin, false);
|
||||
|
||||
settings = SettingService.SetSetting(settings, "JwtOptions:Secret", _secret, true);
|
||||
settings = SettingService.SetSetting(settings, "JwtOptions:Issuer", _issuer, true);
|
||||
|
@ -101,8 +101,8 @@
|
||||
_url = visitor.Url;
|
||||
_referrer = visitor.Referrer;
|
||||
_visits = visitor.Visits.ToString();
|
||||
_visited = visitor.VisitedOn.ToString(CultureInfo.CurrentCulture);
|
||||
_created = visitor.CreatedOn.ToString(CultureInfo.CurrentCulture);
|
||||
_visited = UtcToLocal(visitor.VisitedOn).Value.ToString(CultureInfo.CurrentCulture);
|
||||
_created = UtcToLocal(visitor.CreatedOn).Value.ToString(CultureInfo.CurrentCulture);
|
||||
|
||||
if (visitor.UserId != null)
|
||||
{
|
||||
|
@ -53,8 +53,8 @@ else
|
||||
</td>
|
||||
<td>@context.Language</td>
|
||||
<td>@context.Visits</td>
|
||||
<td>@context.VisitedOn</td>
|
||||
<td>@context.CreatedOn</td>
|
||||
<td>@UtcToLocal(context.VisitedOn)</td>
|
||||
<td>@UtcToLocal(context.CreatedOn)</td>
|
||||
</Row>
|
||||
</Pager>
|
||||
</TabPanel>
|
||||
@ -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,6 +152,7 @@ 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);
|
||||
|
@ -52,7 +52,7 @@
|
||||
|
||||
if (CreatedOn != null)
|
||||
{
|
||||
_text += $" {Localizer["On"]} <b>{CreatedOn.Value.ToString(DateTimeFormat)}</b>";
|
||||
_text += $" {Localizer["On"]} <b>{UtcToLocal(CreatedOn).Value.ToString(DateTimeFormat)}</ b >";
|
||||
}
|
||||
|
||||
_text += "</p>";
|
||||
@ -69,7 +69,7 @@
|
||||
|
||||
if (ModifiedOn != null)
|
||||
{
|
||||
_text += $" {Localizer["On"]} <b>{ModifiedOn.Value.ToString(DateTimeFormat)}</b>";
|
||||
_text += $" {Localizer["On"]} <b>{UtcToLocal(ModifiedOn).Value.ToString(DateTimeFormat)}</ b >";
|
||||
}
|
||||
|
||||
_text += "</p>";
|
||||
@ -86,7 +86,7 @@
|
||||
|
||||
if (DeletedOn != null)
|
||||
{
|
||||
_text += $" {Localizer["On"]} <b>{DeletedOn.Value.ToString(DateTimeFormat)}</b>";
|
||||
_text += $" {Localizer["On"]} <b>{UtcToLocal(DeletedOn).Value.ToString(DateTimeFormat)}</ b >";
|
||||
}
|
||||
|
||||
_text += "</p>";
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,22 +1,23 @@
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using Oqtane.Shared;
|
||||
using Oqtane.Models;
|
||||
using System.Threading.Tasks;
|
||||
using Oqtane.Services;
|
||||
using System;
|
||||
using Oqtane.Enums;
|
||||
using Oqtane.UI;
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.JSInterop;
|
||||
using System.Linq;
|
||||
using System.Dynamic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using Microsoft.JSInterop;
|
||||
using Oqtane.Enums;
|
||||
using Oqtane.Models;
|
||||
using Oqtane.Services;
|
||||
using Oqtane.Shared;
|
||||
using Oqtane.UI;
|
||||
|
||||
namespace Oqtane.Modules
|
||||
{
|
||||
public abstract class ModuleBase : ComponentBase, IModuleControl
|
||||
{
|
||||
private Logger _logger;
|
||||
private string _urlparametersstate;
|
||||
private string _urlparametersstate = string.Empty;
|
||||
private Dictionary<string, string> _urlparameters;
|
||||
private bool _scriptsloaded = false;
|
||||
|
||||
@ -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; }
|
||||
@ -61,7 +62,7 @@ namespace Oqtane.Modules
|
||||
public Dictionary<string, string> UrlParameters {
|
||||
get
|
||||
{
|
||||
if (_urlparametersstate == null || _urlparametersstate != PageState.UrlParameters)
|
||||
if (string.IsNullOrEmpty(_urlparametersstate) || _urlparametersstate != PageState.UrlParameters)
|
||||
{
|
||||
_urlparametersstate = PageState.UrlParameters;
|
||||
_urlparameters = GetUrlParameters(UrlParametersTemplate);
|
||||
@ -78,19 +79,22 @@ namespace Oqtane.Modules
|
||||
{
|
||||
List<Resource> resources = null;
|
||||
var type = GetType();
|
||||
if (type.BaseType == typeof(ModuleBase))
|
||||
if (type.IsSubclassOf(typeof(ModuleBase)))
|
||||
{
|
||||
if (type.IsSubclassOf(typeof(ModuleControlBase)))
|
||||
{
|
||||
if (Resources != null)
|
||||
{
|
||||
resources = Resources.Where(item => item.ResourceType == ResourceType.Script).ToList();
|
||||
}
|
||||
}
|
||||
else // ModuleBase
|
||||
{
|
||||
if (PageState.Page.Resources != null)
|
||||
{
|
||||
resources = PageState.Page.Resources.Where(item => item.ResourceType == ResourceType.Script && item.Level == ResourceLevel.Module && item.Namespace == type.Namespace).ToList();
|
||||
}
|
||||
}
|
||||
else // modulecontrolbase
|
||||
{
|
||||
if (Resources != null)
|
||||
{
|
||||
resources = Resources.Where(item => item.ResourceType == ResourceType.Script).ToList();
|
||||
}
|
||||
}
|
||||
if (resources != null && resources.Any())
|
||||
{
|
||||
@ -413,6 +417,118 @@ 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)
|
||||
{
|
||||
// Using StringBuilder avoids the performance penalty of repeated string allocations
|
||||
// that occur with string.Replace or string concatenation inside loops.
|
||||
var sb = new StringBuilder();
|
||||
var cache = new Dictionary<string, string>(); // Cache to store resolved tokens
|
||||
int index = 0;
|
||||
|
||||
// Loop through content to find and replace all tokens
|
||||
while (index < content.Length)
|
||||
{
|
||||
int start = content.IndexOf('[', index); // Find start of token
|
||||
if (start == -1)
|
||||
{
|
||||
sb.Append(content, index, content.Length - index); // Append remaining content
|
||||
break;
|
||||
}
|
||||
|
||||
int end = content.IndexOf(']', start); // Find end of token
|
||||
if (end == -1)
|
||||
{
|
||||
sb.Append(content, index, content.Length - index); // Append unmatched content
|
||||
break;
|
||||
}
|
||||
|
||||
sb.Append(content, index, start - index); // Append content before token
|
||||
|
||||
string token = content.Substring(start + 1, end - start - 1); // Extract token without brackets
|
||||
string[] parts = token.Split('|', 2); // Separate default fallback if present
|
||||
string key = parts[0];
|
||||
string fallback = parts.Length == 2 ? parts[1] : null;
|
||||
|
||||
if (!cache.TryGetValue(token, out string replacement)) // Check cache first
|
||||
{
|
||||
replacement = "[" + token + "]"; // Default replacement is original token
|
||||
string[] segments = key.Split(':');
|
||||
|
||||
if (segments.Length >= 2)
|
||||
{
|
||||
object current = GetTarget(segments[0], obj); // Start from root object
|
||||
for (int i = 1; i < segments.Length && current != null; i++)
|
||||
{
|
||||
var type = current.GetType();
|
||||
var prop = type.GetProperty(segments[i]);
|
||||
current = prop?.GetValue(current);
|
||||
}
|
||||
|
||||
if (current != null)
|
||||
{
|
||||
replacement = current.ToString();
|
||||
}
|
||||
else if (fallback != null)
|
||||
{
|
||||
replacement = fallback; // Use fallback if available
|
||||
}
|
||||
}
|
||||
cache[token] = replacement; // Store in cache
|
||||
}
|
||||
|
||||
sb.Append(replacement); // Append replacement value
|
||||
index = end + 1; // Move index past token
|
||||
}
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
// Resolve the object instance for a given object name
|
||||
// Easy to extend with additional object types
|
||||
private object GetTarget(string name, object obj)
|
||||
{
|
||||
return name switch
|
||||
{
|
||||
"ModuleState" => ModuleState,
|
||||
"PageState" => PageState,
|
||||
_ => (obj != null && obj.GetType().Name == name) ? obj : null // Fallback to obj
|
||||
};
|
||||
}
|
||||
|
||||
// date methods
|
||||
public DateTime? UtcToLocal(DateTime? datetime)
|
||||
{
|
||||
TimeZoneInfo timezone = null;
|
||||
if (PageState.User != null && !string.IsNullOrEmpty(PageState.User.TimeZoneId))
|
||||
{
|
||||
timezone = TimeZoneInfo.FindSystemTimeZoneById(PageState.User.TimeZoneId);
|
||||
}
|
||||
else if (!string.IsNullOrEmpty(PageState.Site.TimeZoneId))
|
||||
{
|
||||
timezone = TimeZoneInfo.FindSystemTimeZoneById(PageState.Site.TimeZoneId);
|
||||
}
|
||||
return Utilities.UtcAsLocalDateTime(datetime, timezone);
|
||||
}
|
||||
|
||||
public DateTime? LocalToUtc(DateTime? datetime)
|
||||
{
|
||||
TimeZoneInfo timezone = null;
|
||||
if (PageState.User != null && !string.IsNullOrEmpty(PageState.User.TimeZoneId))
|
||||
{
|
||||
timezone = TimeZoneInfo.FindSystemTimeZoneById(PageState.User.TimeZoneId);
|
||||
}
|
||||
else if (!string.IsNullOrEmpty(PageState.Site.TimeZoneId))
|
||||
{
|
||||
timezone = TimeZoneInfo.FindSystemTimeZoneById(PageState.Site.TimeZoneId);
|
||||
}
|
||||
return Utilities.LocalDateAndTimeAsUtc(datetime, timezone);
|
||||
}
|
||||
|
||||
// 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.3</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.3</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.5" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Authentication" Version="9.0.5" />
|
||||
<PackageReference Include="Microsoft.Extensions.Localization" Version="9.0.5" />
|
||||
<PackageReference Include="Microsoft.Extensions.Http" Version="9.0.5" />
|
||||
</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>
|
||||
|
@ -135,9 +135,6 @@
|
||||
<data name="Error.Folder.Save" xml:space="preserve">
|
||||
<value>Error Saving Folder</value>
|
||||
</data>
|
||||
<data name="Message.Folder.Files.InvalidDelete" xml:space="preserve">
|
||||
<value>Folder Has Files And Cannot Be Deleted</value>
|
||||
</data>
|
||||
<data name="Message.Folder.Subfolders.InvalidDelete" xml:space="preserve">
|
||||
<value>Folder Has Subfolders And Cannot Be Deleted</value>
|
||||
</data>
|
||||
|
@ -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>
|
||||
|
@ -121,10 +121,10 @@
|
||||
<value>Forgot Password</value>
|
||||
</data>
|
||||
<data name="Success.Account.Verified" xml:space="preserve">
|
||||
<value>User Account Verified Successfully. You Can Now Login With Your Username And Password Below.</value>
|
||||
<value>User Account Email Address Verified Successfully. You Can Now Login With Your Username And Password.</value>
|
||||
</data>
|
||||
<data name="Message.Account.NotVerified" xml:space="preserve">
|
||||
<value>User Account Could Not Be Verified. Please Contact Your Administrator For Further Instructions.</value>
|
||||
<value>User Account Email Address Could Not Be Verified. Please Contact Your Administrator For Further Instructions.</value>
|
||||
</data>
|
||||
<data name="Success.Account.Linked" xml:space="preserve">
|
||||
<value>User Account Linked Successfully. You Can Now Login With Your External Login Below.</value>
|
||||
@ -133,7 +133,7 @@
|
||||
<value>External Login Could Not Be Linked. Please Contact Your Administrator For Further Instructions.</value>
|
||||
</data>
|
||||
<data name="Error.Login.Fail" xml:space="preserve">
|
||||
<value>Login Failed. Please Remember That Passwords Are Case Sensitive. If You Have Attempted To Sign In Multiple Times Unsuccessfully, Your Account Will Be Locked Out For A Period Of Time. Note That User Accounts Require Verification When They Are Initially Created So You May Wish To Check Your Email If You Are A New User.</value>
|
||||
<value>Login Failed. Please Remember That Passwords Are Case Sensitive. If You Have Attempted To Sign In Multiple Times Unsuccessfully, Your Account Will Be Locked Out For A Period Of Time. Note That User Accounts Often Require Email Address Verification So You May Wish To Check Your Email For A Notification.</value>
|
||||
</data>
|
||||
<data name="Message.Required.UserInfo" xml:space="preserve">
|
||||
<value>Please Provide All Required Fields</value>
|
||||
|
@ -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>Check For Updates</value>
|
||||
</data>
|
||||
<data name="Success.Module.Synchronize" xml:space="preserve">
|
||||
<value>Module Information Has Been Retrieved From The Marketplace</value>
|
||||
</data>
|
||||
<data name="Error.Module.Synchronize" xml:space="preserve">
|
||||
<value>Error Retrieving Module Information From The Marketplace</value>
|
||||
</data>
|
||||
</root>
|
@ -121,7 +121,7 @@
|
||||
<value>Export</value>
|
||||
</data>
|
||||
<data name="Content.HelpText" xml:space="preserve">
|
||||
<value>The Exported Module Content</value>
|
||||
<value>Select the Export option and you will be able to view the module content</value>
|
||||
</data>
|
||||
<data name="Content.Text" xml:space="preserve">
|
||||
<value>Content: </value>
|
||||
@ -135,4 +135,25 @@
|
||||
<data name="Export Content" xml:space="preserve">
|
||||
<value>Export Content</value>
|
||||
</data>
|
||||
<data name="Content.Heading" xml:space="preserve">
|
||||
<value>Content</value>
|
||||
</data>
|
||||
<data name="File.Heading" xml:space="preserve">
|
||||
<value>File</value>
|
||||
</data>
|
||||
<data name="Folder.Text" xml:space="preserve">
|
||||
<value>Folder:</value>
|
||||
</data>
|
||||
<data name="Folder.HelpText" xml:space="preserve">
|
||||
<value>Select a folder where you wish to save the exported content</value>
|
||||
</data>
|
||||
<data name="Message.Content.Export" xml:space="preserve">
|
||||
<value>Please Select A Folder And Provide A Filename Before Choosing Export</value>
|
||||
</data>
|
||||
<data name="Filename.Text" xml:space="preserve">
|
||||
<value>Filename:</value>
|
||||
</data>
|
||||
<data name="Filename.HelpText" xml:space="preserve">
|
||||
<value>Specify a name for the file (without an extension)</value>
|
||||
</data>
|
||||
</root>
|
@ -189,4 +189,19 @@
|
||||
<data name="ModuleSettings.Title" xml:space="preserve">
|
||||
<value>Module Settings</value>
|
||||
</data>
|
||||
<data name="Header.Text" xml:space="preserve">
|
||||
<value>Header:</value>
|
||||
</data>
|
||||
<data name="Header.HelpText" xml:space="preserve">
|
||||
<value>Optionally provide content to be injected above the module instance</value>
|
||||
</data>
|
||||
<data name="Footer.Text" xml:space="preserve">
|
||||
<value>Footer:</value>
|
||||
</data>
|
||||
<data name="Footer.HelpText" xml:space="preserve">
|
||||
<value>Optionally provide content to be injected below the module instance</value>
|
||||
</data>
|
||||
<data name="ModuleContent.Heading" xml:space="preserve">
|
||||
<value>Content</value>
|
||||
</data>
|
||||
</root>
|
@ -303,4 +303,10 @@
|
||||
<data name="PersonalizedUrlPath.HelpText" xml:space="preserve">
|
||||
<value>Provide a url path for your personalized page. Please note that spaces and punctuation will be replaced by a dash.</value>
|
||||
</data>
|
||||
<data name="UpdateModulePermissions.Text" xml:space="preserve">
|
||||
<value>Update Module Permissions?</value>
|
||||
</data>
|
||||
<data name="UpdateModulePermissions.HelpText" xml:space="preserve">
|
||||
<value>Specify if changes made to page permissions should be propagated to the modules on this page</value>
|
||||
</data>
|
||||
</root>
|
@ -180,4 +180,10 @@
|
||||
<data name="Login" xml:space="preserve">
|
||||
<value>Already have account? Login now.</value>
|
||||
</data>
|
||||
<data name="TimeZone.Text" xml:space="preserve">
|
||||
<value>Time Zone:</value>
|
||||
</data>
|
||||
<data name="TimeZone.HelpText" xml:space="preserve">
|
||||
<value>Your time zone</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,31 @@
|
||||
<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>
|
||||
<data name="TimeZone.Text" xml:space="preserve">
|
||||
<value>Time Zone:</value>
|
||||
</data>
|
||||
<data name="TimeZone.HelpText" xml:space="preserve">
|
||||
<value>The default time zone for the site</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>
|
@ -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>Check For Updates</value>
|
||||
</data>
|
||||
<data name="Success.Theme.Synchronize" xml:space="preserve">
|
||||
<value>Theme Information Has Been Retrieved From The Marketplace</value>
|
||||
</data>
|
||||
<data name="Error.Theme.Synchronize" xml:space="preserve">
|
||||
<value>Error Retrieving Theme Information From The Marketplace</value>
|
||||
</data>
|
||||
</root>
|
@ -124,7 +124,7 @@
|
||||
<value>Error Downloading Framework Package</value>
|
||||
</data>
|
||||
<data name="Framework.HelpText" xml:space="preserve">
|
||||
<value>Upload a framework package and select Install to complete the installation</value>
|
||||
<value>Upload A Framework Package (Oqtane.Framework.#.#.#.nupkg) And Then Select Upgrade</value>
|
||||
</data>
|
||||
<data name="Framework.Text" xml:space="preserve">
|
||||
<value>Framework: </value>
|
||||
@ -144,13 +144,16 @@
|
||||
<data name="Message.Text" xml:space="preserve">
|
||||
<value>Framework Is Already Up To Date</value>
|
||||
</data>
|
||||
<data name="MessageUpgrade.Text" xml:space="preserve">
|
||||
<value>Upload A Framework Package (Oqtane.Framework.version.nupkg) And Then Select Upgrade</value>
|
||||
</data>
|
||||
<data name="Localhost.Text" xml:space="preserve">
|
||||
<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 reduce the time required for the upgrade.</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
|
||||
@ -121,10 +121,10 @@
|
||||
<value>Redirect To:</value>
|
||||
</data>
|
||||
<data name="MappedUrl.HelpText" xml:space="preserve">
|
||||
<value>A relative or absolute Url where the user will be redirected</value>
|
||||
<value>A Url where the user will be redirected (absolute or relative). Use '/' for site root path.</value>
|
||||
</data>
|
||||
<data name="Url.HelpText" xml:space="preserve">
|
||||
<value>An absolute Url identifying a path to a specific page in the site</value>
|
||||
<value>A Url identifying a path to a specific page in the site (absolute or relative)</value>
|
||||
</data>
|
||||
<data name="Url.Text" xml:space="preserve">
|
||||
<value>Url:</value>
|
||||
@ -141,4 +141,7 @@
|
||||
<data name="Message.SaveUrlMapping" xml:space="preserve">
|
||||
<value>The Url must belong to the current site</value>
|
||||
</data>
|
||||
<data name="Generate" xml:space="preserve">
|
||||
<value>Generate</value>
|
||||
</data>
|
||||
</root>
|
@ -121,10 +121,10 @@
|
||||
<value>Redirect To:</value>
|
||||
</data>
|
||||
<data name="MappedUrl.HelpText" xml:space="preserve">
|
||||
<value>A relative or absolute Url where the user will be redirected</value>
|
||||
<value>A Url where the user will be redirected (absolute or relative). Use '/' for site root path.</value>
|
||||
</data>
|
||||
<data name="Url.HelpText" xml:space="preserve">
|
||||
<value>A relative Url identifying a path to a specific page in the site</value>
|
||||
<value>A Url identifying a path to a specific page in the site (absolute or relative)</value>
|
||||
</data>
|
||||
<data name="Url.Text" xml:space="preserve">
|
||||
<value>Url:</value>
|
||||
|
@ -246,4 +246,10 @@
|
||||
<data name="Logout Everywhere" xml:space="preserve">
|
||||
<value>Logout Everywhere</value>
|
||||
</data>
|
||||
<data name="TimeZone.Text" xml:space="preserve">
|
||||
<value>Time Zone:</value>
|
||||
</data>
|
||||
<data name="TimeZone.HelpText" xml:space="preserve">
|
||||
<value>Your time zone</value>
|
||||
</data>
|
||||
</root>
|
@ -156,4 +156,10 @@
|
||||
<data name="Notify.Text" xml:space="preserve">
|
||||
<value>Notify?</value>
|
||||
</data>
|
||||
<data name="TimeZone.Text" xml:space="preserve">
|
||||
<value>Time Zone:</value>
|
||||
</data>
|
||||
<data name="TimeZone.HelpText" xml:space="preserve">
|
||||
<value>The user's time zone</value>
|
||||
</data>
|
||||
</root>
|
@ -210,4 +210,16 @@
|
||||
<data name="Error.User.Impersonate" xml:space="preserve">
|
||||
<value>Unable To Impersonate User</value>
|
||||
</data>
|
||||
<data name="TimeZone.Text" xml:space="preserve">
|
||||
<value>Time Zone:</value>
|
||||
</data>
|
||||
<data name="TimeZone.HelpText" xml:space="preserve">
|
||||
<value>The user's time zone</value>
|
||||
</data>
|
||||
<data name="Confirmed.Text" xml:space="preserve">
|
||||
<value>Confirmed?</value>
|
||||
</data>
|
||||
<data name="Confirmed.HelpText" xml:space="preserve">
|
||||
<value>Indicates if the user's email is verified</value>
|
||||
</data>
|
||||
</root>
|
@ -132,6 +132,18 @@
|
||||
<data name="AllowRegistration.Text" xml:space="preserve">
|
||||
<value>Allow Registration? </value>
|
||||
</data>
|
||||
<data name="RegisterUrl.HelpText" xml:space="preserve">
|
||||
<value>Optionally provide a custom registration url</value>
|
||||
</data>
|
||||
<data name="RegisterUrl.Text" xml:space="preserve">
|
||||
<value>Register Url: </value>
|
||||
</data>
|
||||
<data name="ProfileUrl.HelpText" xml:space="preserve">
|
||||
<value>Optionally provide a custom user profile url</value>
|
||||
</data>
|
||||
<data name="ProfileUrl.Text" xml:space="preserve">
|
||||
<value>Profile Url: </value>
|
||||
</data>
|
||||
<data name="Error.SaveSiteSettings" xml:space="preserve">
|
||||
<value>Error Saving Settings</value>
|
||||
</data>
|
||||
@ -208,7 +220,7 @@
|
||||
<value>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.</value>
|
||||
</data>
|
||||
<data name="AllowSiteLogin.Text" xml:space="preserve">
|
||||
<value>Allow Login?</value>
|
||||
<value>Allow Local Login?</value>
|
||||
</data>
|
||||
<data name="Authority.HelpText" xml:space="preserve">
|
||||
<value>The authority url or issuer url associated with the identity provider</value>
|
||||
@ -507,4 +519,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;
|
||||
}
|
||||
}
|
||||
}
|
@ -42,14 +42,6 @@ namespace Oqtane.Services
|
||||
return await PutJsonAsync<Folder>($"{ApiUrl}/{folder.FolderId}", folder);
|
||||
}
|
||||
|
||||
public async Task UpdateFolderOrderAsync(int siteId, int folderId, int? parentId)
|
||||
{
|
||||
var parent = parentId == null
|
||||
? string.Empty
|
||||
: parentId.ToString();
|
||||
await PutAsync($"{ApiUrl}/?siteid={siteId}&folderid={folderId}&parentid={parent}");
|
||||
}
|
||||
|
||||
public async Task DeleteFolderAsync(int folderId)
|
||||
{
|
||||
await DeleteAsync($"{ApiUrl}/{folderId}");
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
@ -39,15 +39,6 @@ namespace Oqtane.Services
|
||||
/// <returns></returns>
|
||||
Task<Folder> UpdateFolderAsync(Folder folder);
|
||||
|
||||
/// <summary>
|
||||
/// Update the internal Folder-Order within the list of Folders.
|
||||
/// </summary>
|
||||
/// <param name="siteId">Reference to the <see cref="Site"/></param>
|
||||
/// <param name="folderId">Reference to a <see cref="Folder"/> for the security check</param>
|
||||
/// <param name="parentId">Reference to the Parent <see cref="Folder"/> or null - this Folders children will be re-sorted.</param>
|
||||
/// <returns></returns>
|
||||
Task UpdateFolderOrderAsync(int siteId, int folderId, int? parentId);
|
||||
|
||||
/// <summary>
|
||||
/// Delete a <see cref="Folder"/>
|
||||
/// </summary>
|
||||
|
@ -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
|
||||
|
@ -56,7 +56,18 @@ namespace Oqtane.Services
|
||||
/// Exports a given module
|
||||
/// </summary>
|
||||
/// <param name="moduleId"></param>
|
||||
/// <returns>module in JSON</returns>
|
||||
/// <param name="pageId"></param>
|
||||
/// <returns>module content in JSON format</returns>
|
||||
Task<string> ExportModuleAsync(int moduleId, int pageId);
|
||||
|
||||
/// <summary>
|
||||
/// Exports a given module
|
||||
/// </summary>
|
||||
/// <param name="moduleId"></param>
|
||||
/// <param name="pageId"></param>
|
||||
/// <param name="folderId"></param>
|
||||
/// <param name="filename"></param>
|
||||
/// <returns>file id</returns>
|
||||
Task<int> ExportModuleAsync(int moduleId, int pageId, int folderId, string filename);
|
||||
}
|
||||
}
|
||||
|
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);
|
||||
}
|
||||
}
|
18
Oqtane.Client/Services/Interfaces/ITimeZoneService.cs
Normal file
18
Oqtane.Client/Services/Interfaces/ITimeZoneService.cs
Normal file
@ -0,0 +1,18 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using Oqtane.Models;
|
||||
|
||||
namespace Oqtane.Services
|
||||
{
|
||||
/// <summary>
|
||||
/// Service to store and retrieve <see cref="TimeZone"/> entries
|
||||
/// </summary>
|
||||
public interface ITimeZoneService
|
||||
{
|
||||
/// <summary>
|
||||
/// Get the list of time zones
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
Task<List<TimeZone>> GetTimeZonesAsync();
|
||||
}
|
||||
}
|
@ -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)
|
||||
|
@ -50,5 +50,10 @@ namespace Oqtane.Services
|
||||
{
|
||||
return await GetStringAsync($"{Apiurl}/export?moduleid={moduleId}&pageid={pageId}");
|
||||
}
|
||||
|
||||
public async Task<int> ExportModuleAsync(int moduleId, int pageId, int folderId, string filename)
|
||||
{
|
||||
return await PostJsonAsync<string,int>($"{Apiurl}/export?moduleid={moduleId}&pageid={pageId}&folderid={folderId}&filename={filename}", null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
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}");
|
||||
}
|
||||
}
|
||||
}
|
22
Oqtane.Client/Services/TimeZoneService.cs
Normal file
22
Oqtane.Client/Services/TimeZoneService.cs
Normal file
@ -0,0 +1,22 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
using Oqtane.Documentation;
|
||||
using Oqtane.Models;
|
||||
using Oqtane.Shared;
|
||||
|
||||
namespace Oqtane.Services
|
||||
{
|
||||
[PrivateApi("Don't show in the documentation, as everything should use the Interface")]
|
||||
public class TimeZoneService : ServiceBase, ITimeZoneService
|
||||
{
|
||||
public TimeZoneService(HttpClient http, SiteState siteState) : base(http, siteState) { }
|
||||
|
||||
private string Apiurl => CreateApiUrl("TimeZone");
|
||||
|
||||
public async Task<List<TimeZone>> GetTimeZonesAsync()
|
||||
{
|
||||
return await GetJsonAsync<List<TimeZone>>($"{Apiurl}");
|
||||
}
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
|
@ -1,20 +1,20 @@
|
||||
@namespace Oqtane.Themes.Controls
|
||||
@using System.Net
|
||||
@inherits ThemeControlBase
|
||||
@inject ISettingService SettingService
|
||||
@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,9 +23,18 @@
|
||||
|
||||
[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()
|
||||
@ -40,6 +49,42 @@
|
||||
// use existing value
|
||||
_returnurl = PageState.QueryString["returnurl"];
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(RegisterUrl))
|
||||
{
|
||||
_registerurl = RegisterUrl;
|
||||
_registerurl += (!_registerurl.Contains("?") ? "?" : "&") + "returnurl=" + (_registerurl.Contains("://") ? WebUtility.UrlEncode(PageState.Route.RootUrl) + _returnurl : _returnurl);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!string.IsNullOrEmpty(SettingService.GetSetting(PageState.Site.Settings, "LoginOptions:RegisterUrl", "")))
|
||||
{
|
||||
_registerurl = SettingService.GetSetting(PageState.Site.Settings, "LoginOptions:RegisterUrl", "");
|
||||
_registerurl += (!_registerurl.Contains("?") ? "?" : "&") + "returnurl=" + (_registerurl.Contains("://") ? WebUtility.UrlEncode(PageState.Route.RootUrl) + _returnurl : _returnurl);
|
||||
}
|
||||
else
|
||||
{
|
||||
_registerurl = NavigateUrl("register", "returnurl=" + _returnurl);
|
||||
}
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(ProfileUrl))
|
||||
{
|
||||
_profileurl = ProfileUrl;
|
||||
_profileurl += (!_profileurl.Contains("?") ? "?" : "&") + "returnurl=" + (_profileurl.Contains("://") ? WebUtility.UrlEncode(PageState.Route.RootUrl) + _returnurl : _returnurl);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!string.IsNullOrEmpty(SettingService.GetSetting(PageState.Site.Settings, "LoginOptions:ProfileUrl", "")))
|
||||
{
|
||||
_profileurl = SettingService.GetSetting(PageState.Site.Settings, "LoginOptions:ProfileUrl", "");
|
||||
_profileurl += (!_profileurl.Contains("?") ? "?" : "&") + "returnurl=" + (_profileurl.Contains("://") ? WebUtility.UrlEncode(PageState.Route.RootUrl) + _returnurl : _returnurl);
|
||||
}
|
||||
else
|
||||
{
|
||||
_profileurl = NavigateUrl("profile", "returnurl=" + _returnurl);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -107,6 +107,7 @@
|
||||
{
|
||||
<Pane Name="Footer" />
|
||||
}
|
||||
<CookieConsent />
|
||||
</div>
|
||||
</main>
|
||||
|
||||
|
@ -43,19 +43,22 @@ namespace Oqtane.Themes
|
||||
{
|
||||
List<Resource> resources = null;
|
||||
var type = GetType();
|
||||
if (type.BaseType == typeof(ThemeBase))
|
||||
if (type.IsSubclassOf(typeof(ThemeBase)))
|
||||
{
|
||||
if (type.IsSubclassOf(typeof(ThemeControlBase)) || type.IsSubclassOf(typeof(ContainerBase)))
|
||||
{
|
||||
if (Resources != null)
|
||||
{
|
||||
resources = Resources.Where(item => item.ResourceType == ResourceType.Script).ToList();
|
||||
}
|
||||
}
|
||||
else // ThemeBase
|
||||
{
|
||||
if (PageState.Page.Resources != null)
|
||||
{
|
||||
resources = PageState.Page.Resources.Where(item => item.ResourceType == ResourceType.Script && item.Level == ResourceLevel.Page && item.Namespace == type.Namespace).ToList();
|
||||
}
|
||||
}
|
||||
else // themecontrolbase, containerbase
|
||||
{
|
||||
if (Resources != null)
|
||||
{
|
||||
resources = Resources.Where(item => item.ResourceType == ResourceType.Script).ToList();
|
||||
}
|
||||
}
|
||||
if (resources != null && resources.Any())
|
||||
{
|
||||
|
@ -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
|
||||
|
@ -1,6 +1,10 @@
|
||||
@namespace Oqtane.UI
|
||||
@inject SiteState SiteState
|
||||
|
||||
@if (PageState.ModuleId == -1)
|
||||
{
|
||||
@((MarkupString)ModuleState.Header)
|
||||
}
|
||||
@if (_comment != null)
|
||||
{
|
||||
@((MarkupString)_comment)
|
||||
@ -13,6 +17,11 @@
|
||||
<RenderModeBoundary ModuleState="@ModuleState" PageState="@PageState" SiteState="@SiteState" @rendermode="InteractiveRenderMode.GetInteractiveRenderMode(PageState.Site.Runtime, _prerender)" />
|
||||
}
|
||||
}
|
||||
@if (PageState.ModuleId == -1)
|
||||
{
|
||||
@((MarkupString)ModuleState.Footer)
|
||||
}
|
||||
|
||||
|
||||
@code {
|
||||
[CascadingParameter]
|
||||
|
@ -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);
|
||||
|
||||
@ -333,7 +343,7 @@
|
||||
var urlMapping = await UrlMappingService.GetUrlMappingAsync(site.SiteId, route.PagePath);
|
||||
if (urlMapping != null && !string.IsNullOrEmpty(urlMapping.MappedUrl))
|
||||
{
|
||||
var url = (urlMapping.MappedUrl.StartsWith("http")) ? urlMapping.MappedUrl : route.SiteUrl + "/" + urlMapping.MappedUrl + route.Query;
|
||||
var url = (urlMapping.MappedUrl.StartsWith("http")) ? urlMapping.MappedUrl : route.SiteUrl + (!urlMapping.MappedUrl.StartsWith("/") ? "/" : "") + urlMapping.MappedUrl + ((!urlMapping.MappedUrl.Contains("?")) ? route.Query : "");
|
||||
NavigationManager.NavigateTo(url, false);
|
||||
return;
|
||||
}
|
||||
@ -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.3</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.3</PackageReleaseNotes>
|
||||
<RepositoryUrl>https://github.com/oqtane/oqtane.framework</RepositoryUrl>
|
||||
<RepositoryType>Git</RepositoryType>
|
||||
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
|
||||
@ -33,8 +33,8 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<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="MySql.Data" Version="9.3.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.3</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.3</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.5" />
|
||||
<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.3</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.3</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.5" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net9.0</TargetFramework>
|
||||
<Version>6.1.0</Version>
|
||||
<Version>6.1.3</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.3</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.5" />
|
||||
</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.3</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.3</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.3</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.5" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="9.0.5" />
|
||||
<PackageReference Include="Microsoft.Extensions.Http" Version="9.0.5" />
|
||||
<PackageReference Include="Microsoft.Extensions.Localization" Version="9.0.5" />
|
||||
<PackageReference Include="System.Net.Http.Json" Version="9.0.5" />
|
||||
<PackageReference Include="Microsoft.Maui.Controls" Version="9.0.61" />
|
||||
<PackageReference Include="Microsoft.Maui.Controls.Compatibility" Version="9.0.61" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Components.WebView.Maui" Version="9.0.61" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user