Compare commits
283 Commits
Author | SHA1 | Date | |
---|---|---|---|
611084d00c | |||
1b7c810eeb | |||
e99df58bce | |||
70139221ab | |||
5d8d2b4a34 | |||
f59f322226 | |||
acca3f37b5 | |||
0de62f620d | |||
351c4a00e9 | |||
b048d81ac0 | |||
b9c9a2b75f | |||
4cea22d813 | |||
edad4e7d35 | |||
e28acd8d2d | |||
86feaff82f | |||
5f747db224 | |||
ccda41e18e | |||
2bb83c6532 | |||
ec59faba8c | |||
c5d8c44b81 | |||
7a4e4629e5 | |||
4d3dd95a60 | |||
713021359e | |||
927d2c36e8 | |||
afc6368915 | |||
3f604ad7b5 | |||
c5d4e237ad | |||
0b50b21779 | |||
cafc70f1c9 | |||
b4377c346f | |||
7c206af757 | |||
6212d0a052 | |||
8909822aea | |||
5fc0964f73 | |||
7725559a44 | |||
03414a58e9 | |||
ade0419bf6 | |||
9d646b2eed | |||
0d718a5ca2 | |||
78c8b36dfa | |||
b5ca0874fa | |||
e209becff9 | |||
652d42aa6b | |||
058a11597f | |||
dd73d6e19a | |||
3793f618a8 | |||
6621983a9c | |||
e0b0302f5a | |||
b02584bec6 | |||
c832d61409 | |||
ac701f28b5 | |||
53032140e7 | |||
3c7633564f | |||
43eae40404 | |||
e34b1b54d5 | |||
0658a7de0d | |||
9e0a4dfac8 | |||
82034ade90 | |||
b979203e12 | |||
c9a368f13a | |||
cfabf22fef | |||
61aef77916 | |||
8b6c6beceb | |||
4688e49454 | |||
76cc28b215 | |||
467e5423d2 | |||
422bf7b689 | |||
1d230bd4aa | |||
029850842b | |||
3b090396a4 | |||
2e4656ae8b | |||
ebce63baa4 | |||
08f691ee0d | |||
7550a19951 | |||
daeb76df11 | |||
31315e5ba6 | |||
e4e1fa6a4c | |||
6e36312be8 | |||
9cc7ba1d82 | |||
9182001147 | |||
5d1510083e | |||
7035f4cc1f | |||
da826b2a22 | |||
a152d8664d | |||
6ad75d963a | |||
e92d34a9e3 | |||
768066db58 | |||
886b63e55b | |||
3c7ee65b7d | |||
0e060c4564 | |||
3e127dbd9c | |||
ad66d14a1b | |||
5db61b78ea | |||
85f9597f2c | |||
73aca22605 | |||
077343ca20 | |||
c76946c770 | |||
2344a49260 | |||
65f463dbbd | |||
6e100a70b9 | |||
77650ebe09 | |||
cdba2bd9e2 | |||
e84cc92fcd | |||
a2890948bb | |||
f2e1ebef45 | |||
3c33614115 | |||
fcc4b1e8ee | |||
c8ac4ec1e8 | |||
93ab8b88d4 | |||
cc12c302e3 | |||
73941ca30e | |||
f963711820 | |||
1a0a301fb6 | |||
f87a9b7aae | |||
f18f0bc762 | |||
c3b1bc6674 | |||
e9bb228528 | |||
a906957454 | |||
49d10337a1 | |||
87c6c31f1f | |||
5f3639e396 | |||
14d36ef8dc | |||
fc186f1718 | |||
70aa93fe8d | |||
901df22d39 | |||
cd0fe3d6d4 | |||
09f2c3f645 | |||
116542d8e4 | |||
ffae6e269b | |||
2194dc0463 | |||
da2d426137 | |||
8df7672624 | |||
b9dba61c97 | |||
1872a1a77c | |||
cf57bad7fa | |||
7e956894ee | |||
f78046d4c1 | |||
beb09cbf4a | |||
6b2a5d7777 | |||
4b0d368222 | |||
5ab2f6ea3a | |||
f845bf5b25 | |||
bb10d64d58 | |||
96e8e9736f | |||
62472b97e6 | |||
10b89ff00b | |||
2d8339343f | |||
9ddd88b5d0 | |||
614ec645c6 | |||
893ffc48dc | |||
7bddb70f11 | |||
9d4a38e560 | |||
9d20b31585 | |||
fb8838375f | |||
dead4e3f85 | |||
9c833a8a95 | |||
0b90794bca | |||
fd89757814 | |||
5eedc312c8 | |||
36c1cc5e0c | |||
0b4cdea9dd | |||
12172168f6 | |||
c79aa49252 | |||
b869308bf4 | |||
7a748bd142 | |||
d11669e613 | |||
b5108b938c | |||
049d510536 | |||
113f7b18a6 | |||
65b248c3cf | |||
1698996ac3 | |||
2ae47f2375 | |||
6733d12e81 | |||
8b5109e32f | |||
7b651d56b1 | |||
23b9b8aaf6 | |||
8d9fb43828 | |||
0d1be72fdb | |||
ce102a2af6 | |||
dda6071bbc | |||
0c4809bb16 | |||
4b87c5e80e | |||
b8fe9b9074 | |||
bc8b18cea8 | |||
20fdd3e891 | |||
ff9147487f | |||
af494f3b5e | |||
f68d26317d | |||
5b881f7c77 | |||
fe5efff255 | |||
46d18a642c | |||
c00012f28b | |||
d0050f7d59 | |||
f3a4881261 | |||
1728909964 | |||
f6e7460b24 | |||
351829888f | |||
e520c30ade | |||
99267ac2f0 | |||
6f1f7ef7fc | |||
d647e947e2 | |||
adef45c30f | |||
8b80859023 | |||
907fff89c8 | |||
ca532ffdaf | |||
64ee842dc4 | |||
9c0754a88d | |||
3dac9e3dae | |||
6963e667aa | |||
af292b291b | |||
918f75704e | |||
6e258e5d0c | |||
a8ea4ec085 | |||
2f894af028 | |||
dd400e1214 | |||
f8a183bfdf | |||
2404193b61 | |||
3ef11f5b26 | |||
30419cec12 | |||
162841feb8 | |||
f910f63f8d | |||
860cb75f8f | |||
293be93b93 | |||
976e77b31e | |||
9f28ee2982 | |||
e6c1e48b86 | |||
2c2880199d | |||
c7f8737eeb | |||
f3aea3108d | |||
e4f8ad0f05 | |||
8b80f72313 | |||
06ebce31ca | |||
59db8ba997 | |||
df37d4aa7f | |||
fd9935c800 | |||
c057430488 | |||
767fa78e22 | |||
a0e289dcd6 | |||
c62d147254 | |||
f739bee353 | |||
db4dfb69fb | |||
9729a5ef16 | |||
6f12818352 | |||
75fc318da0 | |||
acf71cc2c2 | |||
42efe10473 | |||
b77e72880b | |||
297c1524b4 | |||
6140743769 | |||
d5ca700828 | |||
a7e1fe76c3 | |||
f6c55279d1 | |||
826f4835bb | |||
119a28def1 | |||
c1ffb8bc33 | |||
60cc9d14af | |||
7cf4f8fdaa | |||
1b84e83061 | |||
b0d2ee8760 | |||
2022432d35 | |||
c0ed335d84 | |||
4964562866 | |||
f78dc443ad | |||
56b47db778 | |||
2d4bf17b28 | |||
3b1819c68d | |||
575bbdb53b | |||
2fa7482028 | |||
e5062317e2 | |||
543f4fa3c2 | |||
49cdf69815 | |||
04196a1a19 | |||
b8622b8708 | |||
a2feca0ba3 | |||
ae6c6a75b2 | |||
bd987e9531 | |||
24d69fd820 | |||
9e2fa8d7f1 | |||
74ba2f7283 | |||
7603b5c63f | |||
03566afe66 | |||
3c2e314e2d | |||
1fcf08b5cd |
2
.gitignore
vendored
2
.gitignore
vendored
@ -16,7 +16,7 @@ _ReSharper.Caches
|
||||
Oqtane.Server/appsettings.json
|
||||
Oqtane.Server/Data
|
||||
|
||||
/Oqtane.Server/Properties/PublishProfiles/FolderProfile.pubxml
|
||||
Oqtane.Server/Properties/PublishProfiles/FolderProfile.pubxml
|
||||
Oqtane.Server/Content
|
||||
Oqtane.Server/Packages
|
||||
Oqtane.Server/wwwroot/Content
|
||||
|
@ -2,7 +2,6 @@
|
||||
@inject IInstallationService InstallationService
|
||||
@inject IJSRuntime JSRuntime
|
||||
@inject SiteState SiteState
|
||||
@inject IServiceProvider ServiceProvider
|
||||
|
||||
@if (_initialized)
|
||||
{
|
||||
@ -50,29 +49,21 @@
|
||||
[Parameter]
|
||||
public string AuthorizationToken { get; set; }
|
||||
|
||||
[CascadingParameter]
|
||||
HttpContext HttpContext { get; set; }
|
||||
|
||||
private bool _initialized = false;
|
||||
private string _display = "display: none;";
|
||||
private Installation _installation = new Installation { Success = false, Message = "" };
|
||||
|
||||
private PageState PageState { get; set; }
|
||||
|
||||
private IHttpContextAccessor accessor;
|
||||
|
||||
protected override async Task OnParametersSetAsync()
|
||||
{
|
||||
SiteState.RemoteIPAddress = RemoteIPAddress;
|
||||
SiteState.AntiForgeryToken = AntiForgeryToken;
|
||||
SiteState.AuthorizationToken = AuthorizationToken;
|
||||
|
||||
accessor = (IHttpContextAccessor)ServiceProvider.GetService(typeof(IHttpContextAccessor));
|
||||
if (accessor != null)
|
||||
{
|
||||
SiteState.IsPrerendering = !accessor.HttpContext.Response.HasStarted;
|
||||
}
|
||||
else
|
||||
{
|
||||
SiteState.IsPrerendering = true;
|
||||
}
|
||||
SiteState.IsPrerendering = (HttpContext != null) ? true : false;
|
||||
|
||||
_installation = await InstallationService.IsInstalled();
|
||||
if (_installation.Alias != null)
|
||||
|
@ -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 7)</div>
|
||||
<div style="font-weight: bold">@SharedLocalizer["Version"] @Constants.Version (.NET 8)</div>
|
||||
</div>
|
||||
</div>
|
||||
<hr class="app-rule" />
|
||||
|
@ -12,11 +12,12 @@
|
||||
if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.View, p.PermissionList))
|
||||
{
|
||||
string url = NavigateUrl(p.Path);
|
||||
<div class="col-md-2 mx-auto text-center mb-3">
|
||||
<NavLink class="nav-link text-primary" href="@url" Match="NavLinkMatch.All">
|
||||
<h2><span class="@p.Icon" aria-hidden="true"></span></h2>@SharedLocalizer[p.Name]
|
||||
<p class="col-md-2 mx-auto text-center mb-3">
|
||||
<NavLink class="nav-link text-body" href="@url" Match="NavLinkMatch.All">
|
||||
<h2><span class="@p.Icon" aria-hidden="true"></span></h2>
|
||||
<p class="lead">@((MarkupString)SharedLocalizer[p.Name].ToString().Replace(" ", "<br />"))</p>
|
||||
</NavLink>
|
||||
</div>
|
||||
</p>
|
||||
}
|
||||
}
|
||||
</div>
|
||||
|
@ -4,6 +4,7 @@
|
||||
@inject NavigationManager NavigationManager
|
||||
@inject IFileService FileService
|
||||
@inject IFolderService FolderService
|
||||
@inject ISettingService SettingService
|
||||
@inject IStringLocalizer<Add> Localizer
|
||||
@inject IStringLocalizer<SharedResources> SharedLocalizer
|
||||
|
||||
@ -80,6 +81,7 @@
|
||||
{
|
||||
validated = true;
|
||||
var interop = new Interop(JSRuntime);
|
||||
|
||||
if (await interop.FormValid(form))
|
||||
{
|
||||
if (_url == string.Empty || _folderId == -1)
|
||||
@ -93,7 +95,7 @@
|
||||
_name = _url.Substring(_url.LastIndexOf("/", StringComparison.Ordinal) + 1);
|
||||
}
|
||||
|
||||
if (!Constants.UploadableFiles.Split(',').Contains(Path.GetExtension(_name).ToLower().Replace(".", "")))
|
||||
if (!PageState.Site.UploadableFiles.Split(',').Contains(Path.GetExtension(_name).ToLower().Replace(".", "")))
|
||||
{
|
||||
AddModuleMessage(Localizer["Message.Download.InvalidExtension"], MessageType.Warning);
|
||||
return;
|
||||
|
@ -67,6 +67,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
<br /><br />
|
||||
|
||||
@if (!_isSystem)
|
||||
{
|
||||
@ -79,8 +80,7 @@
|
||||
@((MarkupString)" ")
|
||||
<ActionDialog Header="Delete Folder" Message="Are You Sure You Wish To Delete This Folder?" Action="Delete" Security="SecurityAccessLevel.Admin" Class="btn btn-danger" OnClick="@(async () => await DeleteFolder())" ResourceKey="DeleteFolder" />
|
||||
}
|
||||
<br />
|
||||
<br />
|
||||
<br /><br />
|
||||
@if (PageState.QueryString.ContainsKey("id"))
|
||||
{
|
||||
<AuditInfo CreatedBy="@_createdBy" CreatedOn="@_createdOn" ModifiedBy="@_modifiedBy" ModifiedOn="@_modifiedOn"></AuditInfo>
|
||||
|
@ -8,27 +8,28 @@
|
||||
|
||||
@if (_files != null)
|
||||
{
|
||||
<div class="container">
|
||||
<div class="row mb-1 align-items-center">
|
||||
<div class="col-sm-2">
|
||||
<label class="control-label">@Localizer["Folder"] </label>
|
||||
</div>
|
||||
<div class="col-sm-6">
|
||||
<div class="row">
|
||||
<div class="col-md mb-1">
|
||||
<ActionLink Action="Edit" Text="Add Folder" Class="btn btn-secondary" ResourceKey="AddFolder" />
|
||||
</div>
|
||||
<div class="col-md-8 mb-1">
|
||||
<div class="input-group">
|
||||
<span class="input-group-text">@Localizer["Folder"]:</span>
|
||||
<select class="form-select" @onchange="(e => FolderChanged(e))">
|
||||
@foreach (Folder folder in _folders)
|
||||
{
|
||||
<option value="@(folder.FolderId)">@(new string('-', folder.Level * 2))@(folder.Name)</option>
|
||||
}
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-sm-4">
|
||||
<ActionLink Action="Edit" Text="Edit Folder" Class="btn btn-secondary" Parameters="@($"id=" + _folderId.ToString())" ResourceKey="EditFolder" />
|
||||
<ActionLink Action="Edit" Text="Add Folder" Class="btn btn-secondary" ResourceKey="AddFolder" />
|
||||
<ActionLink Action="Add" Text="Upload Files" Parameters="@($"id=" + _folderId.ToString())" ResourceKey="UploadFiles" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md mb-1 text-end">
|
||||
<ActionLink Action="Add" Text="Upload Files" Parameters="@($"id=" + _folderId.ToString())" ResourceKey="UploadFiles" />
|
||||
</div>
|
||||
</div>
|
||||
<Pager Items="@_files">
|
||||
|
||||
<Pager Items="@_files" SearchProperties="Name">
|
||||
<Header>
|
||||
<th style="width: 1px;"> </th>
|
||||
<th style="width: 1px;"> </th>
|
||||
|
@ -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" class="form-control" @bind="@_retentionHistory" maxlength="4" required />
|
||||
<input id="retention" type="number" min="0" step ="1" class="form-control" @bind="@_retentionHistory" maxlength="4" required />
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
|
@ -15,7 +15,7 @@ else
|
||||
<br />
|
||||
<br />
|
||||
|
||||
<Pager Items="@_jobs">
|
||||
<Pager Items="@_jobs" SearchProperties="Name">
|
||||
<Header>
|
||||
<th style="width: 1px;"> </th>
|
||||
<th style="width: 1px;"> </th>
|
||||
|
@ -131,8 +131,6 @@ else
|
||||
var interop = new Interop(JSRuntime);
|
||||
var localizationCookieValue = CookieRequestCultureProvider.MakeCookieValue(new RequestCulture(culture));
|
||||
await interop.SetCookie(CookieRequestCultureProvider.DefaultCookieName, localizationCookieValue, 360);
|
||||
|
||||
NavigationManager.NavigateTo(NavigationManager.Uri, true);
|
||||
}
|
||||
}
|
||||
|
||||
|
110
Oqtane.Client/Modules/Admin/Languages/Edit.razor
Normal file
110
Oqtane.Client/Modules/Admin/Languages/Edit.razor
Normal file
@ -0,0 +1,110 @@
|
||||
@namespace Oqtane.Modules.Admin.Languages
|
||||
@inherits ModuleBase
|
||||
@using System.Globalization
|
||||
@using Microsoft.AspNetCore.Localization
|
||||
@inject NavigationManager NavigationManager
|
||||
@inject ILocalizationService LocalizationService
|
||||
@inject ILanguageService LanguageService
|
||||
@inject IPackageService PackageService
|
||||
@inject IStringLocalizer<Edit> Localizer
|
||||
@inject IStringLocalizer<SharedResources> SharedLocalizer
|
||||
|
||||
@if (_code == null)
|
||||
{
|
||||
<p><em>@SharedLocalizer["Loading"]</em></p>
|
||||
}
|
||||
else
|
||||
{
|
||||
<TabStrip>
|
||||
<TabPanel Name="Manage" ResourceKey="Manage" Heading="Manage">
|
||||
<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="name" HelpText="Name Of The Language" ResourceKey="Name">Name:</Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="code" class="form-control" @bind="@_code" readonly/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="default" HelpText="Indicates Whether Or Not This Language Is The Default For The Site" ResourceKey="IsDefault">Default?</Label>
|
||||
<div class="col-sm-9">
|
||||
<select id="default" class="form-select" @bind="@_default" required>
|
||||
<option value="True">@SharedLocalizer["Yes"]</option>
|
||||
<option value="False">@SharedLocalizer["No"]</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<button type="button" class="btn btn-success" @onclick="SaveLanguage">@SharedLocalizer["Save"]</button>
|
||||
<NavLink class="btn btn-secondary" href="@NavigateUrl()">@SharedLocalizer["Cancel"]</NavLink>
|
||||
</form>
|
||||
</TabPanel>
|
||||
</TabStrip>
|
||||
}
|
||||
|
||||
@code {
|
||||
private ElementReference form;
|
||||
private bool validated = false;
|
||||
private int _languageId = -1;
|
||||
private string _code = string.Empty;
|
||||
private string _cultureName = string.Empty;
|
||||
private string _default = "False";
|
||||
private List<Language> _languages;
|
||||
|
||||
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Admin;
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
_languageId = Int32.Parse(PageState.QueryString["id"]);
|
||||
_languages = await LanguageService.GetLanguagesAsync(PageState.Site.SiteId, Constants.ClientId);
|
||||
Language language = _languages.Where(x => x.LanguageId == _languageId).FirstOrDefault();
|
||||
if (language != null)
|
||||
{
|
||||
_code = language.Code;
|
||||
_cultureName = language.Name;
|
||||
_default = language.IsDefault.ToString();
|
||||
if (language.SiteId == null)
|
||||
{
|
||||
language.SiteId = PageState.Site.SiteId;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private async Task SaveLanguage()
|
||||
{
|
||||
Language language = _languages.Where(x => x.LanguageId == _languageId).FirstOrDefault();
|
||||
if (language != null)
|
||||
{
|
||||
language.IsDefault = Boolean.Parse(_default);
|
||||
try
|
||||
{
|
||||
await LanguageService.EditLanguageAsync(language);
|
||||
|
||||
if (language.IsDefault)
|
||||
{
|
||||
await SetCultureAsync(language.Code);
|
||||
}
|
||||
|
||||
await logger.LogInformation("Language Edited {Language}", language);
|
||||
|
||||
NavigationManager.NavigateTo(NavigateUrl());
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
await logger.LogError(ex, "Error Editing Language {Language} {Error}", language, ex.Message);
|
||||
AddModuleMessage(Localizer["Error.Language.Edit"], MessageType.Error);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private async Task SetCultureAsync(string culture)
|
||||
{
|
||||
if (culture != CultureInfo.CurrentUICulture.Name)
|
||||
{
|
||||
var interop = new Interop(JSRuntime);
|
||||
var localizationCookieValue = CookieRequestCultureProvider.MakeCookieValue(new RequestCulture(culture));
|
||||
await interop.SetCookie(CookieRequestCultureProvider.DefaultCookieName, localizationCookieValue, 360);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -14,8 +14,9 @@ else
|
||||
{
|
||||
<ActionLink Action="Add" Text="Add Language" ResourceKey="AddLanguage" />
|
||||
|
||||
<Pager Items="@_languages">
|
||||
<Pager Items="@_languages" SearchProperties="Name,Code">
|
||||
<Header>
|
||||
<th style="width: 1px;"> </th>
|
||||
<th style="width: 1px;"> </th>
|
||||
<th>@SharedLocalizer["Name"]</th>
|
||||
<th>@Localizer["Code"]</th>
|
||||
@ -27,6 +28,12 @@ else
|
||||
}
|
||||
</Header>
|
||||
<Row>
|
||||
<td>
|
||||
@if (!context.IsDefault)
|
||||
{
|
||||
<ActionLink Action="Edit" Text="Edit" Parameters="@($"id=" + context.LanguageId.ToString())" ResourceKey="EditLanguage" />
|
||||
}
|
||||
</td>
|
||||
<td><ActionDialog Header="Delete Language" Message="@string.Format(Localizer["Confirm.Language.Delete"], context.Name)" Action="Delete" Security="SecurityAccessLevel.Admin" Class="btn btn-danger" OnClick="@(async () => await DeleteLanguage(context))" Disabled="@((context.IsDefault && _languages.Count > 2) || context.Code == Constants.DefaultCulture)" ResourceKey="DeleteLanguage" /></td>
|
||||
<td>@context.Name</td>
|
||||
<td>@context.Code</td>
|
||||
|
@ -36,10 +36,13 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group mt-2">
|
||||
<div class="form-check">
|
||||
<input id="remember" type="checkbox" class="form-check-input" @bind="@_remember" />
|
||||
<Label Class="control-label" For="remember" HelpText="Specify if you would like to be signed back in automatically the next time you visit this site" ResourceKey="Remember">Remember Me?</Label>
|
||||
</div>
|
||||
@if (!_alwaysremember)
|
||||
{
|
||||
<div class="form-check">
|
||||
<input id="remember" type="checkbox" class="form-check-input" @bind="@_remember" />
|
||||
<Label Class="control-label" For="remember" HelpText="Specify if you would like to be signed back in automatically the next time you visit this site" ResourceKey="Remember">Remember Me?</Label>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
<button type="button" class="btn btn-primary" @onclick="Login">@SharedLocalizer["Login"]</button>
|
||||
<button type="button" class="btn btn-secondary" @onclick="Cancel">@SharedLocalizer["Cancel"]</button>
|
||||
@ -78,6 +81,7 @@
|
||||
private string _passwordtype = "password";
|
||||
private string _togglepassword = string.Empty;
|
||||
private bool _remember = false;
|
||||
private bool _alwaysremember = false;
|
||||
private string _code = string.Empty;
|
||||
|
||||
private string _returnUrl = string.Empty;
|
||||
@ -93,23 +97,17 @@
|
||||
{
|
||||
try
|
||||
{
|
||||
_allowexternallogin = (SettingService.GetSetting(PageState.Site.Settings, "ExternalLogin:ProviderType", "") != "") ? true : false;
|
||||
_allowsitelogin = bool.Parse(SettingService.GetSetting(PageState.Site.Settings, "LoginOptions:AllowSiteLogin", "true"));
|
||||
_alwaysremember = bool.Parse(SettingService.GetSetting(PageState.Site.Settings, "LoginOptions:AlwaysRemember", "false"));
|
||||
|
||||
_togglepassword = SharedLocalizer["ShowPassword"];
|
||||
|
||||
if (PageState.QueryString.ContainsKey("returnurl"))
|
||||
{
|
||||
_returnUrl = PageState.QueryString["returnurl"];
|
||||
}
|
||||
|
||||
_allowexternallogin = (SettingService.GetSetting(PageState.Site.Settings, "ExternalLogin:ProviderType", "") != "") ? true : false;
|
||||
_allowsitelogin = bool.Parse(SettingService.GetSetting(PageState.Site.Settings, "LoginOptions:AllowSiteLogin", "true"));
|
||||
|
||||
if (_allowexternallogin && !_allowsitelogin)
|
||||
{
|
||||
// redirect to external login
|
||||
NavigationManager.NavigateTo(Utilities.TenantUrl(PageState.Alias, "/pages/external?returnurl=" + _returnUrl), true);
|
||||
return;
|
||||
}
|
||||
|
||||
_togglepassword = SharedLocalizer["ShowPassword"];
|
||||
|
||||
if (PageState.QueryString.ContainsKey("name"))
|
||||
{
|
||||
_username = PageState.QueryString["name"];
|
||||
@ -192,7 +190,8 @@
|
||||
var user = new User { SiteId = PageState.Site.SiteId, Username = _username, Password = _password, LastIPAddress = SiteState.RemoteIPAddress};
|
||||
|
||||
if (!twofactor)
|
||||
{
|
||||
{
|
||||
_remember = _alwaysremember || _remember;
|
||||
user = await UserService.LoginUserAsync(user, hybrid, _remember);
|
||||
}
|
||||
else
|
||||
|
@ -81,7 +81,7 @@ else
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="retention" HelpText="Number of days of events to retain" ResourceKey="Retention">Retention (Days): </Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="retention" class="form-control" @bind="@_retention" />
|
||||
<input id="retention" type="number" min="0" step="1" class="form-control" @bind="@_retention" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -97,7 +97,7 @@ else
|
||||
private string _rows = "10";
|
||||
private int _page = 1;
|
||||
private List<Log> _logs;
|
||||
private string _retention = "";
|
||||
private int _retention = 30;
|
||||
|
||||
public override string UrlParametersTemplate => "/{level}/{function}/{rows}/{page}";
|
||||
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Host;
|
||||
@ -126,7 +126,7 @@ else
|
||||
await GetLogs();
|
||||
|
||||
var settings = await SettingService.GetSiteSettingsAsync(PageState.Site.SiteId);
|
||||
_retention = SettingService.GetSetting(settings, "LogRetention", "30");
|
||||
_retention = int.Parse( SettingService.GetSetting(settings, "LogRetention", "30"));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@ -218,7 +218,7 @@ else
|
||||
try
|
||||
{
|
||||
var settings = await SettingService.GetSiteSettingsAsync(PageState.Site.SiteId);
|
||||
settings = SettingService.SetSetting(settings, "LogRetention", _retention, true);
|
||||
settings = SettingService.SetSetting(settings, "LogRetention", _retention.ToString(), true);
|
||||
await SettingService.UpdateSiteSettingsAsync(settings, PageState.Site.SiteId);
|
||||
|
||||
AddModuleMessage(Localizer["Success.SaveSiteSettings"], MessageType.Success);
|
||||
|
@ -66,6 +66,7 @@
|
||||
<div class="container-fluid px-0">
|
||||
<div class="row g-0 mb-2">
|
||||
<div class="col-4">
|
||||
<a href="@context.ProductUrl" target="_blank">
|
||||
@if (context.LogoUrl != null)
|
||||
{
|
||||
<img src="@context.LogoUrl" class="img-fluid" alt="@context.Name" />
|
||||
@ -74,6 +75,7 @@
|
||||
{
|
||||
<img src="/package.png" class="img-fluid" alt="@context.Name" />
|
||||
}
|
||||
</a>
|
||||
</div>
|
||||
<div class="col-8 text-end">
|
||||
<small>@SharedLocalizer["Search.Version"]:</small> <strong>@context.Version</strong>
|
||||
|
@ -156,7 +156,7 @@
|
||||
private bool IsValidXML(string description)
|
||||
{
|
||||
// must contain letters, digits, or spaces
|
||||
return Regex.IsMatch(description, "^[A-Za-z0-9 ]+$");
|
||||
return Regex.IsMatch(description, "^[A-Za-z0-9 .,!?]+$");
|
||||
}
|
||||
|
||||
private void TemplateChanged(ChangeEventArgs e)
|
||||
|
@ -8,6 +8,8 @@
|
||||
@inject NavigationManager NavigationManager
|
||||
@inject IStringLocalizer<Edit> Localizer
|
||||
@inject IStringLocalizer<SharedResources> SharedLocalizer
|
||||
@inject IPageModuleService PageModuleService
|
||||
@inject IModuleService ModuleService
|
||||
|
||||
@if (_initialized)
|
||||
{
|
||||
@ -131,10 +133,22 @@
|
||||
<button type="button" class="btn btn-success" @onclick="SaveModuleDefinition">@SharedLocalizer["Save"]</button>
|
||||
<NavLink class="btn btn-secondary" href="@NavigateUrl()">@SharedLocalizer["Cancel"]</NavLink>
|
||||
</TabPanel>
|
||||
<TabPanel Name="Pages" ResourceKey="Pages" Heading="Pages">
|
||||
<Pager Items="@_pagesWithModules" RowClass="align-middle">
|
||||
<Header>
|
||||
<th style="width: 1px;"> </th>
|
||||
<th>@SharedLocalizer["Name"]</th>
|
||||
</Header>
|
||||
<Row>
|
||||
<td><button type="button" class="btn btn-secondary" @onclick="@(async () => NavigationManager.NavigateTo(Browse(context)))">@Localizer["Browse"]</button></td>
|
||||
<td>@(string.IsNullOrEmpty(context.Title) ? @context.Name : @context.Title )</td>
|
||||
</Row>
|
||||
</Pager>
|
||||
</TabPanel>
|
||||
<TabPanel Name="Translations" ResourceKey="Translations" Heading="Translations">
|
||||
@if (_languages != null && _languages.Count > 0)
|
||||
{
|
||||
<Pager Items="@_languages">
|
||||
<Pager Items="@_languages">
|
||||
<Header>
|
||||
<th>@SharedLocalizer["Name"]</th>
|
||||
<th>@Localizer["Code"]</th>
|
||||
@ -240,6 +254,7 @@
|
||||
private DateTime _createdon;
|
||||
private string _modifiedby;
|
||||
private DateTime _modifiedon;
|
||||
private List<Page> _pagesWithModules;
|
||||
|
||||
#pragma warning disable 649
|
||||
private PermissionGrid _permissionGrid;
|
||||
@ -291,6 +306,18 @@
|
||||
_languages = _languages.OrderBy(item => item.Name).ToList();
|
||||
}
|
||||
|
||||
// Group modules by PageId
|
||||
// Get distinct PageIds where modules are present
|
||||
var distinctPageIds = PageState.Modules
|
||||
.Where(md => md.ModuleDefinition.ModuleDefinitionId == _moduleDefinitionId && md.IsDeleted == false)
|
||||
.Select(md => md.PageId)
|
||||
.Distinct();
|
||||
|
||||
// Filter and retrieve the corresponding pages
|
||||
_pagesWithModules = PageState.Pages
|
||||
.Where(pg => distinctPageIds.Contains(pg.PageId) && pg.IsDeleted == false)
|
||||
.ToList();
|
||||
|
||||
_initialized = true;
|
||||
}
|
||||
}
|
||||
@ -439,5 +466,5 @@
|
||||
AddModuleMessage(Localizer["Error.Validate"], MessageType.Error);
|
||||
}
|
||||
}
|
||||
|
||||
private string Browse(Page page) => string.IsNullOrEmpty(page.Url) ? NavigateUrl(page.Path) : page.Url;
|
||||
}
|
||||
|
@ -26,6 +26,17 @@
|
||||
<input id="title" type="text" class="form-control" @bind="@_title" required />
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="pane" HelpText="The pane where the module will be displayed" ResourceKey="Pane">Pane: </Label>
|
||||
<div class="col-sm-9">
|
||||
<select class="form-select" @bind="@_pane">
|
||||
@foreach (string pane in PageState.Page.Panes)
|
||||
{
|
||||
<option value="@pane">@pane Pane</option>
|
||||
}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="container" HelpText="Select the module's container" ResourceKey="Container">Container: </Label>
|
||||
<div class="col-sm-9">
|
||||
@ -112,6 +123,7 @@
|
||||
private List<ThemeControl> _containers = new List<ThemeControl>();
|
||||
private string _module;
|
||||
private string _title;
|
||||
private string _pane;
|
||||
private string _containerType;
|
||||
private string _allPages = "false";
|
||||
private string _permissionNames = "";
|
||||
@ -134,80 +146,82 @@
|
||||
{
|
||||
_module = ModuleState.ModuleDefinition.Name;
|
||||
_title = ModuleState.Title;
|
||||
_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;
|
||||
_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;
|
||||
|
||||
if (ModuleState.ModuleDefinition != null)
|
||||
{
|
||||
_permissionNames = ModuleState.ModuleDefinition?.PermissionNames;
|
||||
if (ModuleState.ModuleDefinition != null)
|
||||
{
|
||||
_permissionNames = ModuleState.ModuleDefinition?.PermissionNames;
|
||||
|
||||
if (!string.IsNullOrEmpty(ModuleState.ModuleDefinition.SettingsType))
|
||||
{
|
||||
// module settings type explicitly declared in IModule interface
|
||||
_moduleSettingsType = Type.GetType(ModuleState.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);
|
||||
}
|
||||
if (_moduleSettingsType != null)
|
||||
{
|
||||
var moduleobject = Activator.CreateInstance(_moduleSettingsType) as IModuleControl;
|
||||
if (!string.IsNullOrEmpty(moduleobject.Title))
|
||||
{
|
||||
_moduleSettingsTitle = moduleobject.Title;
|
||||
}
|
||||
if (!string.IsNullOrEmpty(ModuleState.ModuleDefinition.SettingsType))
|
||||
{
|
||||
// module settings type explicitly declared in IModule interface
|
||||
_moduleSettingsType = Type.GetType(ModuleState.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);
|
||||
}
|
||||
if (_moduleSettingsType != null)
|
||||
{
|
||||
var moduleobject = Activator.CreateInstance(_moduleSettingsType) as IModuleControl;
|
||||
if (!string.IsNullOrEmpty(moduleobject.Title))
|
||||
{
|
||||
_moduleSettingsTitle = moduleobject.Title;
|
||||
}
|
||||
|
||||
ModuleSettingsComponent = builder =>
|
||||
{
|
||||
builder.OpenComponent(0, _moduleSettingsType);
|
||||
builder.AddComponentReferenceCapture(1, inst => { _moduleSettings = Convert.ChangeType(inst, _moduleSettingsType); });
|
||||
builder.CloseComponent();
|
||||
};
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
AddModuleMessage(string.Format(Localizer["Error.Module.Load"], ModuleState.ModuleDefinitionName), MessageType.Error);
|
||||
}
|
||||
ModuleSettingsComponent = builder =>
|
||||
{
|
||||
builder.OpenComponent(0, _moduleSettingsType);
|
||||
builder.AddComponentReferenceCapture(1, inst => { _moduleSettings = Convert.ChangeType(inst, _moduleSettingsType); });
|
||||
builder.CloseComponent();
|
||||
};
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
AddModuleMessage(string.Format(Localizer["Error.Module.Load"], ModuleState.ModuleDefinitionName), MessageType.Error);
|
||||
}
|
||||
|
||||
var theme = PageState.Site.Themes.FirstOrDefault(item => item.Containers.Any(themecontrol => themecontrol.TypeName.Equals(_containerType)));
|
||||
if (theme != null && !string.IsNullOrEmpty(theme.ContainerSettingsType))
|
||||
{
|
||||
_containerSettingsType = Type.GetType(theme.ContainerSettingsType);
|
||||
if (_containerSettingsType != null)
|
||||
{
|
||||
ContainerSettingsComponent = builder =>
|
||||
{
|
||||
builder.OpenComponent(0, _containerSettingsType);
|
||||
builder.AddComponentReferenceCapture(1, inst => { _containerSettings = Convert.ChangeType(inst, _containerSettingsType); });
|
||||
builder.CloseComponent();
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
if (theme != null && !string.IsNullOrEmpty(theme.ContainerSettingsType))
|
||||
{
|
||||
_containerSettingsType = Type.GetType(theme.ContainerSettingsType);
|
||||
if (_containerSettingsType != null)
|
||||
{
|
||||
ContainerSettingsComponent = builder =>
|
||||
{
|
||||
builder.OpenComponent(0, _containerSettingsType);
|
||||
builder.AddComponentReferenceCapture(1, inst => { _containerSettings = Convert.ChangeType(inst, _containerSettingsType); });
|
||||
builder.CloseComponent();
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async Task SaveModule()
|
||||
{
|
||||
validated = true;
|
||||
var interop = new Interop(JSRuntime);
|
||||
if (await interop.FormValid(form))
|
||||
{
|
||||
if (!string.IsNullOrEmpty(_title))
|
||||
{
|
||||
var pagemodule = await PageModuleService.GetPageModuleAsync(ModuleState.PageModuleId);
|
||||
pagemodule.PageId = int.Parse(_pageId);
|
||||
pagemodule.Title = _title;
|
||||
pagemodule.ContainerType = (_containerType != "-") ? _containerType : string.Empty;
|
||||
if (!string.IsNullOrEmpty(pagemodule.ContainerType) && pagemodule.ContainerType == PageState.Page.DefaultContainerType)
|
||||
private async Task SaveModule()
|
||||
{
|
||||
validated = true;
|
||||
var interop = new Interop(JSRuntime);
|
||||
if (await interop.FormValid(form))
|
||||
{
|
||||
if (!string.IsNullOrEmpty(_title))
|
||||
{
|
||||
var pagemodule = await PageModuleService.GetPageModuleAsync(ModuleState.PageModuleId);
|
||||
pagemodule.PageId = int.Parse(_pageId);
|
||||
pagemodule.Title = _title;
|
||||
pagemodule.Pane = _pane;
|
||||
pagemodule.ContainerType = (_containerType != "-") ? _containerType : string.Empty;
|
||||
if (!string.IsNullOrEmpty(pagemodule.ContainerType) && pagemodule.ContainerType == PageState.Page.DefaultContainerType)
|
||||
{
|
||||
pagemodule.ContainerType = string.Empty;
|
||||
}
|
||||
|
@ -306,13 +306,15 @@
|
||||
private void ThemeChanged(ChangeEventArgs e)
|
||||
{
|
||||
_themetype = (string)e.Value;
|
||||
_containers = ThemeService.GetContainerControls(PageState.Site.Themes, _themetype);
|
||||
_containertype = _containers.First().TypeName;
|
||||
ThemeSettings();
|
||||
StateHasChanged();
|
||||
|
||||
// if theme chosen is different than default site theme, display warning message to user
|
||||
if (ThemeService.GetTheme(PageState.Site.Themes, _themetype)?.ThemeName != ThemeService.GetTheme(PageState.Site.Themes, PageState.Site.DefaultThemeType)?.ThemeName)
|
||||
{
|
||||
AddModuleMessage(Localizer["ThemeChanged.Message"], MessageType.Warning);
|
||||
_containers = ThemeService.GetContainerControls(PageState.Site.Themes, _themetype);
|
||||
_containertype = _containers.First().TypeName;
|
||||
ThemeSettings();
|
||||
StateHasChanged();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -447,13 +447,15 @@
|
||||
private void ThemeChanged(ChangeEventArgs e)
|
||||
{
|
||||
_themetype = (string)e.Value;
|
||||
_containers = ThemeService.GetContainerControls(PageState.Site.Themes, _themetype);
|
||||
_containertype = _containers.First().TypeName;
|
||||
ThemeSettings();
|
||||
StateHasChanged();
|
||||
|
||||
// if theme chosen is different than default site theme, display warning message to user
|
||||
if (ThemeService.GetTheme(PageState.Site.Themes, _themetype)?.ThemeName != ThemeService.GetTheme(PageState.Site.Themes, PageState.Site.DefaultThemeType)?.ThemeName)
|
||||
{
|
||||
AddModuleMessage(Localizer["ThemeChanged.Message"], MessageType.Warning);
|
||||
_containers = ThemeService.GetContainerControls(PageState.Site.Themes, _themetype);
|
||||
_containertype = _containers.First().TypeName;
|
||||
ThemeSettings();
|
||||
StateHasChanged();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -9,7 +9,7 @@
|
||||
{
|
||||
<ActionLink Action="Add" Text="Add Page" ResourceKey="AddPage" />
|
||||
|
||||
<Pager Items="@PageState.Pages.Where(item => !item.IsDeleted)">
|
||||
<Pager Items="@PageState.Pages.Where(item => !item.IsDeleted)" SearchProperties="Name">
|
||||
<Header>
|
||||
<th style="width: 1px;"> </th>
|
||||
<th style="width: 1px;"> </th>
|
||||
|
@ -22,7 +22,7 @@
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="description" HelpText="The help text displayed to the user for this profile item" ResourceKey="Description">Description: </Label>
|
||||
<div class="col-sm-9">
|
||||
<textarea id="description" class="form-control" @bind="@_description" rows="5" maxlength="256" required ></textarea>
|
||||
<textarea id="description" class="form-control" @bind="@_description" rows="3" maxlength="256" required></textarea>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
@ -34,19 +34,25 @@
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="order" HelpText="The index order of where this profile item should be displayed" ResourceKey="Order">Order: </Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="order" class="form-control" @bind="@_vieworder" maxlength="4" required />
|
||||
<input id="order" class="form-control" @bind="@_vieworder" min="0" max="99" type="number" required />
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="length" HelpText="The max number of characters this profile item should accept (enter zero for unlimited)" ResourceKey="Length">Length: </Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="length" class="form-control" @bind="@_maxlength" maxlength="4" required />
|
||||
<input id="length" class="form-control" @bind="@_maxlength" min="0" max="524288" type="number" required />
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="rows" HelpText="The number of rows for text entry (one is the default)" ResourceKey="Rows">Rows: </Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="rows" class="form-control" @bind="@_rows" min="1" max="10" type="number" required />
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="defaultVal" HelpText="The default value for this profile item" ResourceKey="DefaultValue">Default Value: </Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="defaultVal" class="form-control" @bind="@_defaultvalue" maxlength="2000"/>
|
||||
<input id="defaultVal" class="form-control" @bind="@_defaultvalue" maxlength="2000" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
@ -101,6 +107,7 @@
|
||||
private string _category = string.Empty;
|
||||
private string _vieworder = "0";
|
||||
private string _maxlength = "0";
|
||||
private string _rows = "1";
|
||||
private string _defaultvalue = string.Empty;
|
||||
private string _options = string.Empty;
|
||||
private string _validation = string.Empty;
|
||||
@ -131,6 +138,7 @@
|
||||
_category = profile.Category;
|
||||
_vieworder = profile.ViewOrder.ToString();
|
||||
_maxlength = profile.MaxLength.ToString();
|
||||
_rows = profile.Rows.ToString();
|
||||
_defaultvalue = profile.DefaultValue;
|
||||
_options = profile.Options;
|
||||
_validation = profile.Validation;
|
||||
@ -175,6 +183,7 @@
|
||||
profile.Category = _category;
|
||||
profile.ViewOrder = int.Parse(_vieworder);
|
||||
profile.MaxLength = int.Parse(_maxlength);
|
||||
profile.Rows = int.Parse(_rows);
|
||||
profile.DefaultValue = _defaultvalue;
|
||||
profile.Options = _options;
|
||||
profile.Validation = _validation;
|
||||
|
@ -12,7 +12,7 @@ else
|
||||
{
|
||||
<ActionLink Action="Add" Text="Add Profile" Security="SecurityAccessLevel.Edit" ResourceKey="AddProfile" />
|
||||
|
||||
<Pager Items="@_profiles">
|
||||
<Pager Items="@_profiles" SearchProperties="Title,Category">
|
||||
<Header>
|
||||
<th style="width: 1px;"> </th>
|
||||
<th style="width: 1px;"> </th>
|
||||
|
@ -1,4 +1,5 @@
|
||||
@namespace Oqtane.Modules.Admin.Register
|
||||
@using System.Net
|
||||
@inherits ModuleBase
|
||||
@inject NavigationManager NavigationManager
|
||||
@inject IUserService UserService
|
||||
@ -88,9 +89,9 @@ else
|
||||
}
|
||||
|
||||
protected override void OnParametersSet()
|
||||
{
|
||||
_togglepassword = SharedLocalizer["ShowPassword"];
|
||||
}
|
||||
{
|
||||
_togglepassword = SharedLocalizer["ShowPassword"];
|
||||
}
|
||||
|
||||
private async Task Register()
|
||||
{
|
||||
@ -120,7 +121,14 @@ else
|
||||
if (user != null)
|
||||
{
|
||||
await logger.LogInformation("User Created {Username} {Email}", _username, _email);
|
||||
AddModuleMessage(Localizer["Info.User.AccountCreate"], MessageType.Info);
|
||||
if (PageState.QueryString.ContainsKey("returnurl"))
|
||||
{
|
||||
NavigationManager.NavigateTo(WebUtility.UrlDecode(PageState.QueryString["returnurl"]));
|
||||
}
|
||||
else // legacy behavior
|
||||
{
|
||||
AddModuleMessage(Localizer["Info.User.AccountCreate"], MessageType.Info);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -12,7 +12,7 @@ else
|
||||
{
|
||||
<ActionLink Action="Add" Text="Add Role" Security="SecurityAccessLevel.Edit" ResourceKey="AddRole" />
|
||||
|
||||
<Pager Items="@_roles">
|
||||
<Pager Items="@_roles" SearchProperties="Name">
|
||||
<Header>
|
||||
<th style="width: 1px;"> </th>
|
||||
<th style="width: 1px;"> </th>
|
||||
|
@ -74,7 +74,7 @@
|
||||
<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="@Constants.ImageFiles" @ref="_logofilemanager" />
|
||||
<FileManager FileId="@_logofileid" Filter="@_ImageFiles" @ref="_logofilemanager" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
@ -119,6 +119,22 @@
|
||||
</div>
|
||||
</div>
|
||||
</Section>
|
||||
<Section Name="FileExtensions" Heading="File Extensions" ResourceKey="FileExtensions">
|
||||
<div class="container">
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="imageExt" HelpText="Enter a comma separated list of image file extensions" ResourceKey="ImageExtensions">Image Extensions: </Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="imageExt" spellcheck="false" class="form-control" @bind="@_ImageFiles" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="uploadableFileExt" HelpText="Enter a comma separated list of uploadable file extensions" ResourceKey="UploadableFileExtensions">Uploadable File Extensions: </Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="uploadableFileExt" spellcheck="false" class="form-control" @bind="@_UploadableFiles" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Section>
|
||||
<Section Name="PageContent" Heading="Page Content" ResourceKey="PageContent">
|
||||
<div class="container">
|
||||
<div class="row mb-1 align-items-center">
|
||||
@ -207,7 +223,7 @@
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="retention" HelpText="Number of days of notifications to retain" ResourceKey="Retention">Retention (Days): </Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="retention" class="form-control" @bind="@_retention" />
|
||||
<input id="retention" class="form-control" type="number" min="0" step="1" @bind="@_retention" />
|
||||
</div>
|
||||
</div>
|
||||
<button type="button" class="btn btn-secondary" @onclick="SendEmail">@Localizer["Smtp.TestConfig"]</button>
|
||||
@ -295,12 +311,11 @@
|
||||
<Section Name="Hosting" Heading="Hosting Model" ResourceKey="Hosting">
|
||||
<div class="container">
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="runtime" HelpText="The Blazor runtime hosting model" ResourceKey="Runtime">Runtime: </Label>
|
||||
<Label Class="col-sm-3" For="runtime" HelpText="The Blazor runtime hosting model for the site" ResourceKey="Runtime">Runtime: </Label>
|
||||
<div class="col-sm-9">
|
||||
<select id="runtime" class="form-select" @bind="@_runtime" required>
|
||||
<option value="Server">@SharedLocalizer["BlazorServer"]</option>
|
||||
<option value="WebAssembly">@SharedLocalizer["BlazorWebAssembly"]</option>
|
||||
<option value="Hybrid">@SharedLocalizer["BlazorHybrid"]</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
@ -313,6 +328,15 @@
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="hybridenabled" HelpText="Specifies if the site can be integrated with an external .NET MAUI hybrid application" ResourceKey="HybridEnabled">Hybrid Enabled? </Label>
|
||||
<div class="col-sm-9">
|
||||
<select id="hybridenabled" class="form-select" @bind="@_hybridenabled" required>
|
||||
<option value="True">@SharedLocalizer["Yes"]</option>
|
||||
<option value="False">@SharedLocalizer["No"]</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Section>
|
||||
<Section Name="TenantInformation" Heading="Database" ResourceKey="TenantInformation">
|
||||
@ -378,7 +402,9 @@
|
||||
private string _smtpsender = string.Empty;
|
||||
private string _smtprelay = "False";
|
||||
private string _smtpenabled = "True";
|
||||
private string _retention = string.Empty;
|
||||
private string _ImageFiles = string.Empty;
|
||||
private string _UploadableFiles = string.Empty;
|
||||
private int _retention = 30;
|
||||
private string _pwaisenabled;
|
||||
private int _pwaappiconfileid = -1;
|
||||
private FileManager _pwaappiconfilemanager;
|
||||
@ -390,6 +416,7 @@
|
||||
private string _defaultalias;
|
||||
private string _runtime = "";
|
||||
private string _prerender = "";
|
||||
private string _hybridenabled = "";
|
||||
private string _tenant = string.Empty;
|
||||
private string _database = string.Empty;
|
||||
private string _connectionstring = string.Empty;
|
||||
@ -415,7 +442,7 @@
|
||||
_homepageid = site.HomePageId.Value.ToString();
|
||||
}
|
||||
_isdeleted = site.IsDeleted.ToString();
|
||||
_sitemap = PageState.Alias.Protocol + PageState.Alias.Name + "/pages/sitemap.xml";
|
||||
_sitemap = PageState.Alias.Protocol + PageState.Alias.Name + "/sitemap.xml";
|
||||
_siteguid = site.SiteGuid;
|
||||
_version = site.Version;
|
||||
|
||||
@ -461,7 +488,13 @@
|
||||
_smtpsender = SettingService.GetSetting(settings, "SMTPSender", string.Empty);
|
||||
_smtprelay = SettingService.GetSetting(settings, "SMTPRelay", "False");
|
||||
_smtpenabled = SettingService.GetSetting(settings, "SMTPEnabled", "True");
|
||||
_retention = SettingService.GetSetting(settings, "NotificationRetention", "30");
|
||||
_retention = int.Parse(SettingService.GetSetting(settings, "NotificationRetention", "30"));
|
||||
|
||||
// file extensions
|
||||
_ImageFiles = SettingService.GetSetting(settings, "ImageFiles", Constants.ImageFiles);
|
||||
_ImageFiles = (string.IsNullOrEmpty(_ImageFiles)) ? Constants.ImageFiles : _ImageFiles;
|
||||
_UploadableFiles = SettingService.GetSetting(settings, "UploadableFiles", Constants.UploadableFiles);
|
||||
_UploadableFiles = (string.IsNullOrEmpty(_UploadableFiles)) ? Constants.UploadableFiles : _UploadableFiles;
|
||||
|
||||
// aliases
|
||||
await GetAliases();
|
||||
@ -469,6 +502,7 @@
|
||||
// hosting model
|
||||
_runtime = site.Runtime;
|
||||
_prerender = site.RenderMode.Replace(_runtime, "");
|
||||
_hybridenabled = site.HybridEnabled.ToString();
|
||||
|
||||
// database
|
||||
if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host))
|
||||
@ -578,34 +612,35 @@
|
||||
}
|
||||
|
||||
// PWA
|
||||
if (site.PwaIsEnabled.ToString() != _pwaisenabled)
|
||||
{
|
||||
site.PwaIsEnabled = Boolean.Parse(_pwaisenabled);
|
||||
reload = true; // needs to be reloaded on server
|
||||
}
|
||||
int? pwaappiconfileid = _pwaappiconfilemanager.GetFileId();
|
||||
if (pwaappiconfileid == -1) pwaappiconfileid = null;
|
||||
if (site.PwaAppIconFileId != pwaappiconfileid)
|
||||
{
|
||||
site.PwaAppIconFileId = pwaappiconfileid;
|
||||
reload = true; // needs to be reloaded on server
|
||||
}
|
||||
int? pwasplashiconfileid = _pwasplashiconfilemanager.GetFileId();
|
||||
if (pwasplashiconfileid == -1) pwasplashiconfileid = null;
|
||||
if (site.PwaSplashIconFileId != pwasplashiconfileid)
|
||||
{
|
||||
site.PwaSplashIconFileId = pwasplashiconfileid;
|
||||
reload = true; // needs to be reloaded on server
|
||||
}
|
||||
if (site.PwaIsEnabled.ToString() != _pwaisenabled)
|
||||
{
|
||||
site.PwaIsEnabled = Boolean.Parse(_pwaisenabled);
|
||||
reload = true; // needs to be reloaded on server
|
||||
}
|
||||
int? pwaappiconfileid = _pwaappiconfilemanager.GetFileId();
|
||||
if (pwaappiconfileid == -1) pwaappiconfileid = null;
|
||||
if (site.PwaAppIconFileId != pwaappiconfileid)
|
||||
{
|
||||
site.PwaAppIconFileId = pwaappiconfileid;
|
||||
reload = true; // needs to be reloaded on server
|
||||
}
|
||||
int? pwasplashiconfileid = _pwasplashiconfilemanager.GetFileId();
|
||||
if (pwasplashiconfileid == -1) pwasplashiconfileid = null;
|
||||
if (site.PwaSplashIconFileId != pwasplashiconfileid)
|
||||
{
|
||||
site.PwaSplashIconFileId = pwasplashiconfileid;
|
||||
reload = true; // needs to be reloaded on server
|
||||
}
|
||||
|
||||
// hosting model
|
||||
if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host))
|
||||
{
|
||||
if (site.Runtime != _runtime || site.RenderMode != _runtime + _prerender)
|
||||
if (site.Runtime != _runtime || site.RenderMode != _runtime + _prerender || site.HybridEnabled != bool.Parse(_hybridenabled))
|
||||
{
|
||||
site.Runtime = _runtime;
|
||||
site.RenderMode = _runtime + _prerender;
|
||||
reload = true; // needs to be reloaded on server
|
||||
site.HybridEnabled = bool.Parse(_hybridenabled);
|
||||
reload = true; // needs to be reloaded on serve
|
||||
}
|
||||
}
|
||||
|
||||
@ -622,7 +657,12 @@
|
||||
settings = SettingService.SetSetting(settings, "SMTPRelay", _smtprelay, true);
|
||||
settings = SettingService.SetSetting(settings, "SMTPEnabled", _smtpenabled, true);
|
||||
settings = SettingService.SetSetting(settings, "SiteGuid", _siteguid, true);
|
||||
settings = SettingService.SetSetting(settings, "NotificationRetention", _retention, true);
|
||||
settings = SettingService.SetSetting(settings, "NotificationRetention", _retention.ToString(), true);
|
||||
|
||||
// file extensions
|
||||
settings = SettingService.SetSetting(settings, "ImageFiles", (_ImageFiles != Constants.ImageFiles) ? _ImageFiles.Replace(" ", "") : "", false);
|
||||
settings = SettingService.SetSetting(settings, "UploadableFiles", (_UploadableFiles != Constants.UploadableFiles) ? _UploadableFiles.Replace(" ", "") : "", false);
|
||||
|
||||
await SettingService.UpdateSiteSettingsAsync(settings, site.SiteId);
|
||||
|
||||
await logger.LogInformation("Site Settings Saved {Site}", site);
|
||||
|
@ -37,7 +37,7 @@ else
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="defaultTheme" HelpText="Select the default theme for the website" ResourceKey="DefaultTheme">Default Theme: </Label>
|
||||
<div class="col-sm-9">
|
||||
<select id="defaultTheme" class="form-select" @onchange="(e => ThemeChanged(e))" required>
|
||||
<select id="defaultTheme" class="form-select" value="@_themetype" @onchange="(e => ThemeChanged(e))" required>
|
||||
<option value="-"><@Localizer["Theme.Select"]></option>
|
||||
@foreach (var theme in _themes)
|
||||
{
|
||||
@ -58,19 +58,6 @@ else
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="adminContainer" HelpText="Select the admin container for the site" ResourceKey="AdminContainer">Admin Container: </Label>
|
||||
<div class="col-sm-9">
|
||||
<select id="adminContainer" class="form-select" @bind="@_admincontainertype" required>
|
||||
<option value="-"><@Localizer["Container.Select"]></option>
|
||||
<option value=""><@Localizer["DefaultContainer.Admin"]></option>
|
||||
@foreach (var container in _containers)
|
||||
{
|
||||
<option value="@container.TypeName">@container.Name</option>
|
||||
}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="siteTemplate" HelpText="Select the site template" ResourceKey="SiteTemplate">Site Template: </Label>
|
||||
<div class="col-sm-9">
|
||||
@ -89,7 +76,6 @@ else
|
||||
<select id="runtime" class="form-select" @bind="@_runtime" required>
|
||||
<option value="Server">@SharedLocalizer["BlazorServer"]</option>
|
||||
<option value="WebAssembly">@SharedLocalizer["BlazorWebAssembly"]</option>
|
||||
<option value="Hybrid">@SharedLocalizer["BlazorHybrid"]</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
@ -105,7 +91,7 @@ else
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="tenant" HelpText="Select the database for the site" ResourceKey="Tenant">Database: </Label>
|
||||
<div class="col-sm-9">
|
||||
<select id="tenant" class="form-select" @onchange="(e => TenantChanged(e))" required>
|
||||
<select id="tenant" class="form-select" value="@_tenantid" @onchange="(e => TenantChanged(e))" required>
|
||||
<option value="-"><@Localizer["Tenant.Select"]></option>
|
||||
<option value="+"><@Localizer["Tenant.Add"]></option>
|
||||
@foreach (Tenant tenant in _tenants)
|
||||
@ -188,46 +174,59 @@ else
|
||||
}
|
||||
|
||||
@code {
|
||||
private List<Database> _databases;
|
||||
private ElementReference form;
|
||||
private bool validated = false;
|
||||
private string _databaseName;
|
||||
private Type _databaseConfigType;
|
||||
private object _databaseConfig;
|
||||
private RenderFragment DatabaseConfigComponent { get; set; }
|
||||
private bool _showConnectionString = false;
|
||||
private string _connectionString = string.Empty;
|
||||
private List<Database> _databases;
|
||||
private ElementReference form;
|
||||
private bool validated = false;
|
||||
private string _databaseName;
|
||||
private Type _databaseConfigType;
|
||||
private object _databaseConfig;
|
||||
private RenderFragment DatabaseConfigComponent { get; set; }
|
||||
private bool _showConnectionString = false;
|
||||
private string _connectionString = string.Empty;
|
||||
|
||||
private List<Theme> _themeList;
|
||||
private List<ThemeControl> _themes = new List<ThemeControl>();
|
||||
private List<ThemeControl> _containers = new List<ThemeControl>();
|
||||
private List<SiteTemplate> _siteTemplates;
|
||||
private List<Tenant> _tenants;
|
||||
private string _tenantid = "-";
|
||||
private List<Theme> _themeList;
|
||||
private List<ThemeControl> _themes = new List<ThemeControl>();
|
||||
private List<ThemeControl> _containers = new List<ThemeControl>();
|
||||
private List<SiteTemplate> _siteTemplates;
|
||||
private List<Tenant> _tenants;
|
||||
private string _tenantid = "-";
|
||||
|
||||
private string _tenantName = string.Empty;
|
||||
private string _tenantName = string.Empty;
|
||||
|
||||
private string _hostusername = string.Empty;
|
||||
private string _hostpassword = string.Empty;
|
||||
private string _hostusername = string.Empty;
|
||||
private string _hostpassword = string.Empty;
|
||||
|
||||
private string _name = string.Empty;
|
||||
private string _urls = string.Empty;
|
||||
private string _themetype = "-";
|
||||
private string _containertype = "-";
|
||||
private string _admincontainertype = "";
|
||||
private string _sitetemplatetype = "-";
|
||||
private string _runtime = "Server";
|
||||
private string _prerender = "Prerendered";
|
||||
private string _name = string.Empty;
|
||||
private string _urls = string.Empty;
|
||||
private string _themetype = "-";
|
||||
private string _containertype = "-";
|
||||
private string _sitetemplatetype = "-";
|
||||
private string _runtime = "Server";
|
||||
private string _prerender = "Prerendered";
|
||||
|
||||
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Host;
|
||||
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Host;
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
_tenants = await TenantService.GetTenantsAsync();
|
||||
_urls = PageState.Alias.Name;
|
||||
_themeList = await ThemeService.GetThemesAsync();
|
||||
_themes = ThemeService.GetThemeControls(_themeList);
|
||||
_siteTemplates = await SiteTemplateService.GetSiteTemplatesAsync();
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
_tenants = await TenantService.GetTenantsAsync();
|
||||
if (_tenants.Any(item => item.Name == TenantNames.Master))
|
||||
{
|
||||
_tenantid = _tenants.First(item => item.Name == TenantNames.Master).TenantId.ToString();
|
||||
}
|
||||
_urls = PageState.Alias.Name;
|
||||
_themeList = await ThemeService.GetThemesAsync();
|
||||
_themes = ThemeService.GetThemeControls(_themeList);
|
||||
if (_themes.Any(item => item.TypeName == Constants.DefaultTheme))
|
||||
{
|
||||
_themetype = Constants.DefaultTheme;
|
||||
_containers = ThemeService.GetContainerControls(_themeList, _themetype);
|
||||
_containertype = _containers.First().TypeName;
|
||||
}
|
||||
_siteTemplates = await SiteTemplateService.GetSiteTemplatesAsync();
|
||||
if (_siteTemplates.Any(item => item.TypeName == Constants.DefaultSiteTemplate))
|
||||
{
|
||||
_sitetemplatetype = Constants.DefaultSiteTemplate;
|
||||
}
|
||||
|
||||
_databases = await DatabaseService.GetDatabasesAsync();
|
||||
if (_databases.Exists(item => item.IsDefault))
|
||||
@ -295,7 +294,6 @@ else
|
||||
_containers = new List<ThemeControl>();
|
||||
_containertype = "-";
|
||||
}
|
||||
_admincontainertype = "";
|
||||
StateHasChanged();
|
||||
}
|
||||
catch (Exception ex)
|
||||
@ -399,7 +397,7 @@ else
|
||||
config.Aliases = _urls;
|
||||
config.DefaultTheme = _themetype;
|
||||
config.DefaultContainer = _containertype;
|
||||
config.DefaultAdminContainer = _admincontainertype;
|
||||
config.DefaultAdminContainer = "";
|
||||
config.SiteTemplate = _sitetemplatetype;
|
||||
config.Runtime = _runtime;
|
||||
config.RenderMode = _runtime + _prerender;
|
||||
|
@ -14,7 +14,7 @@ else
|
||||
{
|
||||
<ActionLink Action="Add" Text="Add Site" ResourceKey="AddSite" />
|
||||
|
||||
<Pager Items="@_sites">
|
||||
<Pager Items="@_sites" SearchProperties="Name">
|
||||
<Header>
|
||||
<th style="width: 1px;"> </th>
|
||||
<th style="width: 1px;"> </th>
|
||||
|
@ -66,6 +66,7 @@
|
||||
<div class="container-fluid px-0">
|
||||
<div class="row g-0 mb-2">
|
||||
<div class="col-4">
|
||||
<a href="@context.ProductUrl" target="_blank">
|
||||
@if (context.LogoUrl != null)
|
||||
{
|
||||
<img src="@context.LogoUrl" class="img-fluid" alt="@context.Name" />
|
||||
@ -74,6 +75,7 @@
|
||||
{
|
||||
<img src="/package.png" class="img-fluid" alt="@context.Name" />
|
||||
}
|
||||
</a>
|
||||
</div>
|
||||
<div class="col-8 text-end">
|
||||
<small>@SharedLocalizer["Search.Version"]:</small> <strong>@context.Version</strong>
|
||||
|
@ -28,7 +28,7 @@ else
|
||||
</div>
|
||||
</div>
|
||||
<br/>
|
||||
<Pager Items="@_urlMappings">
|
||||
<Pager Items="@_urlMappings" SearchProperties="Url">
|
||||
<Header>
|
||||
<th style="width: 1px;"> </th>
|
||||
<th style="width: 1px;"> </th>
|
||||
|
@ -2,6 +2,7 @@
|
||||
@inherits ModuleBase
|
||||
@inject NavigationManager NavigationManager
|
||||
@inject IUserService UserService
|
||||
@inject IUserRoleService UserRoleService
|
||||
@inject INotificationService NotificationService
|
||||
@inject IStringLocalizer<Add> Localizer
|
||||
@inject IStringLocalizer<SharedResources> SharedLocalizer
|
||||
@ -10,10 +11,10 @@
|
||||
{
|
||||
<div class="container">
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="to" HelpText="Enter the username you wish to send a message to" ResourceKey="To">To: </Label>
|
||||
<Label Class="col-sm-3" For="to" HelpText="Enter the user you wish to send a message to" ResourceKey="To">To: </Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="to" class="form-control" @bind="@username" />
|
||||
</div>
|
||||
<AutoComplete OnSearch="GetUsers" Placeholder="@Localizer["Username.Enter"]" @ref="username" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="subject" HelpText="Enter the subject of the message" ResourceKey="Subject">Subject: </Label>
|
||||
@ -30,11 +31,11 @@
|
||||
</div>
|
||||
<br/>
|
||||
<button type="button" class="btn btn-primary" @onclick="Send">@SharedLocalizer["Send"]</button>
|
||||
<NavLink class="btn btn-secondary" href="@NavigateUrl()">@SharedLocalizer["Cancel"]</NavLink>
|
||||
<NavLink class="btn btn-secondary" href="@PageState.ReturnUrl">@SharedLocalizer["Cancel"]</NavLink>
|
||||
}
|
||||
|
||||
@code {
|
||||
private string username = "";
|
||||
private AutoComplete username;
|
||||
private string subject = "";
|
||||
private string body = "";
|
||||
|
||||
@ -42,21 +43,35 @@
|
||||
|
||||
public override string Title => "Send Notification";
|
||||
|
||||
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.Username.Contains(filter, StringComparison.OrdinalIgnoreCase))
|
||||
.ToDictionary(item => item.UserId.ToString(), item => item.User.Username);
|
||||
}
|
||||
|
||||
private async Task Send()
|
||||
{
|
||||
try
|
||||
{
|
||||
var user = await UserService.GetUserAsync(username, PageState.Site.SiteId);
|
||||
if (user != null)
|
||||
if (!string.IsNullOrEmpty(username.Key) && !string.IsNullOrEmpty(subject))
|
||||
{
|
||||
var notification = new Notification(PageState.Site.SiteId, PageState.User, user, subject, body);
|
||||
notification = await NotificationService.AddNotificationAsync(notification);
|
||||
await logger.LogInformation("Notification Created {NotificationId}", notification.NotificationId);
|
||||
NavigationManager.NavigateTo(NavigateUrl());
|
||||
var user = await UserService.GetUserAsync(int.Parse(username.Key), ModuleState.SiteId);
|
||||
if (user != null)
|
||||
{
|
||||
var notification = new Notification(PageState.Site.SiteId, PageState.User, user, subject, body);
|
||||
notification = await NotificationService.AddNotificationAsync(notification);
|
||||
await logger.LogInformation("Notification Created {NotificationId}", notification.NotificationId);
|
||||
NavigationManager.NavigateTo(PageState.ReturnUrl);
|
||||
}
|
||||
else
|
||||
{
|
||||
AddModuleMessage(Localizer["Message.User.Invalid"], MessageType.Warning);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
AddModuleMessage(Localizer["Message.User.Invalid"], MessageType.Warning);
|
||||
AddModuleMessage(Localizer["Message.Required"], MessageType.Warning);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
|
@ -1,4 +1,5 @@
|
||||
@namespace Oqtane.Modules.Admin.UserProfile
|
||||
@using System.Net
|
||||
@using System.Text.RegularExpressions;
|
||||
@inherits ModuleBase
|
||||
@inject NavigationManager NavigationManager
|
||||
@ -11,18 +12,18 @@
|
||||
@inject IStringLocalizer<Index> Localizer
|
||||
@inject IStringLocalizer<SharedResources> SharedLocalizer
|
||||
|
||||
@if (PageState.User != null && photo != null)
|
||||
@if (_initialized)
|
||||
{
|
||||
<img src="@ImageUrl(photofileid, 400, 400)" alt="@displayname" style="max-width: 400px" class="rounded-circle mx-auto d-block">
|
||||
}
|
||||
else
|
||||
{
|
||||
<br />
|
||||
}
|
||||
<TabStrip>
|
||||
<TabPanel Name="Identity" ResourceKey="Identity">
|
||||
@if (profiles != null && settings != 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">
|
||||
}
|
||||
else
|
||||
{
|
||||
<br />
|
||||
}
|
||||
<TabStrip>
|
||||
<TabPanel Name="Identity" ResourceKey="Identity">
|
||||
<ModuleMessage Message="@_passwordrequirements" Type="MessageType.Info" />
|
||||
<div class="container">
|
||||
<div class="row mb-1 align-items-center">
|
||||
@ -33,34 +34,34 @@ else
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="password" HelpText="If you wish to change your password you can enter it here. Please choose a sufficiently secure password." ResourceKey="Password"></Label>
|
||||
<div class="col-sm-9">
|
||||
<div class="col-sm-9">
|
||||
<div class="input-group">
|
||||
<input id="password" type="@_passwordtype" class="form-control" @bind="@_password" autocomplete="new-password" />
|
||||
<input id="password" type="@_passwordtype" class="form-control" @bind="@_password" autocomplete="new-password" />
|
||||
<button type="button" class="btn btn-secondary" @onclick="@TogglePassword" tabindex="-1">@_togglepassword</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<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>
|
||||
</div>
|
||||
@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>
|
||||
<option value="True">@SharedLocalizer["Yes"]</option>
|
||||
<option value="False">@SharedLocalizer["No"]</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
@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>
|
||||
<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="email" HelpText="Your email address where you wish to receive notifications" ResourceKey="Email"></Label>
|
||||
<div class="col-sm-9">
|
||||
@ -76,18 +77,15 @@ else
|
||||
<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="@Constants.ImageFiles" ShowFolders="false" ShowFiles="true" UploadMultiple="false" FolderId="@folderid" @ref="filemanager" />
|
||||
<FileManager FileId="@photofileid" Filter="@PageState.Site.ImageFiles" ShowFolders="false" ShowFiles="true" UploadMultiple="false" FolderId="@folderid" @ref="filemanager" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<br />
|
||||
<button type="button" class="btn btn-success" @onclick="Save">@SharedLocalizer["Save"]</button>
|
||||
<button type="button" class="btn btn-secondary" @onclick="Cancel">@SharedLocalizer["Cancel"]</button>
|
||||
}
|
||||
</TabPanel>
|
||||
<TabPanel Name="Profile" ResourceKey="Profile">
|
||||
@if (profiles != null && settings != null)
|
||||
{
|
||||
</TabPanel>
|
||||
<TabPanel Name="Profile" ResourceKey="Profile">
|
||||
<div class="container">
|
||||
<div class="row mb-1 align-items-center">
|
||||
@foreach (Profile profile in profiles)
|
||||
@ -104,8 +102,8 @@ else
|
||||
}
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="@p.Name" HelpText="@p.Description">@p.Title</Label>
|
||||
<div class="col-sm-9">
|
||||
@if (!string.IsNullOrEmpty(p.Options))
|
||||
<div class="col-sm-9">
|
||||
@if (!string.IsNullOrEmpty(p.Options))
|
||||
{
|
||||
<select id="@p.Name" class="form-select" @onchange="@(e => ProfileChanged(e, p.Name))">
|
||||
@foreach (var option in p.Options.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries))
|
||||
@ -123,13 +121,27 @@ else
|
||||
}
|
||||
else
|
||||
{
|
||||
@if (p.IsRequired)
|
||||
@if (p.Rows == 1)
|
||||
{
|
||||
<input id="@p.Name" class="form-control" maxlength="@p.MaxLength" value="@GetProfileValue(p.Name, p.DefaultValue)" required @onchange="@(e => ProfileChanged(e, p.Name))" />
|
||||
@if (p.IsRequired)
|
||||
{
|
||||
<input id="@p.Name" class="form-control" maxlength="@p.MaxLength" value="@GetProfileValue(p.Name, p.DefaultValue)" required @onchange="@(e => ProfileChanged(e, p.Name))" />
|
||||
}
|
||||
else
|
||||
{
|
||||
<input id="@p.Name" class="form-control" maxlength="@p.MaxLength" value="@GetProfileValue(p.Name, p.DefaultValue)" @onchange="@(e => ProfileChanged(e, p.Name))" />
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
<input id="@p.Name" class="form-control" maxlength="@p.MaxLength" value="@GetProfileValue(p.Name, p.DefaultValue)" @onchange="@(e => ProfileChanged(e, p.Name))" />
|
||||
@if (p.IsRequired)
|
||||
{
|
||||
<textarea id="@p.Name" class="form-control" maxlength="@p.MaxLength" rows="@p.Rows" value="@GetProfileValue(p.Name, p.DefaultValue)" required @onchange="@(e => ProfileChanged(e, p.Name))"></textarea>
|
||||
}
|
||||
else
|
||||
{
|
||||
<textarea id="@p.Name" class="form-control" maxlength="@p.MaxLength" rows="@p.Rows" value="@GetProfileValue(p.Name, p.DefaultValue)" @onchange="@(e => ProfileChanged(e, p.Name))"></textarea>
|
||||
}
|
||||
}
|
||||
}
|
||||
</div>
|
||||
@ -140,134 +152,151 @@ else
|
||||
</div>
|
||||
<button type="button" class="btn btn-success" @onclick="Save">@SharedLocalizer["Save"]</button>
|
||||
<button type="button" class="btn btn-secondary" @onclick="Cancel">@SharedLocalizer["Cancel"]</button>
|
||||
}
|
||||
</TabPanel>
|
||||
<TabPanel Name="Notifications" ResourceKey="Notifications">
|
||||
@if (notifications != null)
|
||||
{
|
||||
</TabPanel>
|
||||
<TabPanel Name="Notifications" ResourceKey="Notifications">
|
||||
<ActionLink Action="Add" Text="Send Notification" Security="SecurityAccessLevel.View" EditMode="false" ResourceKey="SendNotification" ReturnUrl="@NavigateUrl(PageState.Page.Path, "tab=Notifications")" />
|
||||
<br />
|
||||
<br />
|
||||
<select class="form-select" @onchange="(e => FilterChanged(e))">
|
||||
<option value="to">@Localizer["Inbox"]</option>
|
||||
<option value="from">@Localizer["Items.Sent"]</option>
|
||||
</select>
|
||||
<br />
|
||||
<ActionLink Action="Add" Text="Send Notification" Security="SecurityAccessLevel.View" EditMode="false" ResourceKey="SendNotification" />
|
||||
<br /><br />
|
||||
@if (filter == "to")
|
||||
{
|
||||
<Pager Items="@notifications">
|
||||
<Header>
|
||||
@if (notifications.Any())
|
||||
{
|
||||
<Pager Items="@notifications">
|
||||
<Header>
|
||||
<th style="width: 1px;"> </th>
|
||||
<th style="width: 1px;"> </th>
|
||||
<th>@Localizer["From"]</th>
|
||||
<th>@Localizer["Subject"]</th>
|
||||
<th>@Localizer["Received"]</th>
|
||||
</Header>
|
||||
<Row>
|
||||
<td><ActionLink Action="View" Parameters="@($"id=" + context.NotificationId.ToString())" Security="SecurityAccessLevel.View" EditMode="false" ResourceKey="ViewNotification" /></td>
|
||||
<td><ActionDialog Header="Delete Notification" Message="Are You Sure You Wish To Delete This Notification?" Action="Delete" Security="SecurityAccessLevel.View" Class="btn btn-danger" OnClick="@(async () => await Delete(context))" EditMode="false" ResourceKey="DeleteNotification" /></td>
|
||||
|
||||
@if (context.IsRead)
|
||||
{
|
||||
<td>@context.FromDisplayName</td>
|
||||
<td>@context.Subject</td>
|
||||
<td>@string.Format("{0:dd-MMM-yyyy HH:mm:ss}", @context.CreatedOn)</td>
|
||||
}
|
||||
else
|
||||
{
|
||||
<td><b>@context.FromDisplayName</b></td>
|
||||
<td><b>@context.Subject</b></td>
|
||||
<td><b>@string.Format("{0:dd-MMM-yyyy HH:mm:ss}", @context.CreatedOn)</b></td>
|
||||
}
|
||||
</Row>
|
||||
<Detail>
|
||||
<td colspan="2"></td>
|
||||
<td colspan="3">
|
||||
@{
|
||||
string input = "___";
|
||||
if (context.Body.Contains(input))
|
||||
{
|
||||
context.Body = context.Body.Split(input)[0];
|
||||
context.Body = context.Body.Replace("\n", "");
|
||||
context.Body = context.Body.Replace("\r", "");
|
||||
}
|
||||
notificationSummary = context.Body.Length > 100 ? (context.Body.Substring(0, 97) + "...") : context.Body;
|
||||
}
|
||||
</Header>
|
||||
<Row>
|
||||
<td><ActionLink Action="View" Parameters="@($"id=" + context.NotificationId.ToString())" Security="SecurityAccessLevel.View" EditMode="false" ResourceKey="ViewNotification" ReturnUrl="@NavigateUrl(PageState.Page.Path, "tab=Notifications")" /></td>
|
||||
<td><ActionDialog Header="Delete Notification" Message="Are You Sure You Wish To Delete This Notification?" Action="Delete" Security="SecurityAccessLevel.View" Class="btn btn-danger" OnClick="@(async () => await Delete(context))" EditMode="false" ResourceKey="DeleteNotification" /></td>
|
||||
|
||||
@if (context.IsRead)
|
||||
{
|
||||
@notificationSummary
|
||||
<td>@context.FromDisplayName</td>
|
||||
<td>@context.Subject</td>
|
||||
<td>@string.Format("{0:dd-MMM-yyyy HH:mm:ss}", @context.CreatedOn)</td>
|
||||
}
|
||||
else
|
||||
{
|
||||
<b>@notificationSummary</b>
|
||||
<td><b>@context.FromDisplayName</b></td>
|
||||
<td><b>@context.Subject</b></td>
|
||||
<td><b>@string.Format("{0:dd-MMM-yyyy HH:mm:ss}", @context.CreatedOn)</b></td>
|
||||
}
|
||||
</td>
|
||||
</Detail>
|
||||
</Pager>
|
||||
</Row>
|
||||
<Detail>
|
||||
<td colspan="2"></td>
|
||||
<td colspan="3">
|
||||
@{
|
||||
string input = "___";
|
||||
if (context.Body.Contains(input))
|
||||
{
|
||||
context.Body = context.Body.Split(input)[0];
|
||||
context.Body = context.Body.Replace("\n", "");
|
||||
context.Body = context.Body.Replace("\r", "");
|
||||
}
|
||||
notificationSummary = context.Body.Length > 100 ? (context.Body.Substring(0, 97) + "...") : context.Body;
|
||||
}
|
||||
@if (context.IsRead)
|
||||
{
|
||||
@notificationSummary
|
||||
}
|
||||
else
|
||||
{
|
||||
<b>@notificationSummary</b>
|
||||
}
|
||||
</td>
|
||||
</Detail>
|
||||
</Pager>
|
||||
<br />
|
||||
<ActionDialog Header="Clear Notifications" Message="Are You Sure You Wish To Permanently Delete All Notifications ?" Action="Delete All Notifications" Security="SecurityAccessLevel.Admin" Class="btn btn-danger" OnClick="@(async () => await DeleteAllNotifications())" ResourceKey="DeleteAllNotifications" />
|
||||
}
|
||||
else
|
||||
{
|
||||
<div class="no-notifications-text">
|
||||
@Localizer["NoNotificationsReceived.Text"]
|
||||
</div>
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
<Pager Items="@notifications">
|
||||
<Header>
|
||||
<th> </th>
|
||||
<th> </th>
|
||||
@if (notifications.Any())
|
||||
{
|
||||
<Pager Items="@notifications">
|
||||
<Header>
|
||||
<th style="width: 1px;"></th>
|
||||
<th style="width: 1px;"></th>
|
||||
<th>@Localizer["To"]</th>
|
||||
<th>@Localizer["Subject"]</th>
|
||||
<th>@Localizer["Sent"]</th>
|
||||
</Header>
|
||||
<Row>
|
||||
<td><ActionLink Action="View" Parameters="@($"id=" + context.NotificationId.ToString())" Security="SecurityAccessLevel.View" EditMode="false" ResourceKey="ViewNotification" /></td>
|
||||
<td><ActionDialog Header="Delete Notification" Message="Are You Sure You Wish To Delete This Notification?" Action="Delete" Security="SecurityAccessLevel.View" Class="btn btn-danger" OnClick="@(async () => await Delete(context))" EditMode="false" ResourceKey="DeleteNotification" /></td>
|
||||
</Header>
|
||||
<Row>
|
||||
<td><ActionLink Action="View" Parameters="@($"id=" + context.NotificationId.ToString())" Security="SecurityAccessLevel.View" EditMode="false" ResourceKey="ViewNotification" ReturnUrl="@NavigateUrl(PageState.Page.Path, "tab=Notifications")" /></td>
|
||||
<td><ActionDialog Header="Delete Notification" Message="Are You Sure You Wish To Delete This Notification?" Action="Delete" Security="SecurityAccessLevel.View" Class="btn btn-danger" OnClick="@(async () => await Delete(context))" EditMode="false" ResourceKey="DeleteNotification" /></td>
|
||||
|
||||
@if (context.IsRead)
|
||||
{
|
||||
<td>@context.ToDisplayName</td>
|
||||
<td>@context.Subject</td>
|
||||
<td>@string.Format("{0:dd-MMM-yyyy HH:mm:ss}", @context.CreatedOn)</td>
|
||||
}
|
||||
else
|
||||
{
|
||||
<td><b>@context.ToDisplayName</b></td>
|
||||
<td><b>@context.Subject</b></td>
|
||||
<td><b>@string.Format("{0:dd-MMM-yyyy HH:mm:ss}", @context.CreatedOn)</b></td>
|
||||
}
|
||||
|
||||
</Row>
|
||||
<Detail>
|
||||
<td colspan="2"></td>
|
||||
<td colspan="3">
|
||||
@{
|
||||
string input = "___";
|
||||
if (context.Body.Contains(input))
|
||||
{
|
||||
context.Body = context.Body.Split(input)[0];
|
||||
context.Body = context.Body.Replace("\n", "");
|
||||
context.Body = context.Body.Replace("\r", "");
|
||||
}
|
||||
notificationSummary = context.Body.Length > 100 ? (context.Body.Substring(0, 97) + "...") : context.Body;
|
||||
}
|
||||
@if (context.IsRead)
|
||||
{
|
||||
@notificationSummary
|
||||
<td>@context.ToDisplayName</td>
|
||||
<td>@context.Subject</td>
|
||||
<td>@string.Format("{0:dd-MMM-yyyy HH:mm:ss}", @context.CreatedOn)</td>
|
||||
}
|
||||
else
|
||||
else
|
||||
{
|
||||
<b>@notificationSummary</b>
|
||||
}
|
||||
</td>
|
||||
</Detail>
|
||||
</Pager>
|
||||
<td><b>@context.ToDisplayName</b></td>
|
||||
<td><b>@context.Subject</b></td>
|
||||
<td><b>@string.Format("{0:dd-MMM-yyyy HH:mm:ss}", @context.CreatedOn)</b></td>
|
||||
}
|
||||
|
||||
</Row>
|
||||
<Detail>
|
||||
<td colspan="2"></td>
|
||||
<td colspan="3">
|
||||
@{
|
||||
string input = "___";
|
||||
if (context.Body.Contains(input))
|
||||
{
|
||||
context.Body = context.Body.Split(input)[0];
|
||||
context.Body = context.Body.Replace("\n", "");
|
||||
context.Body = context.Body.Replace("\r", "");
|
||||
}
|
||||
notificationSummary = context.Body.Length > 100 ? (context.Body.Substring(0, 97) + "...") : context.Body;
|
||||
}
|
||||
@if (context.IsRead)
|
||||
{
|
||||
@notificationSummary
|
||||
}
|
||||
else
|
||||
{
|
||||
<b>@notificationSummary</b>
|
||||
}
|
||||
</td>
|
||||
</Detail>
|
||||
</Pager>
|
||||
<br />
|
||||
<ActionDialog Header="Clear Notifications" Message="Are You Sure You Wish To Permanently Delete All Notifications ?" Action="Delete All Notifications" Security="SecurityAccessLevel.Admin" Class="btn btn-danger" OnClick="@(async () => await DeleteAllNotifications())" ResourceKey="DeleteAllNotifications" />
|
||||
}
|
||||
else
|
||||
{
|
||||
<div class="no-notifications-text">
|
||||
@Localizer["NoNotificationsSent.Text"]
|
||||
</div>
|
||||
}
|
||||
}
|
||||
@if (notifications.Any())
|
||||
{
|
||||
<br />
|
||||
<ActionDialog Header="Clear Notifications" Message="Are You Sure You Wish To Permanently Delete All Notifications ?" Action="Delete All Notifications" Security="SecurityAccessLevel.Admin" Class="btn btn-danger" OnClick="@(async () => await DeleteAllNotifications())" ResourceKey="DeleteAllNotifications" />
|
||||
}
|
||||
}
|
||||
</TabPanel>
|
||||
</TabStrip>
|
||||
<br /><br />
|
||||
</TabPanel>
|
||||
</TabStrip>
|
||||
<br />
|
||||
<br />
|
||||
}
|
||||
|
||||
@code {
|
||||
private bool _initialized = false;
|
||||
private string _passwordrequirements;
|
||||
private string username = string.Empty;
|
||||
private string _password = string.Empty;
|
||||
@ -282,24 +311,25 @@ else
|
||||
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> settings;
|
||||
private string category = string.Empty;
|
||||
|
||||
private string filter = "to";
|
||||
private List<Notification> notifications;
|
||||
private string notificationSummary = string.Empty;
|
||||
|
||||
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.View;
|
||||
|
||||
protected override async Task OnParametersSetAsync()
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
_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);
|
||||
|
||||
if (PageState.User != null)
|
||||
{
|
||||
@ -308,6 +338,11 @@ else
|
||||
email = PageState.User.Email;
|
||||
displayname = PageState.User.DisplayName;
|
||||
|
||||
if (string.IsNullOrEmpty(email))
|
||||
{
|
||||
AddModuleMessage(Localizer["Message.User.NoEmail"], MessageType.Warning);
|
||||
}
|
||||
|
||||
// get user folder
|
||||
var folder = await FolderService.GetFolderAsync(ModuleState.SiteId, PageState.User.FolderPath);
|
||||
if (folder != null)
|
||||
@ -325,11 +360,14 @@ else
|
||||
photofileid = -1;
|
||||
photo = null;
|
||||
}
|
||||
var sitesettings = await SettingService.GetSiteSettingsAsync(SiteState.Alias.SiteId);
|
||||
_ImageFiles = SettingService.GetSetting(settings, "ImageFiles", Constants.ImageFiles);
|
||||
|
||||
profiles = await ProfileService.GetProfilesAsync(ModuleState.SiteId);
|
||||
settings = await SettingService.GetUserSettingsAsync(PageState.User.UserId);
|
||||
|
||||
await LoadNotificationsAsync();
|
||||
|
||||
_initialized = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -363,44 +401,54 @@ else
|
||||
{
|
||||
try
|
||||
{
|
||||
if (username != string.Empty && email != string.Empty && ValidateProfiles())
|
||||
if (username != string.Empty && email != string.Empty)
|
||||
{
|
||||
if (_password == confirm)
|
||||
{
|
||||
var user = PageState.User;
|
||||
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();
|
||||
if (user.PhotoFileId == -1)
|
||||
if (ValidateProfiles())
|
||||
{
|
||||
user.PhotoFileId = null;
|
||||
}
|
||||
if (user.PhotoFileId != null)
|
||||
{
|
||||
photofileid = user.PhotoFileId.Value;
|
||||
photo = await FileService.GetFileAsync(photofileid);
|
||||
}
|
||||
else
|
||||
{
|
||||
photofileid = -1;
|
||||
photo = null;
|
||||
}
|
||||
var user = PageState.User;
|
||||
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();
|
||||
if (user.PhotoFileId == -1)
|
||||
{
|
||||
user.PhotoFileId = null;
|
||||
}
|
||||
if (user.PhotoFileId != null)
|
||||
{
|
||||
photofileid = user.PhotoFileId.Value;
|
||||
photo = await FileService.GetFileAsync(photofileid);
|
||||
}
|
||||
else
|
||||
{
|
||||
photofileid = -1;
|
||||
photo = null;
|
||||
}
|
||||
|
||||
user = await UserService.UpdateUserAsync(user);
|
||||
if (user != null)
|
||||
{
|
||||
await SettingService.UpdateUserSettingsAsync(settings, PageState.User.UserId);
|
||||
await logger.LogInformation("User Profile Saved");
|
||||
user = await UserService.UpdateUserAsync(user);
|
||||
if (user != null)
|
||||
{
|
||||
await SettingService.UpdateUserSettingsAsync(settings, PageState.User.UserId);
|
||||
await logger.LogInformation("User Profile Saved");
|
||||
|
||||
AddModuleMessage(Localizer["Success.Profile.Update"], MessageType.Success);
|
||||
StateHasChanged();
|
||||
}
|
||||
else
|
||||
{
|
||||
AddModuleMessage(Localizer["Message.Password.Complexity"], MessageType.Error);
|
||||
if (PageState.QueryString.ContainsKey("returnurl"))
|
||||
{
|
||||
NavigationManager.NavigateTo(WebUtility.UrlDecode(PageState.QueryString["returnurl"]));
|
||||
}
|
||||
else // legacy behavior
|
||||
{
|
||||
AddModuleMessage(Localizer["Success.Profile.Update"], MessageType.Success);
|
||||
StateHasChanged();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
AddModuleMessage(Localizer["Message.Password.Complexity"], MessageType.Error);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
@ -424,27 +472,33 @@ else
|
||||
|
||||
private bool ValidateProfiles()
|
||||
{
|
||||
bool valid = true;
|
||||
foreach (Profile profile in profiles)
|
||||
{
|
||||
if (string.IsNullOrEmpty(SettingService.GetSetting(settings, profile.Name, string.Empty)) && !string.IsNullOrEmpty(profile.DefaultValue))
|
||||
var value = GetProfileValue(profile.Name, string.Empty);
|
||||
if (string.IsNullOrEmpty(value) && !string.IsNullOrEmpty(profile.DefaultValue))
|
||||
{
|
||||
settings = SettingService.SetSetting(settings, profile.Name, profile.DefaultValue);
|
||||
}
|
||||
if (!profile.IsPrivate || UserSecurity.IsAuthorized(PageState.User, RoleNames.Admin))
|
||||
{
|
||||
if (valid == true && profile.IsRequired && string.IsNullOrEmpty(SettingService.GetSetting(settings, profile.Name, string.Empty)))
|
||||
if (profile.IsRequired && string.IsNullOrEmpty(value))
|
||||
{
|
||||
valid = false;
|
||||
AddModuleMessage(string.Format(SharedLocalizer["ProfileRequired"], profile.Title), MessageType.Warning);
|
||||
return false;
|
||||
}
|
||||
if (valid == true && !string.IsNullOrEmpty(profile.Validation))
|
||||
if (!string.IsNullOrEmpty(profile.Validation))
|
||||
{
|
||||
Regex regex = new Regex(profile.Validation);
|
||||
valid = regex.Match(SettingService.GetSetting(settings, profile.Name, string.Empty)).Success;
|
||||
bool valid = regex.Match(value).Success;
|
||||
if (!valid)
|
||||
{
|
||||
AddModuleMessage(string.Format(SharedLocalizer["ProfileInvalid"], profile.Title), MessageType.Warning);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return valid;
|
||||
return true;
|
||||
}
|
||||
|
||||
private void Cancel()
|
||||
@ -486,7 +540,6 @@ else
|
||||
private async void FilterChanged(ChangeEventArgs e)
|
||||
{
|
||||
filter = (string)e.Value;
|
||||
|
||||
await LoadNotificationsAsync();
|
||||
StateHasChanged();
|
||||
}
|
||||
|
@ -8,94 +8,71 @@
|
||||
|
||||
@if (PageState.User != null)
|
||||
{
|
||||
<div class="container">
|
||||
<div class="row mb-1 align-items-center">
|
||||
<label Class="col-sm-3">@Localizer["Title"] </label>
|
||||
@if (title == "From")
|
||||
{
|
||||
<div class="col-sm-3">
|
||||
<input class="form-control" @bind="@username" readonly />
|
||||
</div>
|
||||
}
|
||||
@if (title == "To")
|
||||
{
|
||||
<div class="col-sm-3">
|
||||
<input class="form-control" @bind="@username" />
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<label Class="col-sm-3">@Localizer["Subject"] </label>
|
||||
@if (title == "From")
|
||||
{
|
||||
<div class="col-sm-3">
|
||||
<input class="form-control" @bind="@subject" readonly />
|
||||
</div>
|
||||
}
|
||||
@if (title == "To")
|
||||
{
|
||||
<div class="col-sm-3">
|
||||
<input class="form-control" @bind="@subject" />
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
<div class="container">
|
||||
@if (title == "From")
|
||||
{
|
||||
@if (title == "From")
|
||||
{
|
||||
<div class="container">
|
||||
<div class="row mb-1 align-items-center">
|
||||
<label class="col-sm-3">@Localizer["Date"] </label>
|
||||
<Label Class="col-sm-3" For="username" HelpText="The user who sent the message" ResourceKey="From">From:</Label>
|
||||
<div class="col-sm-9">
|
||||
<input class="form-control" @bind="@createdon" readonly />
|
||||
<input id="username" class="form-control" @bind="@username" readonly />
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
@if (title == "From")
|
||||
{
|
||||
<div class="row mb-1 align-items-center">
|
||||
<label class="col-sm-3">@Localizer["Message"] </label>
|
||||
<div class="col-sm-9">
|
||||
<textarea id="txtFrom" class="form-control" @bind="@body" rows="5" readonly />
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="subject" HelpText="The subject of the message" ResourceKey="Subject">Subject:</Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="subject" class="form-control" @bind="@subject" readonly />
|
||||
</div>
|
||||
|
||||
}
|
||||
@if (title == "To")
|
||||
{
|
||||
|
||||
<div class="row mb-1 align-items-center">
|
||||
<label class="col-sm-3">@Localizer["Message"] </label>
|
||||
<div class="col-sm-9">
|
||||
<textarea id="txtTo" class="form-control" @bind="@body" rows="5" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label class="col-sm-3" For="date" HelpText="The date the message was sent" ResourceKey="Date">Sent:</Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="date" class="form-control" @bind="@createdon" readonly />
|
||||
</div>
|
||||
|
||||
}
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
@if (reply != string.Empty)
|
||||
{
|
||||
<button type="button" class="btn btn-primary" @onclick="Send">@SharedLocalizer["Send"]</button>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label class="col-sm-3" For="message" HelpText="The contents of the message" ResourceKey="Message">Message:</Label>
|
||||
<div class="col-sm-9">
|
||||
<textarea id="message" class="form-control" @bind="@body" rows="5" readonly />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
else
|
||||
{
|
||||
if (title == "From")
|
||||
{
|
||||
<button type="button" class="btn btn-primary" @onclick="Reply">@Localizer["Reply"]</button>
|
||||
}
|
||||
}
|
||||
<NavLink class="btn btn-secondary" href="@NavigateUrl()">@SharedLocalizer["Cancel"]</NavLink>
|
||||
<br />
|
||||
<br />
|
||||
@if (title == "To")
|
||||
{
|
||||
<div class="control-group">
|
||||
<label class="control-label">@Localizer["OriginalMessage"] </label>
|
||||
<textarea id="txtReply" class="form-control" @bind="@reply" rows="5" readonly />
|
||||
<div class="container">
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="username" HelpText="The user who will be the recipient of the message" ResourceKey="To">To:</Label>
|
||||
<div class="col-sm-9">
|
||||
<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="subject" HelpText="The subject of the message" ResourceKey="Subject">Subject:</Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="subject" class="form-control" @bind="@subject" readonly="@(!reply)" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label class="col-sm-3" For="message" HelpText="The content of the message" ResourceKey="Message">Message:</Label>
|
||||
<div class="col-sm-9">
|
||||
<textarea id="message" class="form-control" @bind="@body" rows="5" readonly="@(!reply)" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
@if (reply)
|
||||
{
|
||||
<button type="button" class="btn btn-primary me-2" @onclick="Send">@SharedLocalizer["Send"]</button>
|
||||
}
|
||||
else
|
||||
{
|
||||
if (title == "From" && username != Localizer["System"])
|
||||
{
|
||||
<button type="button" class="btn btn-primary me-2" @onclick="Reply">@Localizer["Reply"]</button>
|
||||
}
|
||||
}
|
||||
<NavLink class="btn btn-secondary" href="@PageState.ReturnUrl">@SharedLocalizer["Cancel"]</NavLink>
|
||||
}
|
||||
|
||||
@code {
|
||||
@ -105,7 +82,7 @@
|
||||
private string subject = string.Empty;
|
||||
private string createdon = string.Empty;
|
||||
private string body = string.Empty;
|
||||
private string reply = string.Empty;
|
||||
private bool reply = false;
|
||||
|
||||
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.View;
|
||||
public override string Title => "View Notification";
|
||||
@ -118,9 +95,6 @@
|
||||
Notification notification = await NotificationService.GetNotificationAsync(notificationid);
|
||||
if (notification != null)
|
||||
{
|
||||
notification.IsRead = true;
|
||||
notification = await NotificationService.UpdateNotificationAsync(notification);
|
||||
|
||||
int userid = -1;
|
||||
if (notification.ToUserId == PageState.User.UserId)
|
||||
{
|
||||
@ -148,11 +122,17 @@
|
||||
}
|
||||
if (username == "")
|
||||
{
|
||||
username = "System";
|
||||
username = Localizer["System"];
|
||||
}
|
||||
subject = notification.Subject;
|
||||
createdon = notification.CreatedOn.ToString();
|
||||
body = notification.Body;
|
||||
|
||||
if (title == "From")
|
||||
{
|
||||
notification.IsRead = true;
|
||||
notification = await NotificationService.UpdateNotificationAsync(notification);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
@ -165,12 +145,16 @@
|
||||
private void Reply()
|
||||
{
|
||||
title = "To";
|
||||
if (!subject.Contains("RE:"))
|
||||
if (!subject.Contains(Localizer["RE:"]))
|
||||
{
|
||||
subject = "RE: " + subject;
|
||||
subject = Localizer["RE"] + " " + subject;
|
||||
}
|
||||
reply = body;
|
||||
body = "\n\n____________________________________________\nSent: " + createdon + "\nSubject: " + subject + "\n\n" + body;
|
||||
body = $"\n\n____________________________________________\n" +
|
||||
$"{Localizer["From.Text"]} {username}\n" +
|
||||
$"{Localizer["Date.Text"]} {createdon}\n" +
|
||||
$"{Localizer["Subject.Text"]} {subject}\n\n" +
|
||||
body;
|
||||
reply = true;
|
||||
StateHasChanged();
|
||||
}
|
||||
|
||||
@ -184,7 +168,7 @@
|
||||
var notification = new Notification(PageState.Site.SiteId, PageState.User, user, subject, body, notificationid);
|
||||
notification = await NotificationService.AddNotificationAsync(notification);
|
||||
await logger.LogInformation("Notification Created {NotificationId}", notification.NotificationId);
|
||||
NavigationManager.NavigateTo(NavigateUrl());
|
||||
NavigationManager.NavigateTo(PageState.ReturnUrl);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -8,63 +8,63 @@
|
||||
@inject IStringLocalizer<Add> Localizer
|
||||
@inject IStringLocalizer<SharedResources> SharedLocalizer
|
||||
|
||||
<TabStrip>
|
||||
<TabPanel Name="Identity" ResourceKey="Identity">
|
||||
@if (profiles != null)
|
||||
{
|
||||
<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="A unique username for a user. Note that this field can not be modified once it is saved." ResourceKey="Username"></Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="username" class="form-control" @bind="@_username" />
|
||||
@if (_initialized)
|
||||
{
|
||||
<TabStrip>
|
||||
<TabPanel Name="Identity" ResourceKey="Identity">
|
||||
@if (profiles != null)
|
||||
{
|
||||
<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="A unique username for a user. Note that this field can not be modified once it is saved." ResourceKey="Username"></Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="username" class="form-control" @bind="@_username" />
|
||||
</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>
|
||||
<div class="col-sm-9">
|
||||
<div class="input-group">
|
||||
<input id="password" type="@_passwordtype" class="form-control" @bind="@_password" autocomplete="new-password" required />
|
||||
<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="confirm" HelpText="Please enter the password again to confirm it matches with the value above" 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" required />
|
||||
<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>
|
||||
<div class="col-sm-9">
|
||||
<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>
|
||||
<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="notify" HelpText="Indicate if new users should receive an email notification" ResourceKey="Notify">Notify? </Label>
|
||||
<div class="col-sm-9">
|
||||
<select id="notify" class="form-select" @bind="@_notify" required>
|
||||
<option value="True">@SharedLocalizer["Yes"]</option>
|
||||
<option value="False">@SharedLocalizer["No"]</option>
|
||||
</select>
|
||||
</div>
|
||||
</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>
|
||||
<div class="col-sm-9">
|
||||
<div class="input-group">
|
||||
<input id="password" type="@_passwordtype" class="form-control" @bind="@_password" autocomplete="new-password" required />
|
||||
<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="confirm" HelpText="Please enter the password again to confirm it matches with the value above" 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" required />
|
||||
<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>
|
||||
<div class="col-sm-9">
|
||||
<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>
|
||||
<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="notify" HelpText="Indicate if new users should receive an email notification" ResourceKey="Notify">Notify? </Label>
|
||||
<div class="col-sm-9">
|
||||
<select id="notify" class="form-select" @bind="@_notify" required>
|
||||
<option value="True">@SharedLocalizer["Yes"]</option>
|
||||
<option value="False">@SharedLocalizer["No"]</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
</TabPanel>
|
||||
<TabPanel Name="Profile" ResourceKey="Profile">
|
||||
@if (profiles != null)
|
||||
{
|
||||
}
|
||||
</TabPanel>
|
||||
<TabPanel Name="Profile" ResourceKey="Profile">
|
||||
<div class="container">
|
||||
<div class="row mb-1 align-items-center">
|
||||
@foreach (Profile profile in profiles)
|
||||
@ -79,30 +79,50 @@
|
||||
}
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="@p.Name" HelpText="@p.Description">@p.Title</Label>
|
||||
<div class="col-sm-9">
|
||||
@if (p.IsRequired)
|
||||
<div class="col-sm-9">
|
||||
@if (!string.IsNullOrEmpty(p.Options))
|
||||
{
|
||||
<input id="@p.Name" class="form-control" maxlength="@p.MaxLength" value="@GetProfileValue(p.Name, p.DefaultValue)" required @onchange="@(e => ProfileChanged(e, p.Name))" />
|
||||
<select id="@p.Name" class="form-select" @onchange="@(e => ProfileChanged(e, p.Name))">
|
||||
@foreach (var option in p.Options.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries))
|
||||
{
|
||||
@if (GetProfileValue(p.Name, "") == option || (GetProfileValue(p.Name, "") == "" && p.DefaultValue == option))
|
||||
{
|
||||
<option value="@option" selected>@option</option>
|
||||
}
|
||||
else
|
||||
{
|
||||
<option value="@option">@option</option>
|
||||
}
|
||||
}
|
||||
</select>
|
||||
}
|
||||
else
|
||||
{
|
||||
<input id="@p.Name" class="form-control" maxlength="@p.MaxLength" value="@GetProfileValue(p.Name, p.DefaultValue)" @onchange="@(e => ProfileChanged(e, p.Name))" />
|
||||
@if (p.Rows == 1)
|
||||
{
|
||||
<input id="@p.Name" class="form-control" maxlength="@p.MaxLength" value="@GetProfileValue(p.Name, p.DefaultValue)" @onchange="@(e => ProfileChanged(e, p.Name))" />
|
||||
}
|
||||
else
|
||||
{
|
||||
<textarea id="@p.Name" class="form-control" maxlength="@p.MaxLength" rows="@p.Rows" value="@GetProfileValue(p.Name, p.DefaultValue)" @onchange="@(e => ProfileChanged(e, p.Name))"></textarea>
|
||||
}
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
</TabPanel>
|
||||
</TabStrip>
|
||||
<br />
|
||||
<br />
|
||||
<button type="button" class="btn btn-success" @onclick="SaveUser">@SharedLocalizer["Save"]</button>
|
||||
<NavLink class="btn btn-secondary" href="@NavigateUrl()">@SharedLocalizer["Cancel"]</NavLink>
|
||||
</TabPanel>
|
||||
</TabStrip>
|
||||
<br />
|
||||
<br />
|
||||
<button type="button" class="btn btn-success" @onclick="SaveUser">@SharedLocalizer["Save"]</button>
|
||||
<NavLink class="btn btn-secondary" href="@NavigateUrl()">@SharedLocalizer["Cancel"]</NavLink>
|
||||
}
|
||||
|
||||
|
||||
@code {
|
||||
private bool _initialized = false;
|
||||
private string _passwordrequirements;
|
||||
private string _username = string.Empty;
|
||||
private string _password = string.Empty;
|
||||
@ -126,6 +146,7 @@
|
||||
_togglepassword = SharedLocalizer["ShowPassword"];
|
||||
profiles = await ProfileService.GetProfilesAsync(ModuleState.SiteId);
|
||||
settings = new Dictionary<string, string>();
|
||||
_initialized = true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@ -148,31 +169,34 @@
|
||||
{
|
||||
try
|
||||
{
|
||||
if (_username != string.Empty && _password != string.Empty && _confirm != string.Empty && _email != string.Empty && ValidateProfiles())
|
||||
if (_username != string.Empty && _password != string.Empty && _confirm != string.Empty && _email != string.Empty)
|
||||
{
|
||||
if (_password == _confirm)
|
||||
{
|
||||
var user = new User();
|
||||
user.SiteId = PageState.Site.SiteId;
|
||||
user.Username = _username;
|
||||
user.Password = _password;
|
||||
user.Email = _email;
|
||||
user.DisplayName = string.IsNullOrWhiteSpace(_displayname) ? _username : _displayname;
|
||||
user.PhotoFileId = null;
|
||||
user.SuppressNotification = !bool.Parse(_notify);
|
||||
|
||||
user = await UserService.AddUserAsync(user);
|
||||
|
||||
if (user != null)
|
||||
if (ValidateProfiles())
|
||||
{
|
||||
await SettingService.UpdateUserSettingsAsync(settings, user.UserId);
|
||||
await logger.LogInformation("User Created {User}", user);
|
||||
NavigationManager.NavigateTo(NavigateUrl());
|
||||
}
|
||||
else
|
||||
{
|
||||
await logger.LogError("Error Adding User {Username} {Email}", _username, _email);
|
||||
AddModuleMessage(Localizer["Error.User.AddCheckPass"], MessageType.Error);
|
||||
var user = new User();
|
||||
user.SiteId = PageState.Site.SiteId;
|
||||
user.Username = _username;
|
||||
user.Password = _password;
|
||||
user.Email = _email;
|
||||
user.DisplayName = string.IsNullOrWhiteSpace(_displayname) ? _username : _displayname;
|
||||
user.PhotoFileId = null;
|
||||
user.SuppressNotification = !bool.Parse(_notify);
|
||||
|
||||
user = await UserService.AddUserAsync(user);
|
||||
|
||||
if (user != null)
|
||||
{
|
||||
await SettingService.UpdateUserSettingsAsync(settings, user.UserId);
|
||||
await logger.LogInformation("User Created {User}", user);
|
||||
NavigationManager.NavigateTo(NavigateUrl());
|
||||
}
|
||||
else
|
||||
{
|
||||
await logger.LogError("Error Adding User {Username} {Email}", _username, _email);
|
||||
AddModuleMessage(Localizer["Error.User.AddCheckPass"], MessageType.Error);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
@ -194,27 +218,33 @@
|
||||
|
||||
private bool ValidateProfiles()
|
||||
{
|
||||
bool valid = true;
|
||||
foreach (Profile profile in profiles)
|
||||
{
|
||||
if (string.IsNullOrEmpty(SettingService.GetSetting(settings, profile.Name, string.Empty)) && !string.IsNullOrEmpty(profile.DefaultValue))
|
||||
var value = GetProfileValue(profile.Name, string.Empty);
|
||||
if (string.IsNullOrEmpty(value) && !string.IsNullOrEmpty(profile.DefaultValue))
|
||||
{
|
||||
settings = SettingService.SetSetting(settings, profile.Name, profile.DefaultValue);
|
||||
}
|
||||
if (!profile.IsPrivate || UserSecurity.IsAuthorized(PageState.User, RoleNames.Admin))
|
||||
{
|
||||
if (valid == true && profile.IsRequired && string.IsNullOrEmpty(SettingService.GetSetting(settings, profile.Name, string.Empty)))
|
||||
if (profile.IsRequired && string.IsNullOrEmpty(value))
|
||||
{
|
||||
valid = false;
|
||||
AddModuleMessage(string.Format(SharedLocalizer["ProfileRequired"], profile.Title), MessageType.Warning);
|
||||
return false;
|
||||
}
|
||||
if (valid == true && !string.IsNullOrEmpty(profile.Validation))
|
||||
if (!string.IsNullOrEmpty(profile.Validation))
|
||||
{
|
||||
Regex regex = new Regex(profile.Validation);
|
||||
valid = regex.Match(SettingService.GetSetting(settings, profile.Name, string.Empty)).Success;
|
||||
bool valid = regex.Match(value).Success;
|
||||
if (!valid)
|
||||
{
|
||||
AddModuleMessage(string.Format(SharedLocalizer["ProfileInvalid"], profile.Title), MessageType.Warning);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return valid;
|
||||
return true;
|
||||
}
|
||||
|
||||
private void ProfileChanged(ChangeEventArgs e, string SettingName)
|
||||
|
@ -9,18 +9,10 @@
|
||||
@inject IStringLocalizer<Edit> Localizer
|
||||
@inject IStringLocalizer<SharedResources> SharedLocalizer
|
||||
|
||||
@if (PageState.User != null && photo != null)
|
||||
@if (_initialized)
|
||||
{
|
||||
<img src="@photo.Url" alt="@displayname" style="max-width: 400px" class="rounded-circle mx-auto d-block">
|
||||
}
|
||||
else
|
||||
{
|
||||
<br />
|
||||
}
|
||||
<TabStrip>
|
||||
<TabPanel Name="Identity" ResourceKey="Identity">
|
||||
@if (profiles != null)
|
||||
{
|
||||
<TabStrip>
|
||||
<TabPanel Name="Identity" ResourceKey="Identity">
|
||||
<ModuleMessage Message="@_passwordrequirements" Type="MessageType.Info" />
|
||||
<div class="container">
|
||||
<div class="row mb-1 align-items-center">
|
||||
@ -31,20 +23,20 @@ else
|
||||
</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>
|
||||
<div class="col-sm-9">
|
||||
<div class="col-sm-9">
|
||||
<div class="input-group">
|
||||
<input id="password" type="@_passwordtype" class="form-control" @bind="@_password" autocomplete="new-password" />
|
||||
<input id="password" type="@_passwordtype" class="form-control" @bind="@_password" autocomplete="new-password" />
|
||||
<button type="button" class="btn btn-secondary" @onclick="@TogglePassword" tabindex="-1">@_togglepassword</button>
|
||||
</div>
|
||||
</div>
|
||||
</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>
|
||||
<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>
|
||||
<div class="row mb-1 align-items-center">
|
||||
@ -59,12 +51,6 @@ else
|
||||
<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 the user" ResourceKey="Photo"></Label>
|
||||
<div class="col-sm-9">
|
||||
<FileManager FileId="@photofileid" @ref="filemanager" />
|
||||
</div>
|
||||
</div>
|
||||
<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>
|
||||
<div class="col-sm-9">
|
||||
@ -87,11 +73,8 @@ else
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
</TabPanel>
|
||||
<TabPanel Name="Profile" ResourceKey="Profile">
|
||||
@if (profiles != null)
|
||||
{
|
||||
</TabPanel>
|
||||
<TabPanel Name="Profile" ResourceKey="Profile">
|
||||
<div class="container">
|
||||
<div class="row mb-1 align-items-center">
|
||||
@foreach (Profile profile in profiles)
|
||||
@ -106,8 +89,8 @@ else
|
||||
}
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="@p.Name" HelpText="@p.Description">@p.Title</Label>
|
||||
<div class="col-sm-9">
|
||||
@if (!string.IsNullOrEmpty(p.Options))
|
||||
<div class="col-sm-9">
|
||||
@if (!string.IsNullOrEmpty(p.Options))
|
||||
{
|
||||
<select id="@p.Name" class="form-select" @onchange="@(e => ProfileChanged(e, p.Name))">
|
||||
@foreach (var option in p.Options.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries))
|
||||
@ -125,13 +108,13 @@ else
|
||||
}
|
||||
else
|
||||
{
|
||||
@if (p.IsRequired)
|
||||
@if (p.Rows == 1)
|
||||
{
|
||||
<input id="@p.Name" class="form-control" maxlength="@p.MaxLength" value="@GetProfileValue(p.Name, p.DefaultValue)" required @onchange="@(e => ProfileChanged(e, p.Name))" />
|
||||
<input id="@p.Name" class="form-control" maxlength="@p.MaxLength" value="@GetProfileValue(p.Name, p.DefaultValue)" @onchange="@(e => ProfileChanged(e, p.Name))" />
|
||||
}
|
||||
else
|
||||
{
|
||||
<input id="@p.Name" class="form-control" maxlength="@p.MaxLength" value="@GetProfileValue(p.Name, p.DefaultValue)" @onchange="@(e => ProfileChanged(e, p.Name))" />
|
||||
<textarea id="@p.Name" class="form-control" maxlength="@p.MaxLength" rows="@p.Rows" value="@GetProfileValue(p.Name, p.DefaultValue)" @onchange="@(e => ProfileChanged(e, p.Name))"></textarea>
|
||||
}
|
||||
}
|
||||
</div>
|
||||
@ -139,17 +122,18 @@ else
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
</TabPanel>
|
||||
</TabStrip>
|
||||
</TabPanel>
|
||||
</TabStrip>
|
||||
|
||||
<button type="button" class="btn btn-success" @onclick="SaveUser">@SharedLocalizer["Save"]</button>
|
||||
<NavLink class="btn btn-secondary" href="@NavigateUrl()">@SharedLocalizer["Cancel"]</NavLink>
|
||||
<br />
|
||||
<br />
|
||||
<AuditInfo CreatedBy="@createdby" CreatedOn="@createdon" ModifiedBy="@modifiedby" ModifiedOn="@modifiedon" DeletedBy="@deletedby" DeletedOn="@deletedon"></AuditInfo>
|
||||
<button type="button" class="btn btn-success" @onclick="SaveUser">@SharedLocalizer["Save"]</button>
|
||||
<NavLink class="btn btn-secondary" href="@NavigateUrl()">@SharedLocalizer["Cancel"]</NavLink>
|
||||
<br />
|
||||
<br />
|
||||
<AuditInfo CreatedBy="@createdby" CreatedOn="@createdon" ModifiedBy="@modifiedby" ModifiedOn="@modifiedon" DeletedBy="@deletedby" DeletedOn="@deletedon"></AuditInfo>
|
||||
}
|
||||
|
||||
@code {
|
||||
@code {
|
||||
private bool _initialized = false;
|
||||
private string _passwordrequirements;
|
||||
private int userid;
|
||||
private string username = string.Empty;
|
||||
@ -159,9 +143,6 @@ else
|
||||
private string confirm = string.Empty;
|
||||
private string email = string.Empty;
|
||||
private string displayname = string.Empty;
|
||||
private FileManager filemanager;
|
||||
private int photofileid = -1;
|
||||
private File photo = null;
|
||||
private string isdeleted;
|
||||
private string lastlogin;
|
||||
private string lastipaddress;
|
||||
@ -179,32 +160,23 @@ else
|
||||
|
||||
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Edit;
|
||||
|
||||
protected override async Task OnParametersSetAsync()
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (PageState.QueryString.ContainsKey("id"))
|
||||
_passwordrequirements = await UserService.GetPasswordRequirementsAsync(PageState.Site.SiteId);
|
||||
_togglepassword = SharedLocalizer["ShowPassword"];
|
||||
profiles = await ProfileService.GetProfilesAsync(PageState.Site.SiteId);
|
||||
|
||||
if (PageState.QueryString.ContainsKey("id") && int.TryParse(PageState.QueryString["id"], out int UserId))
|
||||
{
|
||||
_passwordrequirements = await UserService.GetPasswordRequirementsAsync(PageState.Site.SiteId);
|
||||
_togglepassword = SharedLocalizer["ShowPassword"];
|
||||
profiles = await ProfileService.GetProfilesAsync(PageState.Site.SiteId);
|
||||
userid = Int32.Parse(PageState.QueryString["id"]);
|
||||
userid = UserId;
|
||||
var user = await UserService.GetUserAsync(userid, PageState.Site.SiteId);
|
||||
if (user != null)
|
||||
{
|
||||
username = user.Username;
|
||||
email = user.Email;
|
||||
displayname = user.DisplayName;
|
||||
if (user.PhotoFileId != null)
|
||||
{
|
||||
photofileid = user.PhotoFileId.Value;
|
||||
photo = await FileService.GetFileAsync(photofileid);
|
||||
}
|
||||
else
|
||||
{
|
||||
photofileid = -1;
|
||||
photo = null;
|
||||
}
|
||||
isdeleted = user.IsDeleted.ToString();
|
||||
lastlogin = string.Format("{0:MMM dd yyyy HH:mm:ss}", user.LastLoginOn);
|
||||
lastipaddress = user.LastIPAddress;
|
||||
@ -218,6 +190,8 @@ else
|
||||
deletedon = user.DeletedOn;
|
||||
}
|
||||
}
|
||||
|
||||
_initialized = true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@ -240,35 +214,37 @@ else
|
||||
{
|
||||
try
|
||||
{
|
||||
if (username != string.Empty && email != string.Empty && ValidateProfiles())
|
||||
if (username != string.Empty && email != string.Empty)
|
||||
{
|
||||
if (_password == confirm)
|
||||
{
|
||||
var user = await UserService.GetUserAsync(userid, PageState.Site.SiteId);
|
||||
user.SiteId = PageState.Site.SiteId;
|
||||
user.Username = username;
|
||||
user.Password = _password;
|
||||
user.Email = email;
|
||||
user.DisplayName = string.IsNullOrWhiteSpace(displayname) ? username : displayname;
|
||||
user.PhotoFileId = null;
|
||||
user.PhotoFileId = filemanager.GetFileId();
|
||||
if (user.PhotoFileId == -1)
|
||||
if (ValidateProfiles())
|
||||
{
|
||||
var user = await UserService.GetUserAsync(userid, PageState.Site.SiteId);
|
||||
user.SiteId = PageState.Site.SiteId;
|
||||
user.Username = username;
|
||||
user.Password = _password;
|
||||
user.Email = email;
|
||||
user.DisplayName = string.IsNullOrWhiteSpace(displayname) ? username : displayname;
|
||||
user.PhotoFileId = null;
|
||||
}
|
||||
if (user.PhotoFileId == -1)
|
||||
{
|
||||
user.PhotoFileId = null;
|
||||
}
|
||||
|
||||
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(settings, user.UserId);
|
||||
await logger.LogInformation("User Saved {User}", user);
|
||||
NavigationManager.NavigateTo(NavigateUrl());
|
||||
}
|
||||
else
|
||||
{
|
||||
AddModuleMessage(Localizer["Message.Password.Complexity"], MessageType.Error);
|
||||
user = await UserService.UpdateUserAsync(user);
|
||||
if (user != null)
|
||||
{
|
||||
await SettingService.UpdateUserSettingsAsync(settings, user.UserId);
|
||||
await logger.LogInformation("User Saved {User}", user);
|
||||
NavigationManager.NavigateTo(NavigateUrl());
|
||||
}
|
||||
else
|
||||
{
|
||||
AddModuleMessage(Localizer["Message.Password.Complexity"], MessageType.Error);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
@ -290,27 +266,33 @@ else
|
||||
|
||||
private bool ValidateProfiles()
|
||||
{
|
||||
bool valid = true;
|
||||
foreach (Profile profile in profiles)
|
||||
{
|
||||
if (string.IsNullOrEmpty(SettingService.GetSetting(settings, profile.Name, string.Empty)) && !string.IsNullOrEmpty(profile.DefaultValue))
|
||||
var value = GetProfileValue(profile.Name, string.Empty);
|
||||
if (string.IsNullOrEmpty(value) && !string.IsNullOrEmpty(profile.DefaultValue))
|
||||
{
|
||||
settings = SettingService.SetSetting(settings, profile.Name, profile.DefaultValue);
|
||||
}
|
||||
if (!profile.IsPrivate || UserSecurity.IsAuthorized(PageState.User, RoleNames.Admin))
|
||||
{
|
||||
if (valid == true && profile.IsRequired && string.IsNullOrEmpty(SettingService.GetSetting(settings, profile.Name, string.Empty)))
|
||||
if (profile.IsRequired && string.IsNullOrEmpty(value))
|
||||
{
|
||||
valid = false;
|
||||
AddModuleMessage(string.Format(SharedLocalizer["ProfileRequired"], profile.Title), MessageType.Warning);
|
||||
return false;
|
||||
}
|
||||
if (valid == true && !string.IsNullOrEmpty(profile.Validation))
|
||||
if (!string.IsNullOrEmpty(profile.Validation))
|
||||
{
|
||||
Regex regex = new Regex(profile.Validation);
|
||||
valid = regex.Match(SettingService.GetSetting(settings, profile.Name, string.Empty)).Success;
|
||||
bool valid = regex.Match(value).Success;
|
||||
if (!valid)
|
||||
{
|
||||
AddModuleMessage(string.Format(SharedLocalizer["ProfileInvalid"], profile.Title), MessageType.Warning);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return valid;
|
||||
return true;
|
||||
}
|
||||
|
||||
private void ProfileChanged(ChangeEventArgs e, string SettingName)
|
||||
|
@ -17,21 +17,10 @@ else
|
||||
{
|
||||
<TabStrip>
|
||||
<TabPanel Name="Users" Heading="Users" ResourceKey="Users">
|
||||
<div class="container">
|
||||
<div class="row mb-1 align-items-center">
|
||||
<div class="col-sm-4">
|
||||
<ActionLink Action="Add" Text="Add User" Security="SecurityAccessLevel.Edit" ResourceKey="AddUser" />
|
||||
<ActionLink Text="Import Users" Class="btn btn-secondary" Action="Users" Security="SecurityAccessLevel.Admin" ResourceKey="ImportUsers"/>
|
||||
</div>
|
||||
<div class="col-sm-4">
|
||||
<input class="form-control" @bind="@_search" />
|
||||
</div>
|
||||
<div class="col-sm-4">
|
||||
<button type="button" class="btn btn-secondary" @onclick="OnSearch">@SharedLocalizer["Search"]</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<Pager Items="@users" RowClass="align-middle">
|
||||
<ActionLink Action="Add" Text="Add User" Security="SecurityAccessLevel.Edit" ResourceKey="AddUser" />
|
||||
<ActionLink Text="Import Users" Class="btn btn-secondary ms-2" Action="Users" Security="SecurityAccessLevel.Admin" ResourceKey="ImportUsers"/>
|
||||
|
||||
<Pager Items="@users" RowClass="align-middle" SearchProperties="User.Username,User.Email,User.DisplayName">
|
||||
<Header>
|
||||
<th style="width: 1px;"> </th>
|
||||
<th style="width: 1px;"> </th>
|
||||
@ -109,6 +98,21 @@ else
|
||||
<input id="cookiename" class="form-control" @bind="@_cookiename" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="cookieexpiration" HelpText="You can choose to use a custom authentication cookie expiration timespan for each site (e.g. '08:00:00' for 8 hours). The default is 14 days if not specified." ResourceKey="CookieExpiration">Cookie Expiration Timespan:</Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="cookieexpiration" class="form-control" @bind="@_cookieexpiration" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="alwaysremember" HelpText="Enabling this option will set a permanent cookie in conjunction with the Cookie Expiration Timespan, which will automatically sign in users the next time they visit the site. By default the site will use session cookies." ResourceKey="AlwaysRemember">Always Remember User?</Label>
|
||||
<div class="col-sm-9">
|
||||
<select id="alwaysremember" class="form-select" @bind="@_alwaysremember">
|
||||
<option value="true">@SharedLocalizer["Yes"]</option>
|
||||
<option value="false">@SharedLocalizer["No"]</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
</Section>
|
||||
@if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host))
|
||||
@ -191,7 +195,7 @@ else
|
||||
@if (_providertype != "")
|
||||
{
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="providername" HelpText="The external login provider name which will be displayed on the login page" ResourceKey="ProviderName">Provider Name:</Label>
|
||||
<Label Class="col-sm-3" For="providername" HelpText="Specify a friendly name for the external login provider which will be displayed on the Login page" ResourceKey="ProviderName">Provider Name:</Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="providername" class="form-control" @bind="@_providername" />
|
||||
</div>
|
||||
@ -250,7 +254,25 @@ else
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
@if (_providertype == AuthenticationProviderTypes.OpenIDConnect)
|
||||
{
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="authresponsetype" HelpText="Specify the authorization response type. The default is Authorization Code which is considered to be the most secure option based on the latest OAuth specification." ResourceKey="AuthResponseType">Authorization Response Type:</Label>
|
||||
<div class="col-sm-9">
|
||||
<select id="authresponsetype" class="form-select" @bind="@_authresponsetype" required>
|
||||
<option value="code">@Localizer["AuthFlow.Code"]</option>
|
||||
<option value="code id_token">@Localizer["AuthFlow.CodeIdToken"]</option>
|
||||
<option value="code id_token token">@Localizer["AuthFlow.CodeIdTokenToken"]</option>
|
||||
<option value="code token">@Localizer["AuthFlow.CodeToken"]</option>
|
||||
<option value="id_token">@Localizer["AuthFlow.IdToken"]</option>
|
||||
<option value="id_token token">@Localizer["AuthFlow.IdTokenToken"]</option>
|
||||
<option value="token">@Localizer["AuthFlow.Token"]</option>
|
||||
<option value="none">@Localizer["AuthFlow.None"]</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="scopes" HelpText="A list of Scopes to request from the provider (separated by commas). If none are specified, standard Scopes will be used by default." ResourceKey="Scopes">Scopes:</Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="scopes" class="form-control" @bind="@_scopes" />
|
||||
@ -262,7 +284,7 @@ else
|
||||
<input id="parameters" class="form-control" @bind="@_parameters" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="pkce" HelpText="Indicate if the provider supports Proof Key for Code Exchange (PKCE)" ResourceKey="PKCE">Use PKCE?</Label>
|
||||
<div class="col-sm-9">
|
||||
<select id="pkce" class="form-select" @bind="@_pkce" required>
|
||||
@ -277,33 +299,51 @@ else
|
||||
<input id="redirecturl" class="form-control" @bind="@_redirecturl" readonly />
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="identifierclaimtype" HelpText="The name of the unique user identifier claim provided by the provider" ResourceKey="IdentifierClaimType">Identifier Claim:</Label>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="reviewclaims" HelpText="This option will record the full list of Claims returned by the Provider in the Event Log. It should only be used for testing purposes. External Login will be restricted when this option is enabled." ResourceKey="ReviewClaims">Review Claims?</Label>
|
||||
<div class="col-sm-9">
|
||||
<div class="input-group">
|
||||
<select id="reviewclaims" class="form-select" @bind="@_reviewclaims" required>
|
||||
<option value="true">@SharedLocalizer["Yes"]</option>
|
||||
<option value="false">@SharedLocalizer["No"]</option>
|
||||
</select>
|
||||
@if (_reviewclaims == "true")
|
||||
{
|
||||
<a href="@_externalloginurl" target="_blank" class="btn btn-secondary">@SharedLocalizer["Test"]</a>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="identifierclaimtype" HelpText="Specify the type name of the unique user identifier claim provided by the provider. The default value is 'sub'." ResourceKey="IdentifierClaimType">Identifier Claim:</Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="identifierclaimtype" class="form-control" @bind="@_identifierclaimtype" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="emailclaimtype" HelpText="The name of the email address claim provided by the provider" ResourceKey="EmailClaimType">Email Claim:</Label>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="nameclaimtype" HelpText="Optionally specify the type name of the user's name claim provided by the provider. The typical value is 'name'." ResourceKey="NameClaimType">Name Claim:</Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="nameclaimtype" class="form-control" @bind="@_nameclaimtype" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="emailclaimtype" HelpText="Optionally specify the type name of the email address claim provided by the provider. The typical value is 'email'," ResourceKey="EmailClaimType">Email Claim:</Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="emailclaimtype" class="form-control" @bind="@_emailclaimtype" />
|
||||
</div>
|
||||
</div>
|
||||
@if (_providertype == AuthenticationProviderTypes.OpenIDConnect)
|
||||
{
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="roleclaimtype" HelpText="The name of the role claim provided by the provider" ResourceKey="RoleClaimType">Role Claim:</Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="roleclaimtype" class="form-control" @bind="@_roleclaimtype" />
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="roleclaimtype" HelpText="The name of the role claim provided by the provider" ResourceKey="RoleClaimType">Role Claim:</Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="roleclaimtype" class="form-control" @bind="@_roleclaimtype" />
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="profileclaimtypes" HelpText="A comma delimited list of user profile claims provided by the provider, as well as mappings to your user profile definition. For example if the provider includes a 'given_name' claim and you have a 'FirstName' user profile definition you should specify 'given_name:FirstName'." ResourceKey="ProfileClaimTypes">User Profile Claims:</Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="profileclaimtypes" class="form-control" @bind="@_profileclaimtypes" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="profileclaimtypes" HelpText="A comma delimited list of user profile claims provided by the provider, as well as mappings to your user profile definition. For example if the provider includes a 'given_name' claim and you have a 'FirstName' user profile definition you should specify 'given_name:FirstName'." ResourceKey="ProfileClaimTypes">User Profile Claims:</Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="profileclaimtypes" class="form-control" @bind="@_profileclaimtypes" />
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="domainfilter" HelpText="Provide any email domain filter criteria (separated by commas). Domains to exclude should be prefixed with an exclamation point (!). For example 'microsoft.com,!hotmail.com' would include microsoft.com email addresses but not hotmail.com email addresses." ResourceKey="DomainFilter">Domain Filter:</Label>
|
||||
<div class="col-sm-9">
|
||||
@ -319,14 +359,23 @@ else
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="verifyusers" HelpText="Do you want existing users to perform an additional email verification step to link their external login? If you disable this option, existing users will be linked automatically." ResourceKey="VerifyUsers">Verify Existing Users?</Label>
|
||||
<div class="col-sm-9">
|
||||
<select id="verifyusers" class="form-select" @bind="@_verifyusers">
|
||||
<option value="true">@SharedLocalizer["Yes"]</option>
|
||||
<option value="false">@SharedLocalizer["No"]</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
</Section>
|
||||
<Section Name="Token" Heading="Token Settings" ResourceKey="TokenSettings">
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="secret" HelpText="If you want to want to provide API access, please specify a secret which will be used to encrypt your tokens. The secret should be 16 characters or more to ensure optimal security. Please note that if you change this secret, all existing tokens will become invalid and will need to be regenerated." ResourceKey="Secret">Secret:</Label>
|
||||
<Label Class="col-sm-3" For="jwtsecret" HelpText="If you want to want to provide API access, please specify a secret which will be used to encrypt your tokens. The secret should be 16 characters or more to ensure optimal security. Please note that if you change this secret, all existing tokens will become invalid and will need to be regenerated." ResourceKey="Secret">Secret:</Label>
|
||||
<div class="col-sm-9">
|
||||
<div class="input-group">
|
||||
<input type="@_secrettype" id="secret" class="form-control" @bind="@_secret" />
|
||||
<input type="@_secrettype" id="jwtsecret" class="form-control" @bind="@_secret" />
|
||||
<button type="button" class="btn btn-secondary" @onclick="@ToggleSecret">@_togglesecret</button>
|
||||
</div>
|
||||
</div>
|
||||
@ -368,14 +417,14 @@ else
|
||||
}
|
||||
|
||||
@code {
|
||||
private List<UserRole> allusers;
|
||||
private List<UserRole> users;
|
||||
private string _search = "";
|
||||
|
||||
private string _allowregistration;
|
||||
private string _allowsitelogin;
|
||||
private string _twofactor;
|
||||
private string _cookiename;
|
||||
private string _cookieexpiration;
|
||||
private string _alwaysremember;
|
||||
|
||||
private string _minimumlength;
|
||||
private string _uniquecharacters;
|
||||
@ -397,16 +446,21 @@ else
|
||||
private string _clientsecret;
|
||||
private string _clientsecrettype = "password";
|
||||
private string _toggleclientsecret = string.Empty;
|
||||
private string _authresponsetype;
|
||||
private string _scopes;
|
||||
private string _parameters;
|
||||
private string _pkce;
|
||||
private string _redirecturl;
|
||||
private string _reviewclaims;
|
||||
private string _externalloginurl;
|
||||
private string _identifierclaimtype;
|
||||
private string _nameclaimtype;
|
||||
private string _emailclaimtype;
|
||||
private string _roleclaimtype;
|
||||
private string _profileclaimtypes;
|
||||
private string _domainfilter;
|
||||
private string _createusers;
|
||||
private string _verifyusers;
|
||||
|
||||
private string _secret;
|
||||
private string _secrettype = "password";
|
||||
@ -423,7 +477,6 @@ else
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
await LoadUserSettingsAsync();
|
||||
await LoadUsersAsync(true);
|
||||
|
||||
var settings = await SettingService.GetSiteSettingsAsync(PageState.Site.SiteId);
|
||||
@ -434,6 +487,8 @@ else
|
||||
{
|
||||
_twofactor = SettingService.GetSetting(settings, "LoginOptions:TwoFactor", "false");
|
||||
_cookiename = SettingService.GetSetting(settings, "LoginOptions:CookieName", ".AspNetCore.Identity.Application");
|
||||
_cookieexpiration = SettingService.GetSetting(settings, "LoginOptions:CookieExpiration", "");
|
||||
_alwaysremember = SettingService.GetSetting(settings, "LoginOptions:AlwaysRemember", "false");
|
||||
|
||||
_minimumlength = SettingService.GetSetting(settings, "IdentityOptions:Password:RequiredLength", "6");
|
||||
_uniquecharacters = SettingService.GetSetting(settings, "IdentityOptions:Password:RequiredUniqueChars", "1");
|
||||
@ -455,16 +510,21 @@ else
|
||||
_clientid = SettingService.GetSetting(settings, "ExternalLogin:ClientId", "");
|
||||
_clientsecret = SettingService.GetSetting(settings, "ExternalLogin:ClientSecret", "");
|
||||
_toggleclientsecret = SharedLocalizer["ShowPassword"];
|
||||
_authresponsetype = SettingService.GetSetting(settings, "ExternalLogin:AuthResponseType", "code");
|
||||
_scopes = SettingService.GetSetting(settings, "ExternalLogin:Scopes", "");
|
||||
_parameters = SettingService.GetSetting(settings, "ExternalLogin:Parameters", "");
|
||||
_pkce = SettingService.GetSetting(settings, "ExternalLogin:PKCE", "false");
|
||||
_redirecturl = PageState.Uri.Scheme + "://" + PageState.Alias.Name + "/signin-" + _providertype;
|
||||
_reviewclaims = SettingService.GetSetting(settings, "ExternalLogin:ReviewClaims", "false");
|
||||
_externalloginurl = Utilities.TenantUrl(PageState.Alias, "/pages/external");
|
||||
_identifierclaimtype = SettingService.GetSetting(settings, "ExternalLogin:IdentifierClaimType", "sub");
|
||||
_nameclaimtype = SettingService.GetSetting(settings, "ExternalLogin:NameClaimType", "name");
|
||||
_emailclaimtype = SettingService.GetSetting(settings, "ExternalLogin:EmailClaimType", "email");
|
||||
_roleclaimtype = SettingService.GetSetting(settings, "ExternalLogin:RoleClaimType", "");
|
||||
_profileclaimtypes = SettingService.GetSetting(settings, "ExternalLogin:ProfileClaimTypes", "");
|
||||
_domainfilter = SettingService.GetSetting(settings, "ExternalLogin:DomainFilter", "");
|
||||
_createusers = SettingService.GetSetting(settings, "ExternalLogin:CreateUsers", "true");
|
||||
_verifyusers = SettingService.GetSetting(settings, "ExternalLogin:VerifyUsers", "true");
|
||||
|
||||
_secret = SettingService.GetSetting(settings, "JwtOptions:Secret", "");
|
||||
_togglesecret = SharedLocalizer["ShowPassword"];
|
||||
@ -478,32 +538,14 @@ else
|
||||
{
|
||||
if (load)
|
||||
{
|
||||
allusers = await UserRoleService.GetUserRolesAsync(PageState.Site.SiteId, RoleNames.Registered);
|
||||
users = await UserRoleService.GetUserRolesAsync(PageState.Site.SiteId, RoleNames.Registered);
|
||||
if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host))
|
||||
{
|
||||
var hosts = await UserRoleService.GetUserRolesAsync(PageState.Site.SiteId, RoleNames.Host);
|
||||
allusers.AddRange(hosts);
|
||||
allusers = allusers.OrderBy(u => u.User.DisplayName).ToList();
|
||||
users.AddRange(hosts);
|
||||
users = users.OrderBy(u => u.User.DisplayName).ToList();
|
||||
}
|
||||
}
|
||||
|
||||
users = allusers;
|
||||
if (!string.IsNullOrEmpty(_search))
|
||||
{
|
||||
users = users.Where(item =>
|
||||
(
|
||||
item.User.Username.Contains(_search, StringComparison.OrdinalIgnoreCase) ||
|
||||
item.User.Email.Contains(_search, StringComparison.OrdinalIgnoreCase) ||
|
||||
item.User.DisplayName.Contains(_search, StringComparison.OrdinalIgnoreCase)
|
||||
)
|
||||
).ToList();
|
||||
}
|
||||
}
|
||||
|
||||
private async Task OnSearch()
|
||||
{
|
||||
await UpdateUserSettingsAsync();
|
||||
await LoadUsersAsync(false);
|
||||
}
|
||||
|
||||
private async Task DeleteUser(UserRole UserRole)
|
||||
@ -526,21 +568,6 @@ else
|
||||
}
|
||||
}
|
||||
|
||||
private string settingSearch = "AU-search";
|
||||
|
||||
private async Task LoadUserSettingsAsync()
|
||||
{
|
||||
Dictionary<string, string> settings = await SettingService.GetUserSettingsAsync(PageState.User.UserId);
|
||||
_search = SettingService.GetSetting(settings, settingSearch, "");
|
||||
}
|
||||
|
||||
private async Task UpdateUserSettingsAsync()
|
||||
{
|
||||
Dictionary<string, string> settings = await SettingService.GetUserSettingsAsync(PageState.User.UserId);
|
||||
settings = SettingService.SetSetting(settings, settingSearch, _search);
|
||||
await SettingService.UpdateUserSettingsAsync(settings, PageState.User.UserId);
|
||||
}
|
||||
|
||||
private async Task SaveSiteSettings()
|
||||
{
|
||||
try
|
||||
@ -556,6 +583,8 @@ else
|
||||
{
|
||||
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, "IdentityOptions:Password:RequiredLength", _minimumlength, true);
|
||||
settings = SettingService.SetSetting(settings, "IdentityOptions:Password:RequiredUniqueChars", _uniquecharacters, true);
|
||||
@ -576,17 +605,20 @@ else
|
||||
settings = SettingService.SetSetting(settings, "ExternalLogin:UserInfoUrl", _userinfourl, true);
|
||||
settings = SettingService.SetSetting(settings, "ExternalLogin:ClientId", _clientid, true);
|
||||
settings = SettingService.SetSetting(settings, "ExternalLogin:ClientSecret", _clientsecret, true);
|
||||
settings = SettingService.SetSetting(settings, "ExternalLogin:Scopes", _scopes, true);
|
||||
settings = SettingService.SetSetting(settings, "ExternalLogin:AuthResponseType", _authresponsetype, true);
|
||||
settings = SettingService.SetSetting(settings, "ExternalLogin:Scopes", _scopes, true);
|
||||
settings = SettingService.SetSetting(settings, "ExternalLogin:Parameters", _parameters, true);
|
||||
settings = SettingService.SetSetting(settings, "ExternalLogin:PKCE", _pkce, true);
|
||||
settings = SettingService.SetSetting(settings, "ExternalLogin:IdentifierClaimType", _identifierclaimtype, true);
|
||||
settings = SettingService.SetSetting(settings, "ExternalLogin:EmailClaimType", _emailclaimtype, true);
|
||||
settings = SettingService.SetSetting(settings, "ExternalLogin:ReviewClaims", _reviewclaims, true);
|
||||
settings = SettingService.SetSetting(settings, "ExternalLogin:IdentifierClaimType", _identifierclaimtype, true);
|
||||
settings = SettingService.SetSetting(settings, "ExternalLogin:NameClaimType", _nameclaimtype, true);
|
||||
settings = SettingService.SetSetting(settings, "ExternalLogin:EmailClaimType", _emailclaimtype, true);
|
||||
settings = SettingService.SetSetting(settings, "ExternalLogin:RoleClaimType", _roleclaimtype, true);
|
||||
settings = SettingService.SetSetting(settings, "ExternalLogin:ProfileClaimTypes", _profileclaimtypes, true);
|
||||
settings = SettingService.SetSetting(settings, "ExternalLogin:DomainFilter", _domainfilter, true);
|
||||
settings = SettingService.SetSetting(settings, "ExternalLogin:CreateUsers", _createusers, true);
|
||||
settings = SettingService.SetSetting(settings, "ExternalLogin:VerifyUsers", _verifyusers, true);
|
||||
|
||||
if (!string.IsNullOrEmpty(_secret) && _secret.Length < 16) _secret = (_secret + "????????????????").Substring(0, 16);
|
||||
settings = SettingService.SetSetting(settings, "JwtOptions:Secret", _secret, true);
|
||||
settings = SettingService.SetSetting(settings, "JwtOptions:Issuer", _issuer, true);
|
||||
settings = SettingService.SetSetting(settings, "JwtOptions:Audience", _audience, true);
|
||||
|
@ -78,7 +78,7 @@ else
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="retention" HelpText="Number of days of visitor activity to retain" ResourceKey="Retention">Retention (Days): </Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="retention" class="form-control" @bind="@_retention" />
|
||||
<input id="retention" class="form-control" type="number" min="0" step="1" @bind="@_retention" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
@ -104,7 +104,7 @@ else
|
||||
private List<Visitor> _visitors;
|
||||
private string _tracking;
|
||||
private string _filter = "";
|
||||
private string _retention = "";
|
||||
private int _retention = 30;
|
||||
private string _correlation = "true";
|
||||
|
||||
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Admin;
|
||||
@ -129,7 +129,7 @@ else
|
||||
_tracking = PageState.Site.VisitorTracking.ToString();
|
||||
var settings = await SettingService.GetSiteSettingsAsync(PageState.Site.SiteId);
|
||||
_filter = SettingService.GetSetting(settings, "VisitorFilter", Constants.DefaultVisitorFilter);
|
||||
_retention = SettingService.GetSetting(settings, "VisitorRetention", "30");
|
||||
_retention = int.Parse(SettingService.GetSetting(settings, "VisitorRetention", "30"));
|
||||
_correlation = SettingService.GetSetting(settings, "VisitorCorrelation", "true");
|
||||
}
|
||||
|
||||
@ -180,7 +180,7 @@ else
|
||||
|
||||
var settings = await SettingService.GetSiteSettingsAsync(PageState.Site.SiteId);
|
||||
settings = SettingService.SetSetting(settings, "VisitorFilter", _filter, true);
|
||||
settings = SettingService.SetSetting(settings, "VisitorRetention", _retention, true);
|
||||
settings = SettingService.SetSetting(settings, "VisitorRetention", _retention.ToString(), true);
|
||||
settings = SettingService.SetSetting(settings, "VisitorCorrelation", _correlation, true);
|
||||
await SettingService.UpdateSiteSettingsAsync(settings, PageState.Site.SiteId);
|
||||
|
||||
|
@ -3,6 +3,7 @@
|
||||
@inherits ModuleControlBase
|
||||
@inject IFolderService FolderService
|
||||
@inject IFileService FileService
|
||||
@inject ISettingService SettingService
|
||||
@inject IStringLocalizer<FileManager> Localizer
|
||||
@inject IStringLocalizer<SharedResources> SharedLocalizer
|
||||
|
||||
@ -40,10 +41,17 @@
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
else
|
||||
{
|
||||
if (FileId != -1 && _file != null && !UploadMultiple)
|
||||
{
|
||||
<input class="form-control" @bind="@_file.Name" disabled />
|
||||
}
|
||||
}
|
||||
@if (ShowUpload && _haseditpermission)
|
||||
{
|
||||
<div class="row">
|
||||
<div class="col mt-2">
|
||||
<div class="row mt-2">
|
||||
<div class="col">
|
||||
@if (UploadMultiple)
|
||||
{
|
||||
<input type="file" id="@_fileinputid" name="file" accept="@_filter" multiple />
|
||||
@ -53,9 +61,9 @@
|
||||
<input type="file" id="@_fileinputid" name="file" accept="@_filter" />
|
||||
}
|
||||
</div>
|
||||
<div class="col mt-2 text-end">
|
||||
<div class="col-auto">
|
||||
<button type="button" class="btn btn-success" @onclick="UploadFiles">@SharedLocalizer["Upload"]</button>
|
||||
@if (GetFileId() != -1)
|
||||
@if (FileId != -1 && !UploadMultiple)
|
||||
{
|
||||
<button type="button" class="btn btn-danger mx-1" @onclick="DeleteFile">@SharedLocalizer["Delete"]</button>
|
||||
}
|
||||
@ -76,6 +84,14 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
@if (!string.IsNullOrEmpty(_message))
|
||||
{
|
||||
<div class="row mt-1">
|
||||
<div class="col">
|
||||
<ModuleMessage Message="@_message" Type="@_messagetype" />
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
@if (_image != string.Empty)
|
||||
@ -85,14 +101,6 @@
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
@if (!string.IsNullOrEmpty(_message))
|
||||
{
|
||||
<div class="row mt-1">
|
||||
<div class="col">
|
||||
<ModuleMessage Message="@_message" Type="@_messagetype" />
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
|
||||
@ -336,6 +344,7 @@
|
||||
_message = string.Empty;
|
||||
var interop = new Interop(JSRuntime);
|
||||
var uploads = await interop.GetFiles(_fileinputid);
|
||||
|
||||
if (uploads.Length > 0)
|
||||
{
|
||||
string restricted = "";
|
||||
@ -343,7 +352,7 @@
|
||||
{
|
||||
var filename = upload.Split(':')[0];
|
||||
var extension = (filename.LastIndexOf(".") != -1) ? filename.Substring(filename.LastIndexOf(".") + 1) : "";
|
||||
if (!Constants.UploadableFiles.Split(',').Contains(extension.ToLower()))
|
||||
if (!PageState.Site.UploadableFiles.Split(',').Contains(extension.ToLower()))
|
||||
{
|
||||
restricted += (restricted == "" ? "" : ",") + extension;
|
||||
}
|
||||
@ -370,14 +379,19 @@
|
||||
{
|
||||
success = false;
|
||||
var filename = uploads[upload].Split(':')[0];
|
||||
var size = Int64.Parse(uploads[upload].Split(':')[1]);
|
||||
var maxattempts = (int)Math.Ceiling(size / 500000.0) + 1; // 30 MB takes 1 minute at 5 Mbps
|
||||
|
||||
var size = Int64.Parse(uploads[upload].Split(':')[1]); // bytes
|
||||
var megabits = (size / 1048576.0) * 8; // binary conversion
|
||||
var uploadspeed = 2; // 2 Mbps (3G ranges from 300Kbps to 3Mbps)
|
||||
var uploadtime = (megabits / uploadspeed); // seconds
|
||||
var maxattempts = 5; // polling (minimum timeout duration will be 5 seconds)
|
||||
var sleep = (int)Math.Ceiling(uploadtime / maxattempts) * 1000; // milliseconds
|
||||
|
||||
int attempts = 0;
|
||||
while (attempts < maxattempts && !success)
|
||||
{
|
||||
attempts += 1;
|
||||
Thread.Sleep(1000);
|
||||
Thread.Sleep(sleep);
|
||||
|
||||
if (Folder == Constants.PackagesFolder)
|
||||
{
|
||||
|
@ -1,10 +1,20 @@
|
||||
@namespace Oqtane.Modules.Controls
|
||||
@inherits ModuleControlBase
|
||||
@inject IStringLocalizerFactory LocalizerFactory
|
||||
@inject IStringLocalizer<SharedResources> SharedLocalizer
|
||||
@typeparam TableItem
|
||||
|
||||
@if (ItemList != null)
|
||||
{
|
||||
@if (!string.IsNullOrEmpty(SearchProperties))
|
||||
{
|
||||
<div class="input-group my-3">
|
||||
<input id="search" class="form-control" placeholder=@string.Format(Localizer["SearchPlaceholder"], FormatSearchProperties()) @bind="@_search" />
|
||||
<button type="button" class="btn btn-primary" @onclick="Search">@SharedLocalizer["Search"]</button>
|
||||
<button type="button" class="btn btn-secondary" @onclick="Reset">@SharedLocalizer["Reset"]</button>
|
||||
</div>
|
||||
}
|
||||
|
||||
@if ((Toolbar == "Top" || Toolbar == "Both") && _pages > 0 && Items.Count() > _maxItems)
|
||||
{
|
||||
<ul class="pagination justify-content-center my-2">
|
||||
@ -175,6 +185,9 @@
|
||||
private int _startPage = 0;
|
||||
private int _endPage = 0;
|
||||
private int _columns = 0;
|
||||
private string _search = "";
|
||||
|
||||
private IEnumerable<TableItem> AllItems;
|
||||
|
||||
[Parameter]
|
||||
public string Format { get; set; } // Table or Grid
|
||||
@ -221,6 +234,9 @@
|
||||
[Parameter]
|
||||
public Action<int> OnPageChange { get; set; } // a method to be executed in the calling component when the page changes
|
||||
|
||||
[Parameter]
|
||||
public string SearchProperties { get; set; } // comma delimited list of property names to include in search
|
||||
|
||||
private IEnumerable<TableItem> ItemList { get; set; }
|
||||
|
||||
protected override void OnInitialized()
|
||||
@ -276,6 +292,15 @@
|
||||
}
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(SearchProperties))
|
||||
{
|
||||
AllItems = Items; // only used in search
|
||||
if (!string.IsNullOrEmpty(_search))
|
||||
{
|
||||
Search();
|
||||
}
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(PageSize))
|
||||
{
|
||||
_maxItems = int.Parse(PageSize);
|
||||
@ -323,10 +348,10 @@
|
||||
{
|
||||
_endPage = _pages;
|
||||
}
|
||||
ItemList = Items.Skip((_page - 1) * _maxItems).Take(_maxItems);
|
||||
StateHasChanged();
|
||||
OnPageChange?.Invoke(_page);
|
||||
}
|
||||
ItemList = Items.Skip((_page - 1) * _maxItems).Take(_maxItems);
|
||||
StateHasChanged();
|
||||
OnPageChange?.Invoke(_page);
|
||||
}
|
||||
|
||||
public void UpdateList(int page)
|
||||
{
|
||||
@ -369,4 +394,75 @@
|
||||
|
||||
UpdateList(_page);
|
||||
}
|
||||
|
||||
public void Search()
|
||||
{
|
||||
if (!string.IsNullOrEmpty(_search))
|
||||
{
|
||||
Items = AllItems.Where(item =>
|
||||
{
|
||||
var values = SearchProperties.Split(',')
|
||||
.Select(itemType => GetPropertyValue(item, itemType))
|
||||
.Where(value => value != null)
|
||||
.Select(value => value.ToString().ToLower());
|
||||
|
||||
return values.Any(value => value.Contains(_search.ToLower()));
|
||||
}).ToList();
|
||||
}
|
||||
else
|
||||
{
|
||||
Items = AllItems;
|
||||
}
|
||||
_pages = (int)Math.Ceiling(Items.Count() / (decimal)_maxItems);
|
||||
UpdateList(1);
|
||||
}
|
||||
|
||||
private object GetPropertyValue(object obj, string propertyName)
|
||||
{
|
||||
var index = propertyName.IndexOf(".");
|
||||
if (index != -1)
|
||||
{
|
||||
var propertyInfo = obj.GetType().GetProperty(propertyName.Substring(0, index));
|
||||
if (propertyInfo != null)
|
||||
{
|
||||
return GetPropertyValue(propertyInfo.GetValue(obj), propertyName.Substring(index + 1));
|
||||
}
|
||||
return null;
|
||||
}
|
||||
else
|
||||
{
|
||||
var propertyInfo = obj.GetType().GetProperty(propertyName);
|
||||
if (propertyInfo != null)
|
||||
{
|
||||
return propertyInfo.GetValue(obj);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
_search = "";
|
||||
Items = AllItems;
|
||||
_pages = (int)Math.Ceiling(Items.Count() / (decimal)_maxItems);
|
||||
UpdateList(1);
|
||||
}
|
||||
|
||||
private string FormatSearchProperties()
|
||||
{
|
||||
var properties = new List<string>();
|
||||
foreach (var property in SearchProperties.Split(',', StringSplitOptions.RemoveEmptyEntries))
|
||||
{
|
||||
var index = property.LastIndexOf(".");
|
||||
if (index != -1)
|
||||
{
|
||||
properties.Add(property.Substring(index + 1));
|
||||
}
|
||||
else
|
||||
{
|
||||
properties.Add(property);
|
||||
}
|
||||
}
|
||||
return string.Join(",", properties);
|
||||
}
|
||||
}
|
||||
|
@ -1,79 +1,86 @@
|
||||
@using System.Text.RegularExpressions
|
||||
@namespace Oqtane.Modules.Controls
|
||||
@inherits ModuleControlBase
|
||||
@inject ISettingService SettingService
|
||||
@inject IStringLocalizer<RichTextEditor> Localizer
|
||||
|
||||
<div class="row" style="margin-bottom: 50px;">
|
||||
<div class="col">
|
||||
<TabStrip>
|
||||
<TabPanel Name="Rich" Heading="Rich Text Editor" ResourceKey="RichTextEditor">
|
||||
@if (_richfilemanager)
|
||||
{
|
||||
<FileManager @ref="_fileManager" Filter="@Constants.ImageFiles" />
|
||||
<ModuleMessage Message="@_message" Type="MessageType.Warning"></ModuleMessage>
|
||||
<br />
|
||||
}
|
||||
<div class="d-flex justify-content-center mb-2">
|
||||
@if (AllowRawHtml)
|
||||
{
|
||||
<button type="button" class="btn btn-secondary" @onclick="RefreshRichText">@Localizer["SynchronizeContent"]</button>@((MarkupString)" ")
|
||||
}
|
||||
@if (AllowFileManagement)
|
||||
{
|
||||
<button type="button" class="btn btn-primary" @onclick="InsertRichImage">@Localizer["InsertImage"]</button>
|
||||
}
|
||||
@if (_richfilemanager)
|
||||
{
|
||||
@((MarkupString)" ")
|
||||
<button type="button" class="btn btn-secondary" @onclick="CloseRichFileManager">@Localizer["Close"]</button>
|
||||
}
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<div @ref="@_toolBar">
|
||||
@if (ToolbarContent != null)
|
||||
{
|
||||
@ToolbarContent
|
||||
}
|
||||
else
|
||||
{
|
||||
<select class="ql-header">
|
||||
<option selected=""></option>
|
||||
<option value="1"></option>
|
||||
<option value="2"></option>
|
||||
<option value="3"></option>
|
||||
<option value="4"></option>
|
||||
<option value="5"></option>
|
||||
</select>
|
||||
<span class="ql-formats">
|
||||
<button class="ql-bold"></button>
|
||||
<button class="ql-italic"></button>
|
||||
<button class="ql-underline"></button>
|
||||
<button class="ql-strike"></button>
|
||||
</span>
|
||||
<span class="ql-formats">
|
||||
<select class="ql-color"></select>
|
||||
<select class="ql-background"></select>
|
||||
</span>
|
||||
<span class="ql-formats">
|
||||
<button class="ql-list" value="ordered"></button>
|
||||
<button class="ql-list" value="bullet"></button>
|
||||
</span>
|
||||
<span class="ql-formats">
|
||||
<button class="ql-link"></button>
|
||||
</span>
|
||||
}
|
||||
</div>
|
||||
<div @ref="@_editorElement">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</TabPanel>
|
||||
@if (AllowRichText)
|
||||
{
|
||||
<TabPanel Name="Rich" Heading="Rich Text Editor" ResourceKey="RichTextEditor">
|
||||
@if (_richfilemanager)
|
||||
{
|
||||
<FileManager @ref="_fileManager" Filter="@PageState.Site.ImageFiles" />
|
||||
<ModuleMessage Message="@_message" Type="MessageType.Warning"></ModuleMessage>
|
||||
<br />
|
||||
}
|
||||
<div class="d-flex justify-content-center mb-2">
|
||||
@if (AllowRawHtml)
|
||||
{
|
||||
<button type="button" class="btn btn-secondary" @onclick="RefreshRichText">@Localizer["SynchronizeContent"]</button>
|
||||
|
||||
@((MarkupString)" ")
|
||||
}
|
||||
@if (AllowFileManagement)
|
||||
{
|
||||
<button type="button" class="btn btn-primary" @onclick="InsertRichImage">@Localizer["InsertImage"]</button>
|
||||
}
|
||||
@if (_richfilemanager)
|
||||
{
|
||||
@((MarkupString)" ")
|
||||
<button type="button" class="btn btn-secondary" @onclick="CloseRichFileManager">@Localizer["Close"]</button>
|
||||
}
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<div @ref="@_toolBar">
|
||||
@if (ToolbarContent != null)
|
||||
{
|
||||
@ToolbarContent
|
||||
}
|
||||
else
|
||||
{
|
||||
<select class="ql-header">
|
||||
<option selected=""></option>
|
||||
<option value="1"></option>
|
||||
<option value="2"></option>
|
||||
<option value="3"></option>
|
||||
<option value="4"></option>
|
||||
<option value="5"></option>
|
||||
</select>
|
||||
<span class="ql-formats">
|
||||
<button class="ql-bold"></button>
|
||||
<button class="ql-italic"></button>
|
||||
<button class="ql-underline"></button>
|
||||
<button class="ql-strike"></button>
|
||||
</span>
|
||||
<span class="ql-formats">
|
||||
<select class="ql-color"></select>
|
||||
<select class="ql-background"></select>
|
||||
</span>
|
||||
<span class="ql-formats">
|
||||
<button class="ql-list" value="ordered"></button>
|
||||
<button class="ql-list" value="bullet"></button>
|
||||
</span>
|
||||
<span class="ql-formats">
|
||||
<button class="ql-link"></button>
|
||||
</span>
|
||||
}
|
||||
</div>
|
||||
<div @ref="@_editorElement">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</TabPanel>
|
||||
}
|
||||
@if (AllowRawHtml)
|
||||
{
|
||||
<TabPanel Name="Raw" Heading="Raw HTML Editor" ResourceKey="HtmlEditor">
|
||||
@if (_rawfilemanager)
|
||||
{
|
||||
<FileManager @ref="_fileManager" Filter="@Constants.ImageFiles" />
|
||||
<FileManager @ref="_fileManager" Filter="@PageState.Site.ImageFiles" />
|
||||
<ModuleMessage Message="@_message" Type="MessageType.Warning"></ModuleMessage>
|
||||
<br />
|
||||
}
|
||||
@ -104,119 +111,130 @@
|
||||
</div>
|
||||
|
||||
@code {
|
||||
private ElementReference _editorElement;
|
||||
private ElementReference _toolBar;
|
||||
private bool _richfilemanager = false;
|
||||
private FileManager _fileManager;
|
||||
private string _richhtml = string.Empty;
|
||||
private string _originalrichhtml = string.Empty;
|
||||
private bool _rawfilemanager = false;
|
||||
private string _rawhtml = string.Empty;
|
||||
private string _originalrawhtml = string.Empty;
|
||||
private string _message = string.Empty;
|
||||
private ElementReference _editorElement;
|
||||
private ElementReference _toolBar;
|
||||
private bool _richfilemanager = false;
|
||||
private FileManager _fileManager;
|
||||
private string _richhtml = string.Empty;
|
||||
private string _originalrichhtml = string.Empty;
|
||||
private bool _rawfilemanager = false;
|
||||
private string _rawhtml = string.Empty;
|
||||
private string _originalrawhtml = string.Empty;
|
||||
private string _message = string.Empty;
|
||||
|
||||
[Parameter]
|
||||
public string Content { get; set; }
|
||||
[Parameter]
|
||||
public string Content { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public bool ReadOnly { get; set; } = false;
|
||||
[Parameter]
|
||||
public bool ReadOnly { get; set; } = false;
|
||||
|
||||
[Parameter]
|
||||
public string Placeholder { get; set; } = "Enter Your Content...";
|
||||
[Parameter]
|
||||
public string Placeholder { get; set; } = "Enter Your Content...";
|
||||
|
||||
[Parameter]
|
||||
public bool AllowFileManagement { get; set; } = true;
|
||||
[Parameter]
|
||||
public bool AllowFileManagement { get; set; } = true;
|
||||
|
||||
[Parameter]
|
||||
public bool AllowRawHtml { get; set; } = true;
|
||||
|
||||
// parameters only applicable to rich text editor
|
||||
[Parameter]
|
||||
public RenderFragment ToolbarContent { get; set; }
|
||||
[Parameter]
|
||||
public bool AllowRichText { get; set; } = true;
|
||||
|
||||
[Parameter]
|
||||
public string Theme { get; set; } = "snow";
|
||||
[Parameter]
|
||||
public bool AllowRawHtml { get; set; } = true;
|
||||
|
||||
[Parameter]
|
||||
public string DebugLevel { get; set; } = "info";
|
||||
// parameters only applicable to rich text editor
|
||||
[Parameter]
|
||||
public RenderFragment ToolbarContent { get; set; }
|
||||
|
||||
public override List<Resource> Resources => new List<Resource>()
|
||||
[Parameter]
|
||||
public string Theme { get; set; } = "snow";
|
||||
|
||||
[Parameter]
|
||||
public string DebugLevel { get; set; } = "info";
|
||||
|
||||
public override List<Resource> Resources => new List<Resource>()
|
||||
{
|
||||
new Resource { ResourceType = ResourceType.Script, Bundle = "Quill", Url = "js/quill.min.js" },
|
||||
new Resource { ResourceType = ResourceType.Script, Bundle = "Quill", Url = "js/quill-blot-formatter.min.js" },
|
||||
new Resource { ResourceType = ResourceType.Script, Bundle = "Quill", Url = "js/quill-interop.js" }
|
||||
};
|
||||
|
||||
protected override void OnParametersSet()
|
||||
{
|
||||
_richhtml = Content;
|
||||
_rawhtml = Content;
|
||||
_originalrawhtml = _rawhtml; // preserve for comparison later
|
||||
}
|
||||
protected override void OnParametersSet()
|
||||
{
|
||||
_richhtml = Content;
|
||||
_rawhtml = Content;
|
||||
_originalrawhtml = _rawhtml; // preserve for comparison later
|
||||
}
|
||||
|
||||
protected override async Task OnAfterRenderAsync(bool firstRender)
|
||||
{
|
||||
await base.OnAfterRenderAsync(firstRender);
|
||||
protected override async Task OnAfterRenderAsync(bool firstRender)
|
||||
{
|
||||
await base.OnAfterRenderAsync(firstRender);
|
||||
|
||||
var interop = new RichTextEditorInterop(JSRuntime);
|
||||
var interop = new RichTextEditorInterop(JSRuntime);
|
||||
|
||||
if (firstRender)
|
||||
{
|
||||
await interop.CreateEditor(
|
||||
_editorElement,
|
||||
_toolBar,
|
||||
ReadOnly,
|
||||
Placeholder,
|
||||
Theme,
|
||||
DebugLevel);
|
||||
if (firstRender)
|
||||
{
|
||||
await interop.CreateEditor(
|
||||
_editorElement,
|
||||
_toolBar,
|
||||
ReadOnly,
|
||||
Placeholder,
|
||||
Theme,
|
||||
DebugLevel);
|
||||
|
||||
await interop.LoadEditorContent(_editorElement, _richhtml);
|
||||
await interop.LoadEditorContent(_editorElement, _richhtml);
|
||||
|
||||
// preserve a copy of the rich text content (Quill sanitizes content so we need to retrieve it from the editor)
|
||||
_originalrichhtml = await interop.GetHtml(_editorElement);
|
||||
}
|
||||
}
|
||||
if (AllowRichText)
|
||||
{
|
||||
// preserve a copy of the rich text content (Quill sanitizes content so we need to retrieve it from the editor)
|
||||
_originalrichhtml = await interop.GetHtml(_editorElement);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void CloseRichFileManager()
|
||||
{
|
||||
_richfilemanager = false;
|
||||
_message = string.Empty;
|
||||
StateHasChanged();
|
||||
}
|
||||
public void CloseRichFileManager()
|
||||
{
|
||||
_richfilemanager = false;
|
||||
_message = string.Empty;
|
||||
StateHasChanged();
|
||||
}
|
||||
|
||||
public void CloseRawFileManager()
|
||||
{
|
||||
_rawfilemanager = false;
|
||||
_message = string.Empty;
|
||||
StateHasChanged();
|
||||
}
|
||||
public void CloseRawFileManager()
|
||||
{
|
||||
_rawfilemanager = false;
|
||||
_message = string.Empty;
|
||||
StateHasChanged();
|
||||
}
|
||||
|
||||
public void RefreshRichText()
|
||||
{
|
||||
_richhtml = _rawhtml;
|
||||
StateHasChanged();
|
||||
}
|
||||
public void RefreshRichText()
|
||||
{
|
||||
_richhtml = _rawhtml;
|
||||
StateHasChanged();
|
||||
}
|
||||
|
||||
public async Task RefreshRawHtml()
|
||||
{
|
||||
var interop = new RichTextEditorInterop(JSRuntime);
|
||||
_rawhtml = await interop.GetHtml(_editorElement);
|
||||
StateHasChanged();
|
||||
}
|
||||
public async Task RefreshRawHtml()
|
||||
{
|
||||
var interop = new RichTextEditorInterop(JSRuntime);
|
||||
_rawhtml = await interop.GetHtml(_editorElement);
|
||||
StateHasChanged();
|
||||
}
|
||||
|
||||
public async Task<string> GetHtml()
|
||||
{
|
||||
// evaluate raw html content as first priority
|
||||
if (_rawhtml != _originalrawhtml)
|
||||
{
|
||||
return _rawhtml;
|
||||
}
|
||||
else
|
||||
{
|
||||
// return rich text content if it has changed
|
||||
var interop = new RichTextEditorInterop(JSRuntime);
|
||||
var richhtml = await interop.GetHtml(_editorElement);
|
||||
if (richhtml != _originalrichhtml)
|
||||
public async Task<string> GetHtml()
|
||||
{
|
||||
// evaluate raw html content as first priority
|
||||
if (_rawhtml != _originalrawhtml)
|
||||
{
|
||||
return _rawhtml;
|
||||
}
|
||||
else
|
||||
{
|
||||
var richhtml = "";
|
||||
if (AllowRichText)
|
||||
{
|
||||
// return rich text content if it has changed
|
||||
var interop = new RichTextEditorInterop(JSRuntime);
|
||||
richhtml = await interop.GetHtml(_editorElement);
|
||||
}
|
||||
// rich text value will only be blank if AllowRichText is disabled or the JS Interop method failed
|
||||
if (richhtml != _originalrichhtml && !string.IsNullOrEmpty(richhtml) && !string.IsNullOrEmpty(_originalrichhtml))
|
||||
{
|
||||
return richhtml;
|
||||
}
|
||||
|
@ -1,27 +1,30 @@
|
||||
@namespace Oqtane.Modules.Controls
|
||||
@inherits LocalizableComponent
|
||||
|
||||
<div class="d-flex mt-2">
|
||||
<div>
|
||||
<a data-bs-toggle="collapse" class="app-link-unstyled" href="#@Name" aria-expanded="@_expanded" aria-controls="@Name" @onclick:preventDefault="true">
|
||||
<h5>@_heading</h5>
|
||||
</a>
|
||||
@if (IsVisible)
|
||||
{
|
||||
<div class="d-flex mt-2">
|
||||
<div>
|
||||
<a data-bs-toggle="collapse" class="app-link-unstyled" href="#@Name" aria-expanded="@_expanded" aria-controls="@Name" @onclick:preventDefault="true">
|
||||
<h5>@_heading</h5>
|
||||
</a>
|
||||
</div>
|
||||
<div class="ms-auto">
|
||||
<a data-bs-toggle="collapse" class="app-link-unstyled float-right" href="#@Name" aria-expanded="@_expanded" aria-controls="@Name" @onclick:preventDefault="true">
|
||||
<i class="oi oi-chevron-bottom"></i>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="ms-auto">
|
||||
<a data-bs-toggle="collapse" class="app-link-unstyled float-right" href="#@Name" aria-expanded="@_expanded" aria-controls="@Name" @onclick:preventDefault="true">
|
||||
<i class="oi oi-chevron-bottom"></i>
|
||||
</a>
|
||||
<div class="d-flex">
|
||||
<hr class="app-rule" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="d-flex">
|
||||
<hr class="app-rule" />
|
||||
</div>
|
||||
<div class="collapse @_show" id="@Name">
|
||||
@if (ChildContent != null)
|
||||
{
|
||||
@ChildContent
|
||||
}
|
||||
</div>
|
||||
<div class="collapse @_show" id="@Name">
|
||||
@if (ChildContent != null)
|
||||
{
|
||||
@ChildContent
|
||||
}
|
||||
</div>
|
||||
}
|
||||
|
||||
@code {
|
||||
private string _heading = string.Empty;
|
||||
@ -40,6 +43,9 @@
|
||||
[Parameter]
|
||||
public string Expanded { get; set; } // optional - will default to false if not provided
|
||||
|
||||
[Parameter]
|
||||
public bool IsVisible { get; set; } = true;
|
||||
|
||||
protected override void OnParametersSet()
|
||||
{
|
||||
base.OnParametersSet(); // must be included to call method in LocalizableComponent
|
||||
|
@ -261,7 +261,12 @@ namespace Oqtane.Modules
|
||||
// UI methods
|
||||
public void AddModuleMessage(string message, MessageType type)
|
||||
{
|
||||
ModuleInstance.AddModuleMessage(message, type);
|
||||
AddModuleMessage(message, type, "top");
|
||||
}
|
||||
|
||||
public void AddModuleMessage(string message, MessageType type, string position)
|
||||
{
|
||||
ModuleInstance.AddModuleMessage(message, type, position);
|
||||
}
|
||||
|
||||
public void ClearModuleMessage()
|
||||
|
@ -1,10 +1,10 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk.BlazorWebAssembly">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<OutputType>Exe</OutputType>
|
||||
<Configurations>Debug;Release</Configurations>
|
||||
<Version>4.0.5</Version>
|
||||
<Version>5.0.1</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/v4.0.5</PackageReleaseNotes>
|
||||
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v5.0.1</PackageReleaseNotes>
|
||||
<RepositoryUrl>https://github.com/oqtane/oqtane.framework</RepositoryUrl>
|
||||
<RepositoryType>Git</RepositoryType>
|
||||
<RootNamespace>Oqtane</RootNamespace>
|
||||
@ -21,12 +21,12 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="7.0.5" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" Version="7.0.5" PrivateAssets="all" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Authentication" Version="7.0.5" />
|
||||
<PackageReference Include="Microsoft.Extensions.Localization" Version="7.0.5" />
|
||||
<PackageReference Include="Microsoft.Extensions.Http" Version="7.0.0" />
|
||||
<PackageReference Include="System.Net.Http.Json" Version="7.0.1" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="8.0.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" Version="8.0.0" PrivateAssets="all" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Authentication" Version="8.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Localization" Version="8.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Http" Version="8.0.0" />
|
||||
<PackageReference Include="System.Net.Http.Json" Version="8.0.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Localization" Version="2.2.0" />
|
||||
</ItemGroup>
|
||||
|
||||
|
153
Oqtane.Client/Resources/Modules/Admin/Languages/Edit.resx
Normal file
153
Oqtane.Client/Resources/Modules/Admin/Languages/Edit.resx
Normal file
@ -0,0 +1,153 @@
|
||||
<?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="Error.Language.Edit" xml:space="preserve">
|
||||
<value>Error Updating Language</value>
|
||||
</data>
|
||||
<data name="Name.HelpText" xml:space="preserve">
|
||||
<value>Name Of The Langauage</value>
|
||||
</data>
|
||||
<data name="IsDefault.HelpText" xml:space="preserve">
|
||||
<value>Indicates Whether Or Not This Language Is The Default For The Site</value>
|
||||
</data>
|
||||
<data name="Name.Text" xml:space="preserve">
|
||||
<value>Name:</value>
|
||||
</data>
|
||||
<data name="IsDefault.Text" xml:space="preserve">
|
||||
<value>Default?</value>
|
||||
</data>
|
||||
<data name="Success.Language.Download" xml:space="preserve">
|
||||
<value>Translation Package Saved Successfully. You Must <a href={0}>Restart</a> To Complete The Installation.</value>
|
||||
</data>
|
||||
<data name="LanguageUpload.HelpText" xml:space="preserve">
|
||||
<value>Upload one or more translation packages.</value>
|
||||
</data>
|
||||
<data name="LanguageUpload.Text" xml:space="preserve">
|
||||
<value>Translation</value>
|
||||
</data>
|
||||
<data name="Manage.Heading" xml:space="preserve">
|
||||
<value>Manage</value>
|
||||
</data>
|
||||
<data name="Upload.Heading" xml:space="preserve">
|
||||
<value>Upload</value>
|
||||
</data>
|
||||
<data name="Error.Language.Load" xml:space="preserve">
|
||||
<value>Error Loading Language</value>
|
||||
</data>
|
||||
</root>
|
@ -204,8 +204,8 @@
|
||||
<data name="ExternalLoginStatus.DuplicateEmail" xml:space="preserve">
|
||||
<value>Multiple User Accounts Already Exist With The Email Address Of Your External Login. Please Contact Your Administrator For Further Instructions.</value>
|
||||
</data>
|
||||
<data name="ExternalLoginStatus.InvalidEmail" xml:space="preserve">
|
||||
<value>The External Login Provider Did Not Provide A Valid Email Address For Your Account. Please Contact Your Administrator For Further Instructions.</value>
|
||||
<data name="ExternalLoginStatus.MissingClaims" xml:space="preserve">
|
||||
<value>The External Login Provider Did Not Provide All Of The Required Information. Please Contact Your Administrator For Further Instructions.</value>
|
||||
</data>
|
||||
<data name="ExternalLoginStatus.ProviderKeyMismatch" xml:space="preserve">
|
||||
<value>An Error Occurred Verifying Your External Login. Please Contact Your Administrator For Further Instructions.</value>
|
||||
@ -225,4 +225,7 @@
|
||||
<data name="ExternalLoginStatus.RemoteFailure" xml:space="preserve">
|
||||
<value>Your External Login Failed. Please Contact Your Administrator For Further Instructions.</value>
|
||||
</data>
|
||||
<data name="ExternalLoginStatus.ReviewClaims" xml:space="preserve">
|
||||
<value>The Review Claims Option Was Enabled In External Login Settings. Please Visit The Event Log To View The Claims Returned By The Provider.</value>
|
||||
</data>
|
||||
</root>
|
@ -240,4 +240,10 @@
|
||||
<data name="Validate" xml:space="preserve">
|
||||
<value>Validate</value>
|
||||
</data>
|
||||
<data name="Browse" xml:space="preserve">
|
||||
<value>Browse</value>
|
||||
</data>
|
||||
<data name="Pages.Heading" xml:space="preserve">
|
||||
<value>Pages</value>
|
||||
</data>
|
||||
</root>
|
@ -127,7 +127,7 @@
|
||||
<value>Error Downloading Module</value>
|
||||
</data>
|
||||
<data name="Confirm.Module.Delete" xml:space="preserve">
|
||||
<value>Are You Sure You Wish To Delete The {0} Module?</value>
|
||||
<value>Are You Sure You Wish To Uninstall The {0} Module?</value>
|
||||
</data>
|
||||
<data name="Error.Module.Load" xml:space="preserve">
|
||||
<value>Error Loading Modules</value>
|
||||
@ -142,10 +142,10 @@
|
||||
<value>Install Module</value>
|
||||
</data>
|
||||
<data name="DeleteModule.Header" xml:space="preserve">
|
||||
<value>Delete Module</value>
|
||||
<value>Uninstall Module</value>
|
||||
</data>
|
||||
<data name="DeleteModule.Text" xml:space="preserve">
|
||||
<value>Delete</value>
|
||||
<value>Uninstall</value>
|
||||
</data>
|
||||
<data name="InUse" xml:space="preserve">
|
||||
<value>In Use?</value>
|
||||
|
@ -159,4 +159,10 @@
|
||||
<data name="Module Settings" xml:space="preserve">
|
||||
<value>Module Settings</value>
|
||||
</data>
|
||||
<data name="Pane.HelpText" xml:space="preserve">
|
||||
<value>The pane where the module will be displayed</value>
|
||||
</data>
|
||||
<data name="Pane.Text" xml:space="preserve">
|
||||
<value>Pane:</value>
|
||||
</data>
|
||||
</root>
|
@ -187,6 +187,12 @@
|
||||
<value>Optionally provide a regular expression (RegExp) for validating the value entered</value>
|
||||
</data>
|
||||
<data name="Validation.Text" xml:space="preserve">
|
||||
<value>Validation:</value>
|
||||
<value>Validation: </value>
|
||||
</data>
|
||||
</root>
|
||||
<data name="Rows.HelpText" xml:space="preserve">
|
||||
<value>The number of rows for text entry (one is the default)</value>
|
||||
</data>
|
||||
<data name="Rows.Text" xml:space="preserve">
|
||||
<value>Rows: </value>
|
||||
</data>
|
||||
</root>
|
||||
|
@ -283,7 +283,7 @@
|
||||
<value>Prerender? </value>
|
||||
</data>
|
||||
<data name="Runtime.HelpText" xml:space="preserve">
|
||||
<value>The Blazor runtime hosting model</value>
|
||||
<value>The Blazor runtime hosting model for the site</value>
|
||||
</data>
|
||||
<data name="Runtime.Text" xml:space="preserve">
|
||||
<value>Runtime: </value>
|
||||
@ -402,4 +402,25 @@
|
||||
<data name="Retention.Text" xml:space="preserve">
|
||||
<value>Retention (Days):</value>
|
||||
</data>
|
||||
<data name="FileExtensions.Heading" xml:space="preserve">
|
||||
<value>File Extensions</value>
|
||||
</data>
|
||||
<data name="ImageExtensions.HelpText" xml:space="preserve">
|
||||
<value>Enter a comma separated list of image file extensions</value>
|
||||
</data>
|
||||
<data name="ImageExtensions.Text" xml:space="preserve">
|
||||
<value>Image Extensions:</value>
|
||||
</data>
|
||||
<data name="UploadableFileExtensions.HelpText" xml:space="preserve">
|
||||
<value>Enter a comma separated list of uploadable file extensions</value>
|
||||
</data>
|
||||
<data name="UploadableFileExtensions.Text" xml:space="preserve">
|
||||
<value>Uploadable File Extensions:</value>
|
||||
</data>
|
||||
<data name="HybridEnabled.HelpText" xml:space="preserve">
|
||||
<value>Specifies if the site can be integrated with an external .NET MAUI hybrid application</value>
|
||||
</data>
|
||||
<data name="HybridEnabled.Text" xml:space="preserve">
|
||||
<value>Hybrid Enabled?</value>
|
||||
</data>
|
||||
</root>
|
@ -132,9 +132,6 @@
|
||||
<data name="Theme.Select" xml:space="preserve">
|
||||
<value>Select Theme</value>
|
||||
</data>
|
||||
<data name="DefaultContainer.Admin" xml:space="preserve">
|
||||
<value>Default Admin Container</value>
|
||||
</data>
|
||||
<data name="Aliases.HelpText" xml:space="preserve">
|
||||
<value>The urls for the site (comman delimited). This can include domain names (ie. domain.com), subdomains (ie. sub.domain.com) or a virtual folder (ie. domain.com/folder).</value>
|
||||
</data>
|
||||
@ -183,9 +180,6 @@
|
||||
<data name="DefaultTheme.HelpText" xml:space="preserve">
|
||||
<value>Select the default theme for the site</value>
|
||||
</data>
|
||||
<data name="AdminContainer.HelpText" xml:space="preserve">
|
||||
<value>Select the admin container for the site</value>
|
||||
</data>
|
||||
<data name="SiteTemplate.HelpText" xml:space="preserve">
|
||||
<value>Select the site template</value>
|
||||
</data>
|
||||
@ -207,9 +201,6 @@
|
||||
<data name="Name.Text" xml:space="preserve">
|
||||
<value>Site Name: </value>
|
||||
</data>
|
||||
<data name="AdminContainer.Text" xml:space="preserve">
|
||||
<value>Admin Container: </value>
|
||||
</data>
|
||||
<data name="SiteTemplate.Text" xml:space="preserve">
|
||||
<value>Site Template: </value>
|
||||
</data>
|
||||
|
@ -124,7 +124,7 @@
|
||||
<value>Error Downloading Theme</value>
|
||||
</data>
|
||||
<data name="Confirm.Theme.Delete" xml:space="preserve">
|
||||
<value>Are You Sure You Wish To Delete The {0} Theme?</value>
|
||||
<value>Are You Sure You Wish To Uninstall The {0} Theme?</value>
|
||||
</data>
|
||||
<data name="Error.Theme.Load" xml:space="preserve">
|
||||
<value>Error Loading Themes</value>
|
||||
@ -136,10 +136,10 @@
|
||||
<value>Error Deleting Theme</value>
|
||||
</data>
|
||||
<data name="DeleteTheme.Header" xml:space="preserve">
|
||||
<value>Delete Theme</value>
|
||||
<value>Uninstall Theme</value>
|
||||
</data>
|
||||
<data name="DeleteTheme.Text" xml:space="preserve">
|
||||
<value>Delete</value>
|
||||
<value>Uninstall</value>
|
||||
</data>
|
||||
<data name="CreateTheme.Text" xml:space="preserve">
|
||||
<value>Create Theme</value>
|
||||
|
@ -121,7 +121,7 @@
|
||||
<value>Message: </value>
|
||||
</data>
|
||||
<data name="Message.User.Invalid" xml:space="preserve">
|
||||
<value>User Does Not Exist. Please Verify That The Username Provided Is Correct.</value>
|
||||
<value>The User Specified Does Not Exist</value>
|
||||
</data>
|
||||
<data name="Error.Notification.Add" xml:space="preserve">
|
||||
<value>Error Adding Notification</value>
|
||||
@ -133,7 +133,7 @@
|
||||
<value>Enter the subject of the message</value>
|
||||
</data>
|
||||
<data name="Message.HelpText" xml:space="preserve">
|
||||
<value>Enter the message</value>
|
||||
<value>Enter the message content</value>
|
||||
</data>
|
||||
<data name="To.Text" xml:space="preserve">
|
||||
<value>To: </value>
|
||||
@ -144,4 +144,10 @@
|
||||
<data name="Send Notification" xml:space="preserve">
|
||||
<value>Send Notification</value>
|
||||
</data>
|
||||
<data name="Message.Required" xml:space="preserve">
|
||||
<value>You Must Enter All Required Information</value>
|
||||
</data>
|
||||
<data name="Username.Enter" xml:space="preserve">
|
||||
<value>Enter Username</value>
|
||||
</data>
|
||||
</root>
|
@ -147,6 +147,9 @@
|
||||
<data name="Message.User.NoLogIn" xml:space="preserve">
|
||||
<value>Current User Is Not Logged In</value>
|
||||
</data>
|
||||
<data name="Message.User.NoEmail" xml:space="preserve">
|
||||
<value>You Must Provide An Email Address For Your User Account</value>
|
||||
</data>
|
||||
<data name="Error.Profile.Load" xml:space="preserve">
|
||||
<value>Error Loading User Profile</value>
|
||||
</data>
|
||||
@ -233,5 +236,11 @@
|
||||
</data>
|
||||
<data name="DeleteNotification.Text" xml:space="preserve">
|
||||
<value>Delete</value>
|
||||
</data>
|
||||
</data>
|
||||
<data name="NoNotificationsReceived.Text" xml:space="preserve">
|
||||
<value>No notifications have been received</value>
|
||||
</data>
|
||||
<data name="NoNotificationsSent.Text" xml:space="preserve">
|
||||
<value>No notifications have been sent</value>
|
||||
</data>
|
||||
</root>
|
@ -126,25 +126,43 @@
|
||||
<data name="Error.Notification.Add" xml:space="preserve">
|
||||
<value>Error Adding Notification</value>
|
||||
</data>
|
||||
<data name="Title" xml:space="preserve">
|
||||
<value>Title:</value>
|
||||
</data>
|
||||
<data name="Subject" xml:space="preserve">
|
||||
<value>Subject:</value>
|
||||
</data>
|
||||
<data name="Date" xml:space="preserve">
|
||||
<value>Date:</value>
|
||||
</data>
|
||||
<data name="Message" xml:space="preserve">
|
||||
<value>Message:</value>
|
||||
</data>
|
||||
<data name="Reply" xml:space="preserve">
|
||||
<value>Reply</value>
|
||||
</data>
|
||||
<data name="OriginalMessage" xml:space="preserve">
|
||||
<value>Original Message</value>
|
||||
</data>
|
||||
<data name="View Notification" xml:space="preserve">
|
||||
<value>View Notification</value>
|
||||
</data>
|
||||
<data name="Date.HelpText" xml:space="preserve">
|
||||
<value>The date the message was sent</value>
|
||||
</data>
|
||||
<data name="Date.Text" xml:space="preserve">
|
||||
<value>Sent:</value>
|
||||
</data>
|
||||
<data name="From.HelpText" xml:space="preserve">
|
||||
<value>The user who sent the message</value>
|
||||
</data>
|
||||
<data name="From.Text" xml:space="preserve">
|
||||
<value>From:</value>
|
||||
</data>
|
||||
<data name="Message.HelpText" xml:space="preserve">
|
||||
<value>The content of the message</value>
|
||||
</data>
|
||||
<data name="Message.Text" xml:space="preserve">
|
||||
<value>Message:</value>
|
||||
</data>
|
||||
<data name="RE" xml:space="preserve">
|
||||
<value>RE:</value>
|
||||
</data>
|
||||
<data name="Subject.HelpText" xml:space="preserve">
|
||||
<value>The subject of the message</value>
|
||||
</data>
|
||||
<data name="Subject.Text" xml:space="preserve">
|
||||
<value>Subject:</value>
|
||||
</data>
|
||||
<data name="System" xml:space="preserve">
|
||||
<value>System</value>
|
||||
</data>
|
||||
<data name="To.HelpText" xml:space="preserve">
|
||||
<value>The user who will be the recipient of the message</value>
|
||||
</data>
|
||||
<data name="To.Text" xml:space="preserve">
|
||||
<value>To:</value>
|
||||
</data>
|
||||
</root>
|
@ -168,12 +168,6 @@
|
||||
<data name="Password.Text" xml:space="preserve">
|
||||
<value>Password:</value>
|
||||
</data>
|
||||
<data name="Photo.HelpText" xml:space="preserve">
|
||||
<value>A photo of the user</value>
|
||||
</data>
|
||||
<data name="Photo.Text" xml:space="preserve">
|
||||
<value>Photo:</value>
|
||||
</data>
|
||||
<data name="Username.HelpText" xml:space="preserve">
|
||||
<value>The unique username for a user. Note that this field can not be modified.</value>
|
||||
</data>
|
||||
|
@ -247,7 +247,7 @@
|
||||
<value>Domain Filter:</value>
|
||||
</data>
|
||||
<data name="EmailClaimType.HelpText" xml:space="preserve">
|
||||
<value>The name of the email address claim provided by the identity provider</value>
|
||||
<value>Optionally specify the type name of the email address claim provided by the identity provider. The typical value is 'email'.</value>
|
||||
</data>
|
||||
<data name="EmailClaimType.Text" xml:space="preserve">
|
||||
<value>Email Claim:</value>
|
||||
@ -274,7 +274,7 @@
|
||||
<value>Use PKCE?</value>
|
||||
</data>
|
||||
<data name="ProviderName.HelpText" xml:space="preserve">
|
||||
<value>The external login provider name which will be displayed on the login page</value>
|
||||
<value>Specify a friendly name for the external login provider which will be displayed on the Login page</value>
|
||||
</data>
|
||||
<data name="ProviderName.Text" xml:space="preserve">
|
||||
<value>Provider Name:</value>
|
||||
@ -373,7 +373,7 @@
|
||||
<value>Last Login</value>
|
||||
</data>
|
||||
<data name="IdentifierClaimType.HelpText" xml:space="preserve">
|
||||
<value>The name of the unique user identifier claim provided by the identity provider</value>
|
||||
<value>Specify the type name of the unique user identifier claim provided by the identity provider. The default value is 'sub'.</value>
|
||||
</data>
|
||||
<data name="IdentifierClaimType.Text" xml:space="preserve">
|
||||
<value>Identifier Claim:</value>
|
||||
@ -385,13 +385,13 @@
|
||||
<value>Parameters:</value>
|
||||
</data>
|
||||
<data name="RoleClaimType.HelpText" xml:space="preserve">
|
||||
<value>Optionally provide the name of the role claim provided by the identity provider. These roles will be used in addition to any internal user roles assigned within the site.</value>
|
||||
<value>Optionally provide the type name of the role claim provided by the identity provider. These roles will be used in addition to any internal user roles assigned within the site.</value>
|
||||
</data>
|
||||
<data name="RoleClaimType.Text" xml:space="preserve">
|
||||
<value>Role Claim:</value>
|
||||
</data>
|
||||
<data name="ProfileClaimTypes.HelpText" xml:space="preserve">
|
||||
<value>Optionally provide a comma delimited list of user profile claims provided by the identity provider, as well as mappings to your user profile definition. For example if the identity provider includes a 'given_name' claim and you have a 'FirstName' user profile definition you should specify 'given_name:FirstName'.</value>
|
||||
<value>Optionally provide a comma delimited list of user profile claim type names provided by the identity provider, as well as mappings to your user profile definition. For example if the identity provider includes a 'given_name' claim and you have a 'FirstName' user profile definition you should specify 'given_name:FirstName'.</value>
|
||||
</data>
|
||||
<data name="ProfileClaimTypes.Text" xml:space="preserve">
|
||||
<value>User Profile Claims:</value>
|
||||
@ -408,4 +408,64 @@
|
||||
<data name="ImportUsers.Text" xml:space="preserve">
|
||||
<value>Import Users</value>
|
||||
</data>
|
||||
<data name="AuthFlow.Code" xml:space="preserve">
|
||||
<value>Authorization Code</value>
|
||||
</data>
|
||||
<data name="AuthFlow.CodeIdToken" xml:space="preserve">
|
||||
<value>Authorization Code + ID Token</value>
|
||||
</data>
|
||||
<data name="AuthFlow.CodeIdTokenToken" xml:space="preserve">
|
||||
<value>Authorization Code + ID Token + Access Token</value>
|
||||
</data>
|
||||
<data name="AuthFlow.CodeToken" xml:space="preserve">
|
||||
<value>Authorization Code + Access Token</value>
|
||||
</data>
|
||||
<data name="AuthFlow.IdToken" xml:space="preserve">
|
||||
<value>ID Token</value>
|
||||
</data>
|
||||
<data name="AuthFlow.IdTokenToken" xml:space="preserve">
|
||||
<value>ID Token + Access Token</value>
|
||||
</data>
|
||||
<data name="AuthFlow.None" xml:space="preserve">
|
||||
<value>None</value>
|
||||
</data>
|
||||
<data name="AuthFlow.Token" xml:space="preserve">
|
||||
<value>Access Token</value>
|
||||
</data>
|
||||
<data name="AuthResponseType.Text" xml:space="preserve">
|
||||
<value>Authorization Response Type:</value>
|
||||
</data>
|
||||
<data name="AuthResponseType.HelpText" xml:space="preserve">
|
||||
<value>Specify the authorization response type. The default is Authorization Code which is considered to be the most secure option based on the latest OAuth specification.</value>
|
||||
</data>
|
||||
<data name="VerifyUsers.HelpText" xml:space="preserve">
|
||||
<value>Do you want existing users to perform an additional email verification step to link their external login? If you disable this option, existing users will be linked automatically.</value>
|
||||
</data>
|
||||
<data name="VerifyUsers.Text" xml:space="preserve">
|
||||
<value>Verify Existing Users?</value>
|
||||
</data>
|
||||
<data name="AlwaysRemember.HelpText" xml:space="preserve">
|
||||
<value>Enabling this option will set a permanent cookie in conjunction with the Cookie Expiration Timespan, which will automatically sign in users the next time they visit the site. By default the site will use session cookies.</value>
|
||||
</data>
|
||||
<data name="AlwaysRemember.Text" xml:space="preserve">
|
||||
<value>Always Remember User?</value>
|
||||
</data>
|
||||
<data name="CookieExpiration.HelpText" xml:space="preserve">
|
||||
<value>You can choose to use a custom authentication cookie expiration timespan for each site (e.g. '08:00:00' for 8 hours). The default is 14 days if not specified.</value>
|
||||
</data>
|
||||
<data name="CookieExpiration.Text" xml:space="preserve">
|
||||
<value>Cookie Expiration Timespan:</value>
|
||||
</data>
|
||||
<data name="ReviewClaims.Text" xml:space="preserve">
|
||||
<value>Review Claims?</value>
|
||||
</data>
|
||||
<data name="ReviewClaims.HelpText" xml:space="preserve">
|
||||
<value>This option will record the full list of Claims returned by the Provider in the Event Log. It should only be used for testing purposes. External Login will be restricted when this option is enabled.</value>
|
||||
</data>
|
||||
<data name="NameClaimType.HelpText" xml:space="preserve">
|
||||
<value>Optionally specify the type name of the user's name claim provided by the identity provider. The typical value is 'name'.</value>
|
||||
</data>
|
||||
<data name="NameClaimType.Text" xml:space="preserve">
|
||||
<value>Name Claim:</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
|
||||
@ -120,4 +120,7 @@
|
||||
<data name="PageOfPages" xml:space="preserve">
|
||||
<value>Page {0} of {1}</value>
|
||||
</data>
|
||||
<data name="SearchPlaceholder" xml:space="preserve">
|
||||
<value>Search: {0}</value>
|
||||
</data>
|
||||
</root>
|
@ -426,4 +426,16 @@
|
||||
<data name="Password.ValidationCriteria" xml:space="preserve">
|
||||
<value>Passwords Must Have A Minimum Length Of {0} Characters, Including At Least {1} Unique Character(s), {2}{3}{4}{5} To Satisfy Password Compexity Requirements For This Site.</value>
|
||||
</data>
|
||||
<data name="ProfileInvalid" xml:space="preserve">
|
||||
<value>{0} Is Not Valid</value>
|
||||
</data>
|
||||
<data name="ProfileRequired" xml:space="preserve">
|
||||
<value>{0} Is Required</value>
|
||||
</data>
|
||||
<data name="Uninstall" xml:space="preserve">
|
||||
<value>Uninstall</value>
|
||||
</data>
|
||||
<data name="Test" xml:space="preserve">
|
||||
<value>Test</value>
|
||||
</data>
|
||||
</root>
|
@ -189,4 +189,13 @@
|
||||
<data name="Confirm.Page.Delete" xml:space="preserve">
|
||||
<value>Are You Sure You Want To Delete This Page?</value>
|
||||
</data>
|
||||
<data name="Location" xml:space="preserve">
|
||||
<value>Location:</value>
|
||||
</data>
|
||||
<data name="LocationBottom" xml:space="preserve">
|
||||
<value>Bottom</value>
|
||||
</data>
|
||||
<data name="LocationTop" xml:space="preserve">
|
||||
<value>Top</value>
|
||||
</data>
|
||||
</root>
|
@ -1,6 +1,6 @@
|
||||
using Oqtane.Models;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using Oqtane.Models;
|
||||
|
||||
namespace Oqtane.Services
|
||||
{
|
||||
@ -39,6 +39,13 @@ namespace Oqtane.Services
|
||||
/// <returns></returns>
|
||||
Task<Language> AddLanguageAsync(Language language);
|
||||
|
||||
/// <summary>
|
||||
/// Edits the given language
|
||||
/// </summary>
|
||||
/// <param name="language"></param>
|
||||
/// <returns></returns>
|
||||
Task EditLanguageAsync(Language language);
|
||||
|
||||
/// <summary>
|
||||
/// Deletes the given language
|
||||
/// </summary>
|
||||
|
@ -1,5 +1,4 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
using Oqtane.Documentation;
|
||||
@ -10,7 +9,7 @@ namespace Oqtane.Services
|
||||
{
|
||||
[PrivateApi("Don't show in the documentation, as everything should use the Interface")]
|
||||
public class LanguageService : ServiceBase, ILanguageService
|
||||
{
|
||||
{
|
||||
public LanguageService(HttpClient http, SiteState siteState) : base(http, siteState) { }
|
||||
|
||||
private string Apiurl => CreateApiUrl("Language");
|
||||
@ -35,6 +34,11 @@ namespace Oqtane.Services
|
||||
return await PostJsonAsync<Language>(Apiurl, language);
|
||||
}
|
||||
|
||||
public async Task EditLanguageAsync(Language language)
|
||||
{
|
||||
await PutJsonAsync<Language>(Apiurl, language);
|
||||
}
|
||||
|
||||
public async Task DeleteLanguageAsync(int languageId)
|
||||
{
|
||||
await DeleteAsync($"{Apiurl}/{languageId}");
|
||||
|
@ -4,6 +4,7 @@ using System.Net.Http;
|
||||
using System;
|
||||
using Oqtane.Documentation;
|
||||
using Oqtane.Shared;
|
||||
using System.Globalization;
|
||||
|
||||
namespace Oqtane.Services
|
||||
{
|
||||
@ -18,7 +19,7 @@ namespace Oqtane.Services
|
||||
/// <inheritdoc />
|
||||
public async Task<Sync> GetSyncEventsAsync(DateTime lastSyncDate)
|
||||
{
|
||||
return await GetJsonAsync<Sync>($"{ApiUrl}/{lastSyncDate.ToString("yyyyMMddHHmmssfff")}");
|
||||
return await GetJsonAsync<Sync>($"{ApiUrl}/{lastSyncDate.ToString("yyyyMMddHHmmssfff", CultureInfo.InvariantCulture)}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -15,7 +15,7 @@
|
||||
|
||||
<div class="main g-0">
|
||||
<div class="top-row px-4">
|
||||
<div class="ms-auto"><UserProfile /> <Login /> <ControlPanel /></div>
|
||||
<div class="ms-auto"><UserProfile /> <Login /> <ControlPanel LanguageDropdownAlignment="right" /></div>
|
||||
</div>
|
||||
<div class="container">
|
||||
<div class="row px-4">
|
||||
|
@ -17,7 +17,7 @@
|
||||
|
||||
@if (ShowLanguageSwitcher)
|
||||
{
|
||||
<LanguageSwitcher />
|
||||
<LanguageSwitcher DropdownAlignment="@LanguageDropdownAlignment" />
|
||||
}
|
||||
|
||||
@if (_showEditMode || (PageState.Page.IsPersonalizable && PageState.User != null && UserSecurity.IsAuthorized(PageState.User, RoleNames.Registered)))
|
||||
@ -114,7 +114,7 @@
|
||||
|
||||
<div class="row">
|
||||
<div class="col text-center">
|
||||
<label for="Module" class="control-label">@Localizer["Module.Manage"] </label>
|
||||
<label for="Module" class="control-label">@Localizer["Module.Manage"]</label>
|
||||
<select class="form-select" @bind="@ModuleType">
|
||||
<option value="new">@Localizer["Module.AddNew"]</option>
|
||||
<option value="existing">@Localizer["Module.AddExisting"]</option>
|
||||
@ -179,27 +179,33 @@
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col text-center">
|
||||
<label for="Title" class="control-label">@Localizer["Title"] </label>
|
||||
<label for="Title" class="control-label">@Localizer["Title"]</label>
|
||||
<input type="text" name="Title" class="form-control" @bind="@Title" />
|
||||
</div>
|
||||
</div>
|
||||
@if (_pane.Length > 1)
|
||||
{
|
||||
<div class="row">
|
||||
<div class="col text-center">
|
||||
<label for="Pane" class="control-label">@Localizer["Pane"] </label>
|
||||
<select class="form-select" @bind="@Pane">
|
||||
@foreach (string pane in PageState.Page.Panes)
|
||||
{
|
||||
<option value="@pane">@pane Pane</option>
|
||||
}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
<div class="row">
|
||||
<div class="col text-center">
|
||||
<label for="Container" class="control-label">@Localizer["Container"] </label>
|
||||
<label for="Pane" class="control-label">@Localizer["Pane"]</label>
|
||||
<select class="form-select" @bind="@Pane">
|
||||
@foreach (string pane in PageState.Page.Panes)
|
||||
{
|
||||
<option value="@pane">@pane Pane</option>
|
||||
}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col text-center">
|
||||
<label for="Insert" class="control-label">@Localizer["Location"]</label>
|
||||
<select class="form-select" @bind="@Location">
|
||||
<option value="@int.MinValue">@Localizer["LocationTop"]</option>
|
||||
<option value="@int.MaxValue">@Localizer["LocationBottom"]</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col text-center">
|
||||
<label for="Container" class="control-label">@Localizer["Container"]</label>
|
||||
<select class="form-select" @bind="@ContainerType">
|
||||
@foreach (var container in _containers)
|
||||
{
|
||||
@ -233,6 +239,24 @@
|
||||
}
|
||||
|
||||
@code{
|
||||
[Parameter]
|
||||
public string ButtonClass { get; set; } = "btn-outline-secondary";
|
||||
|
||||
[Parameter]
|
||||
public string ContainerClass { get; set; } = "offcanvas offcanvas-end";
|
||||
|
||||
[Parameter]
|
||||
public string HeaderClass { get; set; } = "offcanvas-header";
|
||||
|
||||
[Parameter]
|
||||
public string BodyClass { get; set; } = "offcanvas-body overflow-auto";
|
||||
|
||||
[Parameter]
|
||||
public bool ShowLanguageSwitcher { get; set; } = true;
|
||||
|
||||
[Parameter]
|
||||
public string LanguageDropdownAlignment { get; set; } = string.Empty; // Empty or Left or Right
|
||||
|
||||
private bool _canViewAdminDashboard = false;
|
||||
private bool _showEditMode = false;
|
||||
private bool _deleteConfirmation = false;
|
||||
@ -243,6 +267,7 @@
|
||||
private List<Module> _modules = new List<Module>();
|
||||
private List<ThemeControl> _containers = new List<ThemeControl>();
|
||||
private string _category = "Common";
|
||||
private string _pane = "";
|
||||
|
||||
protected string PageId { get; private set; } = "-";
|
||||
protected string ModuleId { get; private set; } = "-";
|
||||
@ -251,12 +276,12 @@
|
||||
|
||||
protected string Title { get; private set; } = "";
|
||||
protected string ContainerType { get; private set; } = "";
|
||||
protected int Location { get; private set; } = int.MaxValue;
|
||||
protected string Visibility { get; private set; } = "view";
|
||||
protected string Message { get; private set; } = "";
|
||||
|
||||
private string settingCategory = "CP-category";
|
||||
private string settingPane = "CP-pane";
|
||||
private string _pane = "";
|
||||
|
||||
protected string Category
|
||||
{
|
||||
@ -288,21 +313,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
[Parameter]
|
||||
public string ButtonClass { get; set; } = "btn-outline-secondary";
|
||||
|
||||
[Parameter]
|
||||
public string ContainerClass { get; set; } = "offcanvas offcanvas-end";
|
||||
|
||||
[Parameter]
|
||||
public string HeaderClass { get; set; } = "offcanvas-header";
|
||||
|
||||
[Parameter]
|
||||
public string BodyClass { get; set; } = "offcanvas-body overflow-auto";
|
||||
|
||||
[Parameter]
|
||||
public bool ShowLanguageSwitcher { get; set; } = true;
|
||||
|
||||
protected override async Task OnParametersSetAsync()
|
||||
{
|
||||
_canViewAdminDashboard = CanViewAdminDashboard();
|
||||
@ -310,8 +320,9 @@
|
||||
if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.Edit, PageState.Page.PermissionList))
|
||||
{
|
||||
_showEditMode = true;
|
||||
_pages?.Clear();
|
||||
LoadSettingsAsync();
|
||||
|
||||
_pages?.Clear();
|
||||
foreach (Page p in PageState.Pages)
|
||||
{
|
||||
if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.View, p.PermissionList))
|
||||
@ -319,7 +330,6 @@
|
||||
_pages.Add(p);
|
||||
}
|
||||
}
|
||||
await LoadSettingsAsync();
|
||||
|
||||
var themes = await ThemeService.GetThemesAsync();
|
||||
_containers = ThemeService.GetContainerControls(themes, PageState.Page.ThemeType);
|
||||
@ -443,7 +453,7 @@
|
||||
}
|
||||
|
||||
pageModule.Pane = Pane;
|
||||
pageModule.Order = int.MaxValue;
|
||||
pageModule.Order = Location;
|
||||
pageModule.ContainerType = ContainerType;
|
||||
|
||||
if (pageModule.ContainerType == PageState.Site.DefaultContainerType)
|
||||
@ -653,11 +663,10 @@
|
||||
}
|
||||
}
|
||||
|
||||
private async Task LoadSettingsAsync()
|
||||
private void LoadSettingsAsync()
|
||||
{
|
||||
Dictionary<string, string> settings = await SettingService.GetUserSettingsAsync(PageState.User.UserId);
|
||||
_category = SettingService.GetSetting(settings, settingCategory, "Common");
|
||||
var pane = SettingService.GetSetting(settings, settingPane, "");
|
||||
_category = SettingService.GetSetting(PageState.User.Settings, settingCategory, "Common");
|
||||
var pane = SettingService.GetSetting(PageState.User.Settings, settingPane, "");
|
||||
if (PageState.Page.Panes.Contains(pane))
|
||||
{
|
||||
_pane = pane;
|
||||
|
@ -12,7 +12,7 @@
|
||||
<button id="btnCultures" type="button" class="btn btn-outline-secondary dropdown-toggle" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
||||
<span class="oi oi-globe"></span>
|
||||
</button>
|
||||
<div class="dropdown-menu" aria-labelledby="btnCultures">
|
||||
<div class="dropdown-menu @MenuAlignment" aria-labelledby="btnCultures">
|
||||
@foreach (var culture in _supportedCultures)
|
||||
{
|
||||
<a class="dropdown-item @(CultureInfo.CurrentUICulture.Name == culture.Name ? "active" : String.Empty)" href="#" @onclick="@(async e => await SetCultureAsync(culture.Name))">@culture.DisplayName</a>
|
||||
@ -23,10 +23,15 @@
|
||||
|
||||
@code{
|
||||
private IEnumerable<Culture> _supportedCultures;
|
||||
[Parameter]
|
||||
public string DropdownAlignment { get; set; } = string.Empty; // Empty or Left or Right
|
||||
private string MenuAlignment = string.Empty;
|
||||
|
||||
protected override void OnParametersSet()
|
||||
{
|
||||
var languages = PageState.Languages;
|
||||
MenuAlignment = DropdownAlignment.ToLower() == "right" ? "dropdown-menu-end" : string.Empty;
|
||||
|
||||
var languages = PageState.Languages;
|
||||
_supportedCultures = languages.Select(l => new Culture { Name = l.Code, DisplayName = l.Name });
|
||||
}
|
||||
|
||||
|
@ -23,8 +23,29 @@ namespace Oqtane.Themes.Controls
|
||||
|
||||
protected void LoginUser()
|
||||
{
|
||||
Route route = new Route(PageState.Uri.AbsoluteUri, PageState.Alias.Path);
|
||||
NavigationManager.NavigateTo(NavigateUrl("login", "?returnurl=" + WebUtility.UrlEncode(route.PathAndQuery)));
|
||||
var allowexternallogin = (SettingService.GetSetting(PageState.Site.Settings, "ExternalLogin:ProviderType", "") != "") ? true : false;
|
||||
var allowsitelogin = bool.Parse(SettingService.GetSetting(PageState.Site.Settings, "LoginOptions:AllowSiteLogin", "true"));
|
||||
|
||||
var returnurl = "";
|
||||
if (!PageState.QueryString.ContainsKey("returnurl"))
|
||||
{
|
||||
returnurl = WebUtility.UrlEncode(PageState.Route.PathAndQuery); // remember current url
|
||||
}
|
||||
else
|
||||
{
|
||||
returnurl = PageState.QueryString["returnurl"]; // use existing value
|
||||
}
|
||||
|
||||
if (allowexternallogin && !allowsitelogin)
|
||||
{
|
||||
// external login
|
||||
NavigationManager.NavigateTo(Utilities.TenantUrl(PageState.Alias, "/pages/external?returnurl=" + returnurl), true);
|
||||
}
|
||||
else
|
||||
{
|
||||
// local login
|
||||
NavigationManager.NavigateTo(NavigateUrl("login", "?returnurl=" + returnurl));
|
||||
}
|
||||
}
|
||||
|
||||
protected async Task LogoutUser()
|
||||
|
@ -1,4 +1,5 @@
|
||||
@namespace Oqtane.Themes.Controls
|
||||
@using System.Net
|
||||
@inherits ThemeControlBase
|
||||
@inject IStringLocalizer<UserProfile> Localizer
|
||||
|
||||
@ -26,14 +27,21 @@
|
||||
[Parameter]
|
||||
public bool ShowRegister { get; set; }
|
||||
|
||||
private string _returnurl = "";
|
||||
|
||||
protected override void OnParametersSet()
|
||||
{
|
||||
_returnurl = WebUtility.UrlEncode(PageState.Route.PathAndQuery);
|
||||
}
|
||||
|
||||
private void RegisterUser()
|
||||
{
|
||||
NavigationManager.NavigateTo(NavigateUrl("register"));
|
||||
NavigationManager.NavigateTo(NavigateUrl("register", "returnurl=" + _returnurl));
|
||||
}
|
||||
|
||||
private void UpdateProfile()
|
||||
{
|
||||
NavigationManager.NavigateTo(NavigateUrl("profile"));
|
||||
NavigationManager.NavigateTo(NavigateUrl("profile", "returnurl=" + _returnurl));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -6,7 +6,7 @@
|
||||
<nav class="navbar navbar-dark bg-primary fixed-top">
|
||||
<Logo /><Menu Orientation="Horizontal" />
|
||||
<div class="controls ms-auto">
|
||||
<div class="controls-group"><UserProfile ShowRegister="@_register" /> <Login ShowLogin="@_login" /> <ControlPanel /></div>
|
||||
<div class="controls-group"><UserProfile ShowRegister="@_register" /> <Login ShowLogin="@_login" /> <ControlPanel LanguageDropdownAlignment="right" /></div>
|
||||
</div>
|
||||
</nav>
|
||||
<div class="content">
|
||||
|
@ -5,7 +5,10 @@
|
||||
|
||||
@if (CurrentException is null)
|
||||
{
|
||||
<ModuleMessage Message="@_message" Type="@_messageType"/>
|
||||
if (_messagePosition == "top")
|
||||
{
|
||||
<ModuleMessage Message="@_message" Type="@_messageType" />
|
||||
}
|
||||
@if (ModuleType != null)
|
||||
{
|
||||
<DynamicComponent Type="@ModuleType" Parameters="@ModuleParameters"></DynamicComponent>
|
||||
@ -14,6 +17,10 @@
|
||||
<div class="app-progress-indicator"></div>
|
||||
}
|
||||
}
|
||||
if (_messagePosition == "bottom")
|
||||
{
|
||||
<ModuleMessage Message="@_message" Type="@_messageType" />
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -24,49 +31,58 @@ else
|
||||
}
|
||||
|
||||
@code {
|
||||
private string _message;
|
||||
private string _error;
|
||||
private MessageType _messageType;
|
||||
private bool _progressIndicator = false;
|
||||
private string _message;
|
||||
private string _error;
|
||||
private MessageType _messageType;
|
||||
private string _messagePosition;
|
||||
private bool _progressIndicator = false;
|
||||
|
||||
private Type ModuleType { get; set; }
|
||||
private IDictionary<string, object> ModuleParameters { get; set; }
|
||||
private Type ModuleType { get; set; }
|
||||
private IDictionary<string, object> ModuleParameters { get; set; }
|
||||
|
||||
[CascadingParameter]
|
||||
protected PageState PageState { get; set; }
|
||||
[CascadingParameter]
|
||||
protected PageState PageState { get; set; }
|
||||
|
||||
[CascadingParameter]
|
||||
private Module ModuleState { get; set; }
|
||||
[CascadingParameter]
|
||||
private Module ModuleState { get; set; }
|
||||
|
||||
private ModuleMessage ModuleMessage { get; set; }
|
||||
private ModuleMessage ModuleMessage { get; set; }
|
||||
|
||||
protected override void OnParametersSet()
|
||||
{
|
||||
_message = "";
|
||||
if (!string.IsNullOrEmpty(ModuleState.ModuleType))
|
||||
{
|
||||
ModuleType = Type.GetType(ModuleState.ModuleType);
|
||||
if (ModuleType != null)
|
||||
{
|
||||
ModuleParameters = new Dictionary<string, object> { { "ModuleInstance", this } };
|
||||
return;
|
||||
}
|
||||
// module does not exist with typename specified
|
||||
_message = string.Format(Localizer["Error.Module.InvalidName"], Utilities.GetTypeNameLastSegment(ModuleState.ModuleType, 0));
|
||||
_messageType = MessageType.Error;
|
||||
}
|
||||
else
|
||||
{
|
||||
_message = string.Format(Localizer["Error.Module.InvalidType"], ModuleState.ModuleDefinitionName);
|
||||
_messageType = MessageType.Error;
|
||||
}
|
||||
}
|
||||
protected override void OnParametersSet()
|
||||
{
|
||||
_message = "";
|
||||
if (!string.IsNullOrEmpty(ModuleState.ModuleType))
|
||||
{
|
||||
ModuleType = Type.GetType(ModuleState.ModuleType);
|
||||
if (ModuleType != null)
|
||||
{
|
||||
ModuleParameters = new Dictionary<string, object> { { "ModuleInstance", this } };
|
||||
return;
|
||||
}
|
||||
// module does not exist with typename specified
|
||||
_message = string.Format(Localizer["Error.Module.InvalidName"], Utilities.GetTypeNameLastSegment(ModuleState.ModuleType, 0));
|
||||
_messageType = MessageType.Error;
|
||||
_messagePosition = "top";
|
||||
}
|
||||
else
|
||||
{
|
||||
_message = string.Format(Localizer["Error.Module.InvalidType"], ModuleState.ModuleDefinitionName);
|
||||
_messageType = MessageType.Error;
|
||||
_messagePosition = "top";
|
||||
}
|
||||
}
|
||||
|
||||
public void AddModuleMessage(string message, MessageType type)
|
||||
{
|
||||
_message = message;
|
||||
_messageType = type;
|
||||
_progressIndicator = false;
|
||||
public void AddModuleMessage(string message, MessageType type)
|
||||
{
|
||||
AddModuleMessage(message, type, "top");
|
||||
}
|
||||
|
||||
public void AddModuleMessage(string message, MessageType type, string position)
|
||||
{
|
||||
_message = message;
|
||||
_messageType = type;
|
||||
_messagePosition = position;
|
||||
_progressIndicator = false;
|
||||
StateHasChanged();
|
||||
}
|
||||
|
||||
|
@ -105,11 +105,18 @@
|
||||
Route route = new Route(_absoluteUri, SiteState.Alias.Path);
|
||||
int moduleid = (int.TryParse(route.ModuleId, out moduleid)) ? moduleid : -1;
|
||||
var action = (!string.IsNullOrEmpty(route.Action)) ? route.Action : Constants.DefaultAction;
|
||||
|
||||
var querystring = Utilities.ParseQueryString(route.Query);
|
||||
var returnurl = "";
|
||||
if (querystring.ContainsKey("returnurl"))
|
||||
{
|
||||
returnurl = WebUtility.UrlDecode(querystring["returnurl"]);
|
||||
if (!returnurl.StartsWith("/"))
|
||||
{
|
||||
// urls which are not relative are vulnerable to open redirects or XSS
|
||||
returnurl = "";
|
||||
querystring["returnurl"] = "";
|
||||
}
|
||||
}
|
||||
|
||||
// reload the client application from the server if there is a forced reload
|
||||
@ -155,7 +162,8 @@
|
||||
if (PageState == null || refresh || PageState.Alias.SiteId != SiteState.Alias.SiteId)
|
||||
{
|
||||
var authState = await AuthenticationStateProvider.GetAuthenticationStateAsync();
|
||||
if (authState.User.Identity.IsAuthenticated)
|
||||
// verify user is authenticated for current site
|
||||
if (authState.User.Identity.IsAuthenticated && authState.User.Claims.Any(item => item.Type == "sitekey" && item.Value == SiteState.Alias.SiteKey))
|
||||
{
|
||||
user = await UserService.GetUserAsync(authState.User.Identity.Name, SiteState.Alias.SiteId);
|
||||
if (user != null)
|
||||
|
@ -1,4 +1,5 @@
|
||||
@namespace Oqtane.UI
|
||||
@using System.Net
|
||||
@inject IJSRuntime JSRuntime
|
||||
@inject NavigationManager NavigationManager
|
||||
@inject SiteState SiteState
|
||||
@ -87,6 +88,13 @@
|
||||
|
||||
protected override async Task OnAfterRenderAsync(bool firstRender)
|
||||
{
|
||||
// force authenticated user to provide email address (email may be missing if using external login)
|
||||
if (PageState.User != null && PageState.User.IsAuthenticated && string.IsNullOrEmpty(PageState.User.Email) && PageState.Route.PagePath != "profile")
|
||||
{
|
||||
NavigationManager.NavigateTo(Utilities.NavigateUrl(PageState.Alias.Path, "profile", "returnurl=" + WebUtility.UrlEncode(PageState.Route.PathAndQuery)));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!firstRender)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(PageState.Page.HeadContent) && PageState.Page.HeadContent.Contains("<script"))
|
||||
|
@ -1,12 +1,10 @@
|
||||
using System.Data;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Microsoft.EntityFrameworkCore.Migrations.Operations;
|
||||
using Microsoft.EntityFrameworkCore.Migrations.Operations.Builders;
|
||||
using MySql.Data.MySqlClient;
|
||||
using MySql.EntityFrameworkCore.Metadata;
|
||||
using Oqtane.Databases;
|
||||
using Oqtane.Shared;
|
||||
|
||||
namespace Oqtane.Database.MySQL
|
||||
{
|
||||
|
@ -1,8 +1,8 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<Version>4.0.5</Version>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<Version>5.0.1</Version>
|
||||
<Product>Oqtane</Product>
|
||||
<Authors>Shaun Walker</Authors>
|
||||
<Company>.NET Foundation</Company>
|
||||
@ -10,14 +10,10 @@
|
||||
<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/v4.0.5</PackageReleaseNotes>
|
||||
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v5.0.1</PackageReleaseNotes>
|
||||
<RepositoryUrl>https://github.com/oqtane/oqtane.framework</RepositoryUrl>
|
||||
<RepositoryType>Git</RepositoryType>
|
||||
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
|
||||
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
|
||||
<NuspecFile>$(MSBuildProjectName).nuspec</NuspecFile>
|
||||
<PackageName>$(MSBuildProjectName).$(Version).nupkg</PackageName>
|
||||
<SuppressDependenciesWhenPacking>true</SuppressDependenciesWhenPacking>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
|
||||
@ -27,16 +23,30 @@
|
||||
<PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
|
||||
<OutputPath>bin</OutputPath>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
||||
<NoWarn>1701;1702;EF1001;AD0001</NoWarn>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
|
||||
<NoWarn>1701;1702;EF1001;AD0001</NoWarn>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="MySql.EntityFrameworkCore" Version="7.0.2" />
|
||||
<PackageReference Include="MySql.EntityFrameworkCore" Version="8.0.0-preview" />
|
||||
<PackageReference Include="MySql.Data" Version="8.2.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Oqtane.Server\Oqtane.Server.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<Target Name="CopyPackage" AfterTargets="Pack">
|
||||
<Copy SourceFiles="$(OutputPath)..\$(PackageName)" DestinationFiles="..\Oqtane.Server\wwwroot\Packages\$(MSBuildProjectName).nupkg.bak" />
|
||||
<ItemGroup>
|
||||
<MySQLFiles Include="$(OutputPath)Oqtane.Database.MySQL.dll;$(OutputPath)Oqtane.Database.MySQL.pdb;$(OutputPath)MySql.EntityFrameworkCore.dll;$(OutputPath)MySql.Data.dll" DestinationPath="..\Oqtane.Server\bin\$(Configuration)\net8.0\%(Filename)%(Extension)" />
|
||||
</ItemGroup>
|
||||
|
||||
<Target Name="PublishProvider" AfterTargets="PostBuildEvent" Inputs="@(MySQLFiles)" Outputs="@(MySQLFiles->'%(DestinationPath)')">
|
||||
<Copy SourceFiles="@(MySQLFiles)" DestinationFiles="@(MySQLFiles->'%(DestinationPath)')" />
|
||||
</Target>
|
||||
|
||||
</Project>
|
||||
|
@ -1,26 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
|
||||
<metadata>
|
||||
<id>Oqtane.Database.MySQL</id>
|
||||
<version>4.0.5</version>
|
||||
<authors>Shaun Walker</authors>
|
||||
<owners>.NET Foundation</owners>
|
||||
<title>Oqtane MySQL Provider</title>
|
||||
<summary>MySQL database support for the Oqtane Framework</summary>
|
||||
<description>MySQL database support for the Oqtane Framework</description>
|
||||
<copyright>.NET Foundation</copyright>
|
||||
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
||||
<license type="expression">MIT</license>
|
||||
<projectUrl>https://github.com/oqtane/oqtane.framework</projectUrl>
|
||||
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v4.0.5</releaseNotes>
|
||||
<icon>icon.png</icon>
|
||||
<tags>oqtane</tags>
|
||||
</metadata>
|
||||
<files>
|
||||
<file src="bin\net7.0\Oqtane.Database.MySQL.dll" target="lib\net7.0" />
|
||||
<file src="bin\net7.0\Oqtane.Database.MySQL.pdb" target="lib\net7.0" />
|
||||
<file src="bin\net7.0\Mysql.EntityFrameworkCore.dll" target="lib\net7.0" />
|
||||
<file src="bin\net7.0\Mysql.Data.dll" target="lib\net7.0" />
|
||||
<file src="icon.png" target="" />
|
||||
</files>
|
||||
</package>
|
Binary file not shown.
Before Width: | Height: | Size: 4.8 KiB |
@ -1,8 +1,8 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<Version>4.0.5</Version>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<Version>5.0.1</Version>
|
||||
<Product>Oqtane</Product>
|
||||
<Authors>Shaun Walker</Authors>
|
||||
<Company>.NET Foundation</Company>
|
||||
@ -10,14 +10,10 @@
|
||||
<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/v4.0.5</PackageReleaseNotes>
|
||||
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v5.0.1</PackageReleaseNotes>
|
||||
<RepositoryUrl>https://github.com/oqtane/oqtane.framework</RepositoryUrl>
|
||||
<RepositoryType>Git</RepositoryType>
|
||||
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
|
||||
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
|
||||
<NuspecFile>$(MSBuildProjectName).nuspec</NuspecFile>
|
||||
<PackageName>$(MSBuildProjectName).$(Version).nupkg</PackageName>
|
||||
<SuppressDependenciesWhenPacking>true</SuppressDependenciesWhenPacking>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
|
||||
@ -27,18 +23,31 @@
|
||||
<PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
|
||||
<OutputPath>bin</OutputPath>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
||||
<NoWarn>1701;1702;EF1001;AD0001</NoWarn>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
|
||||
<NoWarn>1701;1702;EF1001;AD0001</NoWarn>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="EFCore.NamingConventions" Version="7.0.2" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="7.0.5" />
|
||||
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="7.0.4" />
|
||||
<PackageReference Include="EFCore.NamingConventions" Version="8.0.0-rc.2" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="8.0.0" />
|
||||
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="8.0.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Oqtane.Server\Oqtane.Server.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<Target Name="CopyPackage" AfterTargets="Pack">
|
||||
<Copy SourceFiles="$(OutputPath)..\$(PackageName)" DestinationFiles="..\Oqtane.Server\wwwroot\Packages\$(MSBuildProjectName).nupkg.bak" />
|
||||
<ItemGroup>
|
||||
<PostgreSQLFiles Include="$(OutputPath)Oqtane.Database.PostgreSQL.dll;$(OutputPath)Oqtane.Database.PostgreSQL.pdb;$(OutputPath)EFCore.NamingConventions.dll;$(OutputPath)Npgsql.EntityFrameworkCore.PostgreSQL.dll;$(OutputPath)Npgsql.dll" DestinationPath="..\Oqtane.Server\bin\$(Configuration)\net8.0\%(Filename)%(Extension)" />
|
||||
</ItemGroup>
|
||||
|
||||
<Target Name="PublishProvider" AfterTargets="PostBuildEvent" Inputs="@(PostgreSQLFiles)" Outputs="@(PostgreSQLFiles->'%(DestinationPath)')">
|
||||
<Copy SourceFiles="@(PostgreSQLFiles)" DestinationFiles="@(PostgreSQLFiles->'%(DestinationPath)')" />
|
||||
</Target>
|
||||
|
||||
</Project>
|
||||
|
@ -1,27 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
|
||||
<metadata>
|
||||
<id>Oqtane.Database.PostgreSQL</id>
|
||||
<version>4.0.5</version>
|
||||
<authors>Shaun Walker</authors>
|
||||
<owners>.NET Foundation</owners>
|
||||
<title>Oqtane PostgreSQL Provider</title>
|
||||
<summary>PostgreSQL database support for the Oqtane Framework</summary>
|
||||
<description>PostgreSQL database support for the Oqtane Framework</description>
|
||||
<copyright>.NET Foundation</copyright>
|
||||
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
||||
<license type="expression">MIT</license>
|
||||
<projectUrl>https://github.com/oqtane/oqtane.framework</projectUrl>
|
||||
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v4.0.5</releaseNotes>
|
||||
<icon>icon.png</icon>
|
||||
<tags>oqtane</tags>
|
||||
</metadata>
|
||||
<files>
|
||||
<file src="bin\net7.0\Oqtane.Database.PostgreSQL.dll" target="lib\net7.0" />
|
||||
<file src="bin\net7.0\Oqtane.Database.PostgreSQL.pdb" target="lib\net7.0" />
|
||||
<file src="bin\net7.0\EFCore.NamingConventions.dll" target="lib\net7.0" />
|
||||
<file src="bin\net7.0\Npgsql.EntityFrameworkCore.PostgreSQL.dll" target="lib\net7.0" />
|
||||
<file src="bin\net7.0\Npgsql.dll" target="lib\net7.0" />
|
||||
<file src="icon.png" target="" />
|
||||
</files>
|
||||
</package>
|
@ -1,11 +1,9 @@
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Metadata.Builders;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Npgsql.EntityFrameworkCore.PostgreSQL.Migrations.Internal;
|
||||
using Oqtane.Migrations.Framework;
|
||||
using Oqtane.Models;
|
||||
using Oqtane.Shared;
|
||||
|
||||
// ReSharper disable ClassNeverInstantiated.Global
|
||||
|
||||
|
@ -9,7 +9,6 @@ using Microsoft.EntityFrameworkCore.Migrations.Operations.Builders;
|
||||
using Npgsql;
|
||||
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
||||
using Oqtane.Databases;
|
||||
using Oqtane.Shared;
|
||||
|
||||
namespace Oqtane.Database.PostgreSQL
|
||||
{
|
||||
|
Binary file not shown.
Before Width: | Height: | Size: 4.8 KiB |
@ -1,8 +1,8 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<Version>4.0.5</Version>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<Version>5.0.1</Version>
|
||||
<Product>Oqtane</Product>
|
||||
<Authors>Shaun Walker</Authors>
|
||||
<Company>.NET Foundation</Company>
|
||||
@ -10,14 +10,10 @@
|
||||
<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/v4.0.5</PackageReleaseNotes>
|
||||
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v5.0.1</PackageReleaseNotes>
|
||||
<RepositoryUrl>https://github.com/oqtane/oqtane.framework</RepositoryUrl>
|
||||
<RepositoryType>Git</RepositoryType>
|
||||
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
|
||||
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
|
||||
<NuspecFile>$(MSBuildProjectName).nuspec</NuspecFile>
|
||||
<PackageName>$(MSBuildProjectName).$(Version).nupkg</PackageName>
|
||||
<SuppressDependenciesWhenPacking>true</SuppressDependenciesWhenPacking>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
|
||||
@ -28,16 +24,28 @@
|
||||
<OutputPath>bin</OutputPath>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
||||
<NoWarn>1701;1702;EF1001;AD0001</NoWarn>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
|
||||
<NoWarn>1701;1702;EF1001;AD0001</NoWarn>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="7.0.5" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="8.0.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Oqtane.Server\Oqtane.Server.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<Target Name="CopyPackage" AfterTargets="Pack">
|
||||
<Copy SourceFiles="$(OutputPath)..\$(PackageName)" DestinationFiles="..\Oqtane.Server\wwwroot\Packages\$(MSBuildProjectName).nupkg.bak" />
|
||||
<ItemGroup>
|
||||
<SqlServerFiles Include="$(OutputPath)Oqtane.Database.SqlServer.dll;$(OutputPath)Oqtane.Database.SqlServer.pdb;$(OutputPath)Microsoft.EntityFrameworkCore.SqlServer.dll" DestinationPath="..\Oqtane.Server\bin\$(Configuration)\net8.0\%(Filename)%(Extension)" />
|
||||
</ItemGroup>
|
||||
|
||||
<Target Name="PublishProvider" AfterTargets="PostBuildEvent" Inputs="@(SqlServerFiles)" Outputs="@(SqlServerFiles->'%(DestinationPath)')">
|
||||
<Copy SourceFiles="@(SqlServerFiles)" DestinationFiles="@(SqlServerFiles->'%(DestinationPath)')" />
|
||||
</Target>
|
||||
|
||||
</Project>
|
||||
|
@ -1,25 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
|
||||
<metadata>
|
||||
<id>Oqtane.Database.SqlServer</id>
|
||||
<version>4.0.5</version>
|
||||
<authors>Shaun Walker</authors>
|
||||
<owners>.NET Foundation</owners>
|
||||
<title>Oqtane SQL Server Provider</title>
|
||||
<summary>SQL Server database support for the Oqtane Framework</summary>
|
||||
<description>SQL Server database support for the Oqtane Framework</description>
|
||||
<copyright>.NET Foundation</copyright>
|
||||
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
||||
<license type="expression">MIT</license>
|
||||
<projectUrl>https://github.com/oqtane/oqtane.framework</projectUrl>
|
||||
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v4.0.5</releaseNotes>
|
||||
<icon>icon.png</icon>
|
||||
<tags>oqtane</tags>
|
||||
</metadata>
|
||||
<files>
|
||||
<file src="bin\net7.0\Oqtane.Database.SqlServer.dll" target="lib\net7.0" />
|
||||
<file src="bin\net7.0\Oqtane.Database.SqlServer.pdb" target="lib\net7.0" />
|
||||
<file src="bin\net7.0\Microsoft.EntityFrameworkCore.SqlServer.dll" target="lib\net7.0" />
|
||||
<file src="icon.png" target="" />
|
||||
</files>
|
||||
</package>
|
@ -1,13 +1,9 @@
|
||||
using System;
|
||||
using System.Text;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Metadata.Builders;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Microsoft.EntityFrameworkCore.SqlServer.Migrations.Internal;
|
||||
using Microsoft.EntityFrameworkCore.Storage;
|
||||
using Oqtane.Migrations.Framework;
|
||||
using Oqtane.Models;
|
||||
using Oqtane.Shared;
|
||||
|
||||
// ReSharper disable ClassNeverInstantiated.Global
|
||||
|
||||
|
@ -6,7 +6,6 @@ using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Microsoft.EntityFrameworkCore.Migrations.Operations;
|
||||
using Microsoft.EntityFrameworkCore.Migrations.Operations.Builders;
|
||||
using Oqtane.Databases;
|
||||
using Oqtane.Shared;
|
||||
|
||||
namespace Oqtane.Database.SqlServer
|
||||
{
|
||||
|
Binary file not shown.
Before Width: | Height: | Size: 4.8 KiB |
@ -1,8 +1,8 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<Version>4.0.5</Version>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<Version>5.0.1</Version>
|
||||
<Product>Oqtane</Product>
|
||||
<Authors>Shaun Walker</Authors>
|
||||
<Company>.NET Foundation</Company>
|
||||
@ -10,14 +10,10 @@
|
||||
<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/v4.0.5</PackageReleaseNotes>
|
||||
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v5.0.1</PackageReleaseNotes>
|
||||
<RepositoryUrl>https://github.com/oqtane/oqtane.framework</RepositoryUrl>
|
||||
<RepositoryType>Git</RepositoryType>
|
||||
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
|
||||
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
|
||||
<NuspecFile>$(MSBuildProjectName).nuspec</NuspecFile>
|
||||
<PackageName>$(MSBuildProjectName).$(Version).nupkg</PackageName>
|
||||
<SuppressDependenciesWhenPacking>true</SuppressDependenciesWhenPacking>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
|
||||
@ -28,16 +24,28 @@
|
||||
<OutputPath>bin</OutputPath>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
||||
<NoWarn>1701;1702;EF1001;AD0001</NoWarn>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
|
||||
<NoWarn>1701;1702;EF1001;AD0001</NoWarn>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="7.0.5" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="8.0.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Oqtane.Server\Oqtane.Server.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<Target Name="CopyPackage" AfterTargets="Pack">
|
||||
<Copy SourceFiles="$(OutputPath)..\$(PackageName)" DestinationFiles="..\Oqtane.Server\wwwroot\Packages\$(MSBuildProjectName).nupkg.bak" />
|
||||
<ItemGroup>
|
||||
<SqliteFiles Include="$(OutputPath)Oqtane.Database.Sqlite.dll;$(OutputPath)Oqtane.Database.Sqlite.pdb;$(OutputPath)Microsoft.EntityFrameworkCore.Sqlite.dll" DestinationPath="..\Oqtane.Server\bin\$(Configuration)\net8.0\%(Filename)%(Extension)" />
|
||||
</ItemGroup>
|
||||
|
||||
<Target Name="PublishProvider" AfterTargets="PostBuildEvent" Inputs="@(SqliteFiles)" Outputs="@(SqliteFiles->'%(DestinationPath)')">
|
||||
<Copy SourceFiles="@(SqliteFiles)" DestinationFiles="@(SqliteFiles->'%(DestinationPath)')" />
|
||||
</Target>
|
||||
|
||||
</Project>
|
||||
|
@ -1,25 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
|
||||
<metadata>
|
||||
<id>Oqtane.Database.Sqlite</id>
|
||||
<version>4.0.5</version>
|
||||
<authors>Shaun Walker</authors>
|
||||
<owners>.NET Foundation</owners>
|
||||
<title>Oqtane SQLite Provider</title>
|
||||
<summary>SQLite database support for the Oqtane Framework</summary>
|
||||
<description>SQLite database support for the Oqtane Framework</description>
|
||||
<copyright>.NET Foundation</copyright>
|
||||
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
||||
<license type="expression">MIT</license>
|
||||
<projectUrl>https://github.com/oqtane/oqtane.framework</projectUrl>
|
||||
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v4.0.5</releaseNotes>
|
||||
<icon>icon.png</icon>
|
||||
<tags>oqtane</tags>
|
||||
</metadata>
|
||||
<files>
|
||||
<file src="bin\net7.0\Oqtane.Database.Sqlite.dll" target="lib\net7.0" />
|
||||
<file src="bin\net7.0\Oqtane.Database.Sqlite.pdb" target="lib\net7.0" />
|
||||
<file src="bin\net7.0\Microsoft.EntityFrameworkCore.Sqlite.dll" target="lib\net7.0" />
|
||||
<file src="icon.png" target="" />
|
||||
</files>
|
||||
</package>
|
@ -1,11 +1,9 @@
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Metadata.Builders;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Microsoft.EntityFrameworkCore.Sqlite.Migrations.Internal;
|
||||
using Oqtane.Migrations.Framework;
|
||||
using Oqtane.Models;
|
||||
using Oqtane.Shared;
|
||||
|
||||
// ReSharper disable ClassNeverInstantiated.Global
|
||||
|
||||
|
@ -6,7 +6,6 @@ using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Microsoft.EntityFrameworkCore.Migrations.Operations;
|
||||
using Microsoft.EntityFrameworkCore.Migrations.Operations.Builders;
|
||||
using Oqtane.Databases;
|
||||
using Oqtane.Shared;
|
||||
|
||||
namespace Oqtane.Database.Sqlite
|
||||
{
|
||||
|
Binary file not shown.
Before Width: | Height: | Size: 4.8 KiB |
@ -1,56 +0,0 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 16
|
||||
VisualStudioVersion = 16.0.28822.285
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{77EECA8C-B58E-469E-B8C5-D543AFC9A654}"
|
||||
ProjectSection(SolutionItems) = preProject
|
||||
.editorconfig = .editorconfig
|
||||
.gitignore = .gitignore
|
||||
README.md = README.md
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Oqtane.Database.MySQL", "Oqtane.Database.MySQL\Oqtane.Database.MySQL.csproj", "{A996FD2D-DAC8-4DFA-92B2-51DF32C6E014}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Oqtane.Database.PostgreSQL", "Oqtane.Database.PostgreSQL\Oqtane.Database.PostgreSQL.csproj", "{3B29B35F-65E7-4819-9AED-EAC7FCFA309B}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Oqtane.Database.Sqlite", "Oqtane.Database.Sqlite\Oqtane.Database.Sqlite.csproj", "{E4F50CA9-19A6-465A-9469-C033748AD95B}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Oqtane.Database.SqlServer", "Oqtane.Database.SqlServer\Oqtane.Database.SqlServer.csproj", "{033DCA37-6354-4A3D-8250-4EC20740EE19}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Oqtane.Server", "Oqtane.Server\Oqtane.Server.csproj", "{6A60C4DD-67E6-42A7-B9AA-A1EE45AD45C7}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Release|Any CPU = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{A996FD2D-DAC8-4DFA-92B2-51DF32C6E014}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{A996FD2D-DAC8-4DFA-92B2-51DF32C6E014}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{A996FD2D-DAC8-4DFA-92B2-51DF32C6E014}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{A996FD2D-DAC8-4DFA-92B2-51DF32C6E014}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{3B29B35F-65E7-4819-9AED-EAC7FCFA309B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{3B29B35F-65E7-4819-9AED-EAC7FCFA309B}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{3B29B35F-65E7-4819-9AED-EAC7FCFA309B}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{3B29B35F-65E7-4819-9AED-EAC7FCFA309B}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{E4F50CA9-19A6-465A-9469-C033748AD95B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{E4F50CA9-19A6-465A-9469-C033748AD95B}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{E4F50CA9-19A6-465A-9469-C033748AD95B}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{E4F50CA9-19A6-465A-9469-C033748AD95B}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{033DCA37-6354-4A3D-8250-4EC20740EE19}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{033DCA37-6354-4A3D-8250-4EC20740EE19}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{033DCA37-6354-4A3D-8250-4EC20740EE19}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{033DCA37-6354-4A3D-8250-4EC20740EE19}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{6A60C4DD-67E6-42A7-B9AA-A1EE45AD45C7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{6A60C4DD-67E6-42A7-B9AA-A1EE45AD45C7}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{6A60C4DD-67E6-42A7-B9AA-A1EE45AD45C7}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{6A60C4DD-67E6-42A7-B9AA-A1EE45AD45C7}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {1FB11796-35DE-4AED-9A52-17733557FCC4}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
@ -1,12 +1,12 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk.Razor">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks Condition="$([MSBuild]::IsOSPlatform('windows'))">$(TargetFrameworks);net7.0-windows10.0.19041.0</TargetFrameworks>
|
||||
<TargetFrameworks Condition="$([MSBuild]::IsOSPlatform('windows'))">$(TargetFrameworks);net8.0-windows10.0.19041.0</TargetFrameworks>
|
||||
<!-- Uncomment to also build the tizen app. You will need to install tizen by following this: https://github.com/Samsung/Tizen.NET -->
|
||||
<!-- <TargetFrameworks>net7.0-android;net7.0-ios;net7.0-maccatalyst</TargetFrameworks> -->
|
||||
<!-- <TargetFrameworks>$(TargetFrameworks);net7.0-tizen</TargetFrameworks> -->
|
||||
<!-- <TargetFrameworks>net8.0-android;net8.0-ios;net8.0-maccatalyst</TargetFrameworks> -->
|
||||
<!-- <TargetFrameworks>$(TargetFrameworks);net8.0-tizen</TargetFrameworks> -->
|
||||
<OutputType>Exe</OutputType>
|
||||
<Version>4.0.5</Version>
|
||||
<Version>5.0.1</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/v4.0.5</PackageReleaseNotes>
|
||||
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v5.0.1</PackageReleaseNotes>
|
||||
<RepositoryUrl>https://github.com/oqtane/oqtane.framework</RepositoryUrl>
|
||||
<RepositoryType>Git</RepositoryType>
|
||||
<RootNamespace>Oqtane.Maui</RootNamespace>
|
||||
@ -31,7 +31,7 @@
|
||||
<ApplicationIdGuid>0E29FC31-1B83-48ED-B6E0-9F3C67B775D4</ApplicationIdGuid>
|
||||
|
||||
<!-- Versions -->
|
||||
<ApplicationDisplayVersion>4.0.5</ApplicationDisplayVersion>
|
||||
<ApplicationDisplayVersion>5.0.1</ApplicationDisplayVersion>
|
||||
<ApplicationVersion>1</ApplicationVersion>
|
||||
|
||||
<SupportedOSPlatformVersion Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'ios'">14.2</SupportedOSPlatformVersion>
|
||||
@ -65,20 +65,22 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.AspNetCore.Components.Authorization" Version="7.0.5" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="7.0.5" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Components.Authorization" Version="8.0.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="8.0.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Localization" Version="2.2.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Http" Version="7.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Localization" Version="7.0.5" />
|
||||
<PackageReference Include="System.Net.Http.Json" Version="7.0.1" />
|
||||
<PackageReference Include="Microsoft.Extensions.Http" Version="8.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Localization" Version="8.0.0" />
|
||||
<PackageReference Include="System.Net.Http.Json" Version="8.0.0" />
|
||||
<PackageReference Include="Microsoft.Maui.Controls" Version="8.0.3" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Components.WebView.Maui" Version="8.0.3" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Reference Include="Oqtane.Client">
|
||||
<HintPath>..\Oqtane.Server\bin\Debug\net7.0\Oqtane.Client.dll</HintPath>
|
||||
<HintPath>..\Oqtane.Server\bin\Debug\net8.0\Oqtane.Client.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Oqtane.Shared">
|
||||
<HintPath>..\Oqtane.Server\bin\Debug\net7.0\Oqtane.Shared.dll</HintPath>
|
||||
<HintPath>..\Oqtane.Server\bin\Debug\net8.0\Oqtane.Shared.dll</HintPath>
|
||||
</Reference>
|
||||
</ItemGroup>
|
||||
|
||||
|
@ -1,8 +1,8 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
|
||||
<metadata>
|
||||
<id>Oqtane.Client</id>
|
||||
<version>4.0.5</version>
|
||||
<version>5.0.1</version>
|
||||
<authors>Shaun Walker</authors>
|
||||
<owners>.NET Foundation</owners>
|
||||
<title>Oqtane Framework</title>
|
||||
@ -12,13 +12,14 @@
|
||||
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
||||
<license type="expression">MIT</license>
|
||||
<projectUrl>https://github.com/oqtane/oqtane.framework</projectUrl>
|
||||
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v4.0.5</releaseNotes>
|
||||
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v5.0.1</releaseNotes>
|
||||
<icon>icon.png</icon>
|
||||
<tags>oqtane</tags>
|
||||
</metadata>
|
||||
<files>
|
||||
<file src="..\Oqtane.Client\bin\Release\net7.0\Oqtane.Client.dll" target="lib\net7.0" />
|
||||
<file src="..\Oqtane.Client\bin\Release\net7.0\Oqtane.Client.pdb" target="lib\net7.0" />
|
||||
<file src="..\Oqtane.Client\bin\Release\net8.0\Oqtane.Client.dll" target="lib\net8.0" />
|
||||
<file src="..\Oqtane.Client\bin\Release\net8.0\Oqtane.Client.pdb" target="lib\net8.0" />
|
||||
<file src="..\Oqtane.Server\bin\Release\net8.0\Oqtane.Licensing.Client.Oqtane.dll" target="lib\net8.0" />
|
||||
<file src="icon.png" target="" />
|
||||
</files>
|
||||
</package>
|
@ -2,7 +2,7 @@
|
||||
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
|
||||
<metadata>
|
||||
<id>Oqtane.Framework</id>
|
||||
<version>4.0.5</version>
|
||||
<version>5.0.1</version>
|
||||
<authors>Shaun Walker</authors>
|
||||
<owners>.NET Foundation</owners>
|
||||
<title>Oqtane Framework</title>
|
||||
@ -11,8 +11,8 @@
|
||||
<copyright>.NET Foundation</copyright>
|
||||
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
||||
<license type="expression">MIT</license>
|
||||
<projectUrl>https://github.com/oqtane/oqtane.framework/releases/download/v4.0.5/Oqtane.Framework.4.0.5.Upgrade.zip</projectUrl>
|
||||
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v4.0.5</releaseNotes>
|
||||
<projectUrl>https://github.com/oqtane/oqtane.framework/releases/download/v5.0.1/Oqtane.Framework.5.0.1.Upgrade.zip</projectUrl>
|
||||
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v5.0.1</releaseNotes>
|
||||
<icon>icon.png</icon>
|
||||
<tags>oqtane framework</tags>
|
||||
</metadata>
|
||||
|
@ -1,8 +1,8 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
|
||||
<metadata>
|
||||
<id>Oqtane.Server</id>
|
||||
<version>4.0.5</version>
|
||||
<version>5.0.1</version>
|
||||
<authors>Shaun Walker</authors>
|
||||
<owners>.NET Foundation</owners>
|
||||
<title>Oqtane Framework</title>
|
||||
@ -12,13 +12,13 @@
|
||||
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
||||
<license type="expression">MIT</license>
|
||||
<projectUrl>https://github.com/oqtane/oqtane.framework</projectUrl>
|
||||
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v4.0.5</releaseNotes>
|
||||
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v5.0.1</releaseNotes>
|
||||
<icon>icon.png</icon>
|
||||
<tags>oqtane</tags>
|
||||
</metadata>
|
||||
<files>
|
||||
<file src="..\Oqtane.Server\bin\Release\net7.0\Oqtane.Server.dll" target="lib\net7.0" />
|
||||
<file src="..\Oqtane.Server\bin\Release\net7.0\Oqtane.Server.pdb" target="lib\net7.0" />
|
||||
<file src="..\Oqtane.Server\bin\Release\net8.0\Oqtane.Server.dll" target="lib\net8.0" />
|
||||
<file src="..\Oqtane.Server\bin\Release\net8.0\Oqtane.Server.pdb" target="lib\net8.0" />
|
||||
<file src="icon.png" target="" />
|
||||
</files>
|
||||
</package>
|
@ -1,8 +1,8 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
|
||||
<metadata>
|
||||
<id>Oqtane.Shared</id>
|
||||
<version>4.0.5</version>
|
||||
<version>5.0.1</version>
|
||||
<authors>Shaun Walker</authors>
|
||||
<owners>.NET Foundation</owners>
|
||||
<title>Oqtane Framework</title>
|
||||
@ -12,13 +12,14 @@
|
||||
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
||||
<license type="expression">MIT</license>
|
||||
<projectUrl>https://github.com/oqtane/oqtane.framework</projectUrl>
|
||||
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v4.0.5</releaseNotes>
|
||||
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v5.0.1</releaseNotes>
|
||||
<icon>icon.png</icon>
|
||||
<tags>oqtane</tags>
|
||||
</metadata>
|
||||
<files>
|
||||
<file src="..\Oqtane.Shared\bin\Release\net7.0\Oqtane.Shared.dll" target="lib\net7.0" />
|
||||
<file src="..\Oqtane.Shared\bin\Release\net7.0\Oqtane.Shared.pdb" target="lib\net7.0" />
|
||||
<file src="..\Oqtane.Shared\bin\Release\net8.0\Oqtane.Shared.dll" target="lib\net8.0" />
|
||||
<file src="..\Oqtane.Shared\bin\Release\net8.0\Oqtane.Shared.pdb" target="lib\net8.0" />
|
||||
<file src="..\Oqtane.Server\bin\Release\net8.0\Oqtane.Licensing.Shared.Oqtane.dll" target="lib\net8.0" />
|
||||
<file src="icon.png" target="" />
|
||||
</files>
|
||||
</package>
|
@ -2,7 +2,7 @@
|
||||
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
|
||||
<metadata>
|
||||
<id>Oqtane.Updater</id>
|
||||
<version>4.0.5</version>
|
||||
<version>5.0.1</version>
|
||||
<authors>Shaun Walker</authors>
|
||||
<owners>.NET Foundation</owners>
|
||||
<title>Oqtane Framework</title>
|
||||
@ -12,12 +12,12 @@
|
||||
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
||||
<license type="expression">MIT</license>
|
||||
<projectUrl>https://github.com/oqtane/oqtane.framework</projectUrl>
|
||||
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v4.0.5</releaseNotes>
|
||||
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v5.0.1</releaseNotes>
|
||||
<icon>icon.png</icon>
|
||||
<tags>oqtane</tags>
|
||||
</metadata>
|
||||
<files>
|
||||
<file src="..\Oqtane.Updater\bin\Release\net7.0\publish\*.*" target="lib\net7.0" />
|
||||
<file src="..\Oqtane.Updater\bin\Release\net8.0\publish\*.*" target="lib\net8.0" />
|
||||
<file src="icon.png" target="" />
|
||||
</files>
|
||||
</package>
|
@ -1 +1 @@
|
||||
Compress-Archive -Path "..\Oqtane.Server\bin\Release\net7.0\publish\*" -DestinationPath "Oqtane.Framework.4.0.5.Install.zip" -Force
|
||||
Compress-Archive -Path "..\Oqtane.Server\bin\Release\net8.0\publish\*" -DestinationPath "Oqtane.Framework.5.0.1.Install.zip" -Force
|
@ -1,21 +1,19 @@
|
||||
del "*.nupkg"
|
||||
del "*.zip"
|
||||
dotnet clean -c Release ..\Oqtane.Databases.sln
|
||||
dotnet clean -c Release ..\Oqtane.sln
|
||||
dotnet build -c Release ..\Oqtane.Databases.sln
|
||||
dotnet build -c Release ..\Oqtane.sln
|
||||
nuget.exe pack Oqtane.Client.nuspec
|
||||
nuget.exe pack Oqtane.Server.nuspec
|
||||
nuget.exe pack Oqtane.Shared.nuspec
|
||||
nuget.exe pack Oqtane.Framework.nuspec
|
||||
del /F/Q/S "..\Oqtane.Server\bin\Release\net7.0\publish" > NUL
|
||||
rmdir /Q/S "..\Oqtane.Server\bin\Release\net7.0\publish"
|
||||
del /F/Q/S "..\Oqtane.Server\bin\Release\net8.0\publish" > NUL
|
||||
rmdir /Q/S "..\Oqtane.Server\bin\Release\net8.0\publish"
|
||||
dotnet publish ..\Oqtane.Server\Oqtane.Server.csproj /p:Configuration=Release
|
||||
del /F/Q/S "..\Oqtane.Server\bin\Release\net7.0\publish\wwwroot\Content" > NUL
|
||||
rmdir /Q/S "..\Oqtane.Server\bin\Release\net7.0\publish\wwwroot\Content"
|
||||
del /F/Q/S "..\Oqtane.Server\bin\Release\net8.0\publish\wwwroot\Content" > NUL
|
||||
rmdir /Q/S "..\Oqtane.Server\bin\Release\net8.0\publish\wwwroot\Content"
|
||||
setlocal ENABLEDELAYEDEXPANSION
|
||||
set retain=Oqtane.Modules.Admin.Login,Oqtane.Modules.HtmlText
|
||||
for /D %%i in ("..\Oqtane.Server\bin\Release\net7.0\publish\wwwroot\Modules\*") do (
|
||||
for /D %%i in ("..\Oqtane.Server\bin\Release\net8.0\publish\wwwroot\Modules\*") do (
|
||||
set /A found=0
|
||||
for %%j in (%retain%) do (
|
||||
if "%%~nxi" == "%%j" set /A found=1
|
||||
@ -23,18 +21,18 @@ if "%%~nxi" == "%%j" set /A found=1
|
||||
if not !found! == 1 rmdir /Q/S "%%i"
|
||||
)
|
||||
set retain=Oqtane.Themes.BlazorTheme,Oqtane.Themes.OqtaneTheme
|
||||
for /D %%i in ("..\Oqtane.Server\bin\Release\net7.0\publish\wwwroot\Themes\*") do (
|
||||
for /D %%i in ("..\Oqtane.Server\bin\Release\net8.0\publish\wwwroot\Themes\*") do (
|
||||
set /A found=0
|
||||
for %%j in (%retain%) do (
|
||||
if "%%~nxi" == "%%j" set /A found=1
|
||||
)
|
||||
if not !found! == 1 rmdir /Q/S "%%i"
|
||||
)
|
||||
del "..\Oqtane.Server\bin\Release\net7.0\publish\appsettings.json"
|
||||
ren "..\Oqtane.Server\bin\Release\net7.0\publish\appsettings.release.json" "appsettings.json"
|
||||
del "..\Oqtane.Server\bin\Release\net8.0\publish\appsettings.json"
|
||||
ren "..\Oqtane.Server\bin\Release\net8.0\publish\appsettings.release.json" "appsettings.json"
|
||||
C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe ".\install.ps1"
|
||||
del "..\Oqtane.Server\bin\Release\net7.0\publish\appsettings.json"
|
||||
del "..\Oqtane.Server\bin\Release\net7.0\publish\web.config"
|
||||
del "..\Oqtane.Server\bin\Release\net8.0\publish\appsettings.json"
|
||||
del "..\Oqtane.Server\bin\Release\net8.0\publish\web.config"
|
||||
C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe ".\upgrade.ps1"
|
||||
dotnet clean -c Release ..\Oqtane.Updater.sln
|
||||
dotnet build -c Release ..\Oqtane.Updater.sln
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user