Compare commits
161 Commits
Author | SHA1 | Date | |
---|---|---|---|
2c56bfd4aa | |||
755615da30 | |||
52af015c2f | |||
4cc0060c67 | |||
eae6d13284 | |||
2a20a7cf21 | |||
27251005ec | |||
90a9b2e5a1 | |||
cd1f12e9b4 | |||
734b9b6458 | |||
a120449c8d | |||
b005a1da56 | |||
afc75a09d9 | |||
c6e6c98875 | |||
fa1a5ab913 | |||
b671b590ad | |||
a46c98eed3 | |||
7ab5ed5bcb | |||
5e609edc31 | |||
ac466429f8 | |||
2804c50c8a | |||
c4315c25bc | |||
e2d5fa48cf | |||
c2375c897d | |||
49f181583b | |||
ea463a6548 | |||
1a567dbdc1 | |||
e4ec10ef49 | |||
3db4fc687a | |||
e136972cd7 | |||
7b1b32e16f | |||
1616f94b86 | |||
7043d1f3bf | |||
7bebfe1919 | |||
d33ded0426 | |||
66aa67581f | |||
3a95c05db9 | |||
67046e9d36 | |||
6f2965a5b0 | |||
197db449a1 | |||
f4800bb7f0 | |||
6a213561f6 | |||
8f8d31f0c9 | |||
f8cfdacc26 | |||
07223e27a4 | |||
4c6f46ad17 | |||
39dc288f9c | |||
467e88ef55 | |||
0965db5d57 | |||
c30339d9b8 | |||
369adcb173 | |||
d837b981d4 | |||
f455461c1e | |||
0087b188a1 | |||
98602f49d8 | |||
70466e5626 | |||
cc7f98a6fe | |||
fd13ad1fca | |||
5077f6fbca | |||
9b15ce6d29 | |||
5a8ca24566 | |||
d3f982cae1 | |||
28b58b9048 | |||
cb10dde97d | |||
2c179867e8 | |||
44fc6de82b | |||
f671af43cd | |||
8c0dc6422e | |||
c91e285475 | |||
642e41530e | |||
b09a3ccdae | |||
a1aab62cea | |||
e8b1aca45c | |||
3de98873d6 | |||
c030ede12c | |||
67f740c264 | |||
9b68337047 | |||
2bae971b92 | |||
c5c5fd859f | |||
15c46fd157 | |||
424950bd3e | |||
39a9968971 | |||
075a09f0df | |||
26e628e189 | |||
7489d9d186 | |||
7db5b6c7f3 | |||
9b7fa8cac2 | |||
5028051a34 | |||
83b3767df6 | |||
6182b96d16 | |||
a719382563 | |||
2aa6eb90e2 | |||
d2495455cd | |||
23d1dd23d1 | |||
573acd6a5d | |||
40ddbbfbb7 | |||
1b488b298b | |||
54b45943db | |||
89b3ae4c74 | |||
fe97a76d00 | |||
78094f69d7 | |||
4499d55464 | |||
4c12ca0607 | |||
1daa9575db | |||
f7c9961f8c | |||
e27a625069 | |||
42b1669cce | |||
74571afc9e | |||
f678f11c59 | |||
e685252b1d | |||
bb75fcb5a3 | |||
7653f36f31 | |||
0670148064 | |||
368b900a6e | |||
6378a78cbd | |||
d3bcdec0f2 | |||
82c074ce2e | |||
a313e2d386 | |||
578cb2f7e3 | |||
c8f56e1659 | |||
56631a3e6b | |||
84ee9de18c | |||
0aeb4e9173 | |||
bb65e5c373 | |||
45e2027c56 | |||
6dc5ef44b7 | |||
e88d3cca07 | |||
a4b7381141 | |||
2ea054dc72 | |||
13ec726ab2 | |||
2e32b65421 | |||
f48ca53cdb | |||
c5b632cb24 | |||
422e9ae99e | |||
68ada8fbe4 | |||
e40bf08691 | |||
a04c7222b2 | |||
172faec5a0 | |||
e01c3e7e4a | |||
7a3d5d0429 | |||
ddf1caaaaa | |||
021293cbb0 | |||
1438e61f1b | |||
182f4dbae7 | |||
26ec3fc7cf | |||
225c758795 | |||
5e653250f3 | |||
b7a3713946 | |||
44242fdb4a | |||
45515b2c06 | |||
2d95fe294c | |||
72cc44641b | |||
9ac3fa5269 | |||
d1ea141165 | |||
dca21fbb8c | |||
06812d5df8 | |||
564410d3cd | |||
4b1cec979a | |||
a5f1bc3895 | |||
b097d031b5 | |||
45df729711 |
2
LICENSE
2
LICENSE
@ -1,6 +1,6 @@
|
|||||||
MIT License
|
MIT License
|
||||||
|
|
||||||
Copyright (c) 2018-2022 .NET Foundation
|
Copyright (c) 2018-2023 .NET Foundation
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
@ -20,13 +20,16 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
@code {
|
@code {
|
||||||
private List<Page> _pages;
|
private List<Page> _pages;
|
||||||
|
|
||||||
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Admin;
|
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Anonymous;
|
||||||
|
|
||||||
protected override void OnInitialized()
|
protected override void OnInitialized()
|
||||||
{
|
{
|
||||||
var admin = PageState.Pages.FirstOrDefault(item => item.Path == "admin");
|
var admin = PageState.Pages.FirstOrDefault(item => item.Path == "admin");
|
||||||
_pages = PageState.Pages.Where(item => item.ParentId == admin?.PageId).ToList();
|
if (admin != null)
|
||||||
|
{
|
||||||
|
_pages = PageState.Pages.Where(item => item.ParentId == admin?.PageId).ToList();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
@inject IStringLocalizer<Add> Localizer
|
@inject IStringLocalizer<Add> Localizer
|
||||||
@inject IStringLocalizer<SharedResources> SharedLocalizer
|
@inject IStringLocalizer<SharedResources> SharedLocalizer
|
||||||
|
|
||||||
@if (_supportedCultures == null)
|
@if (_cultures == null)
|
||||||
{
|
{
|
||||||
<p><em>@SharedLocalizer["Loading"]</em></p>
|
<p><em>@SharedLocalizer["Loading"]</em></p>
|
||||||
}
|
}
|
||||||
@ -17,104 +17,42 @@ else
|
|||||||
{
|
{
|
||||||
<TabStrip>
|
<TabStrip>
|
||||||
<TabPanel Name="Manage" ResourceKey="Manage">
|
<TabPanel Name="Manage" ResourceKey="Manage">
|
||||||
@if (_availableCultures.Count() == 0)
|
<form @ref="form" class="@(validated ? "was-validated" : "needs-validation")" novalidate>
|
||||||
{
|
<div class="container">
|
||||||
<ModuleMessage Type="MessageType.Info" Message="@_message"></ModuleMessage>
|
<div class="row mb-1 align-items-center">
|
||||||
<NavLink class="btn btn-secondary" href="@NavigateUrl()">@SharedLocalizer["Cancel"]</NavLink>
|
<Label Class="col-sm-3" For="translated" HelpText="Specify If You Wish To Select Languages That Have Translations Installed" ResourceKey="Translated">Translated?</Label>
|
||||||
}
|
<div class="col-sm-9">
|
||||||
else
|
<select id="translated" class="form-select" value="@_translated" @onchange="(e => TranslatedChanged(e))" required>
|
||||||
{
|
<option value="True">@SharedLocalizer["Yes"]</option>
|
||||||
<form @ref="form" class="@(validated ? "was-validated" : "needs-validation")" novalidate>
|
<option value="False">@SharedLocalizer["No"]</option>
|
||||||
<div class="container">
|
</select>
|
||||||
<div class="row mb-1 align-items-center">
|
</div>
|
||||||
<Label Class="col-sm-3" For="name" HelpText="Name Of The Language" ResourceKey="Name">Name:</Label>
|
</div>
|
||||||
<div class="col-sm-9">
|
<div class="row mb-1 align-items-center">
|
||||||
<select id="_code" class="form-select" @bind="@_code" required>
|
<Label Class="col-sm-3" For="name" HelpText="Name Of The Language" ResourceKey="Name">Name:</Label>
|
||||||
@foreach (var culture in _availableCultures)
|
<div class="col-sm-9">
|
||||||
{
|
<select id="_code" class="form-select" @bind="@_code" required>
|
||||||
<option value="@culture.Name">@(culture.DisplayName + " (" + culture.Name + ")")</option>
|
<option value="-"><@SharedLocalizer["Not Specified"]></option>
|
||||||
}
|
@foreach (var culture in _cultures)
|
||||||
</select>
|
{
|
||||||
</div>
|
<option value="@culture.Name">@(culture.DisplayName + " (" + culture.Name + ")")</option>
|
||||||
</div>
|
}
|
||||||
<div class="row mb-1 align-items-center">
|
</select>
|
||||||
<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="@_isDefault" required>
|
|
||||||
<option value="True">@SharedLocalizer["Yes"]</option>
|
|
||||||
<option value="False">@SharedLocalizer["No"]</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<button type="button" class="btn btn-success" @onclick="SaveLanguage">@SharedLocalizer["Save"]</button>
|
<div class="row mb-1 align-items-center">
|
||||||
<NavLink class="btn btn-secondary" href="@NavigateUrl()">@SharedLocalizer["Cancel"]</NavLink>
|
<Label Class="col-sm-3" For="default" HelpText="Indicates Whether Or Not This Language Is The Default For The Site" ResourceKey="IsDefault">Default?</Label>
|
||||||
</form>
|
<div class="col-sm-9">
|
||||||
}
|
<select id="default" class="form-select" @bind="@_default" required>
|
||||||
</TabPanel>
|
<option value="True">@SharedLocalizer["Yes"]</option>
|
||||||
<TabPanel Name="Translations" Heading="Translations" ResourceKey="Download" Security="SecurityAccessLevel.Host">
|
<option value="False">@SharedLocalizer["No"]</option>
|
||||||
<div class="row justify-content-center mb-3">
|
</select>
|
||||||
<div class="col-sm-6">
|
</div>
|
||||||
<div class="input-group">
|
|
||||||
<select id="price" class="form-select custom-select" @onchange="(e => PriceChanged(e))">
|
|
||||||
<option value="free">@SharedLocalizer["Free"]</option>
|
|
||||||
<option value="paid">@SharedLocalizer["Paid"]</option>
|
|
||||||
</select>
|
|
||||||
<input id="search" class="form-control" placeholder="@SharedLocalizer["Search.Hint"]" @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>
|
</div>
|
||||||
</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>
|
||||||
@if (_packages != null)
|
</form>
|
||||||
{
|
|
||||||
@if (_packages.Count > 0)
|
|
||||||
{
|
|
||||||
<Pager Items="@_packages">
|
|
||||||
<Row>
|
|
||||||
<td>
|
|
||||||
<h3 style="display: inline;"><a href="@context.ProductUrl" target="_new">@context.Name</a></h3> by: <strong><a href="@context.OwnerUrl" target="new">@context.Owner</a></strong><br />
|
|
||||||
@(context.Description.Length > 400 ? (context.Description.Substring(0, 400) + "...") : context.Description)<br />
|
|
||||||
<strong>@(String.Format("{0:n0}", context.Downloads))</strong> @SharedLocalizer["Search.Downloads"] |
|
|
||||||
@SharedLocalizer["Search.Released"]: <strong>@context.ReleaseDate.ToString("MMM dd, yyyy")</strong> |
|
|
||||||
@SharedLocalizer["Search.Version"]: <strong>@context.Version</strong>
|
|
||||||
@((MarkupString)(!string.IsNullOrEmpty(context.PackageUrl) ? " | " + SharedLocalizer["Search.Source"] + ": <strong>" + new Uri(context.PackageUrl).Host + "</strong>" : ""))
|
|
||||||
@((MarkupString)(context.TrialPeriod > 0 ? " | <strong>" + context.TrialPeriod + " " + @SharedLocalizer["Trial"] + "</strong>" : ""))
|
|
||||||
</td>
|
|
||||||
<td style="width: 1px; vertical-align: middle;">
|
|
||||||
@if (context.Price != null && !string.IsNullOrEmpty(context.PackageUrl))
|
|
||||||
{
|
|
||||||
<button type="button" class="btn btn-primary" @onclick=@(async () => await GetPackage(context.PackageId, context.Version))>@SharedLocalizer["Download"]</button>
|
|
||||||
}
|
|
||||||
</td>
|
|
||||||
<td style="width: 1px; vertical-align: middle;">
|
|
||||||
@if (context.Price != null && !string.IsNullOrEmpty(context.PaymentUrl))
|
|
||||||
{
|
|
||||||
<a class="btn btn-primary" style="text-decoration: none !important" href="@context.PaymentUrl" target="_new">@context.Price.Value.ToString("$#,##0.00")</a>
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
<button type="button" class="btn btn-primary" @onclick=@(async () => await GetPackage(context.PackageId, context.Version))>@SharedLocalizer["Download"]</button>
|
|
||||||
}
|
|
||||||
</td>
|
|
||||||
</Row>
|
|
||||||
</Pager>
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
<br />
|
|
||||||
<div class="mx-auto text-center">
|
|
||||||
@Localizer["Search.NoResults"]
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
<button type="button" class="btn btn-success" @onclick="InstallLanguages">@SharedLocalizer["Install"]</button>
|
|
||||||
<NavLink class="btn btn-secondary" href="@NavigateUrl()">@SharedLocalizer["Cancel"]</NavLink>
|
|
||||||
|
|
||||||
<br />
|
|
||||||
<br />
|
|
||||||
<ModuleMessage Type="MessageType.Info" Message="@SharedLocalizer["Oqtane.Marketplace"]" />
|
|
||||||
}
|
|
||||||
</TabPanel>
|
</TabPanel>
|
||||||
<TabPanel Name="Upload" ResourceKey="Upload" Security="SecurityAccessLevel.Host">
|
<TabPanel Name="Upload" ResourceKey="Upload" Security="SecurityAccessLevel.Host">
|
||||||
<div class="container">
|
<div class="container">
|
||||||
@ -125,216 +63,86 @@ else
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<button type="button" class="btn btn-success" @onclick="InstallLanguages">@SharedLocalizer["Install"]</button>
|
<button type="button" class="btn btn-success" @onclick="InstallTranslations">@SharedLocalizer["Install"]</button>
|
||||||
<NavLink class="btn btn-secondary" href="@NavigateUrl()">@SharedLocalizer["Cancel"]</NavLink>
|
<NavLink class="btn btn-secondary" href="@NavigateUrl()">@SharedLocalizer["Cancel"]</NavLink>
|
||||||
</TabPanel>
|
</TabPanel>
|
||||||
</TabStrip>
|
</TabStrip>
|
||||||
}
|
}
|
||||||
|
|
||||||
@if (_productname != "")
|
|
||||||
{
|
|
||||||
<div class="app-actiondialog">
|
|
||||||
<div class="modal" tabindex="-1" role="dialog">
|
|
||||||
<div class="modal-dialog">
|
|
||||||
<div class="modal-content">
|
|
||||||
<div class="modal-header">
|
|
||||||
<h5 class="modal-title">@SharedLocalizer["Review License Terms"]</h5>
|
|
||||||
<button type="button" class="btn-close" aria-label="Close" @onclick="HideModal"></button>
|
|
||||||
</div>
|
|
||||||
<div class="modal-body">
|
|
||||||
<p style="height: 200px; overflow-y: scroll;">
|
|
||||||
<h3>@_productname</h3>
|
|
||||||
@if (!string.IsNullOrEmpty(_license))
|
|
||||||
{
|
|
||||||
@((MarkupString)_license)
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
@SharedLocalizer["License Not Specified"]
|
|
||||||
}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<div class="modal-footer">
|
|
||||||
<button type="button" class="btn btn-success" @onclick="DownloadPackage">@SharedLocalizer["Accept"]</button>
|
|
||||||
<button type="button" class="btn btn-secondary" @onclick="HideModal">@SharedLocalizer["Cancel"]</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
|
|
||||||
@code {
|
@code {
|
||||||
private ElementReference form;
|
private ElementReference form;
|
||||||
private bool validated = false;
|
private bool validated = false;
|
||||||
|
|
||||||
private string _code = string.Empty;
|
private string _translated = "True";
|
||||||
private string _isDefault = "False";
|
private string _code = "-";
|
||||||
private string _message;
|
private string _default = "False";
|
||||||
private IEnumerable<Culture> _supportedCultures;
|
private List<string> _languages;
|
||||||
private IEnumerable<Culture> _availableCultures;
|
private IEnumerable<Culture> _cultures;
|
||||||
private List<Package> _packages;
|
|
||||||
private string _price = "free";
|
|
||||||
private string _search = "";
|
|
||||||
private string _productname = "";
|
|
||||||
private string _license = "";
|
|
||||||
private string _packageid = "";
|
|
||||||
private string _version = "";
|
|
||||||
|
|
||||||
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Admin;
|
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Admin;
|
||||||
|
|
||||||
protected override async Task OnParametersSetAsync()
|
protected override async Task OnParametersSetAsync()
|
||||||
{
|
{
|
||||||
var languages = await LanguageService.GetLanguagesAsync(PageState.Site.SiteId);
|
var languages = await LanguageService.GetLanguagesAsync(PageState.Site.SiteId);
|
||||||
var languagesCodes = languages.Select(l => l.Code).ToList();
|
_languages = languages.Select(l => l.Code).ToList();
|
||||||
|
|
||||||
_supportedCultures = await LocalizationService.GetCulturesAsync();
|
await LoadCultures();
|
||||||
_availableCultures = _supportedCultures.Where(c => !c.Name.Equals(Constants.DefaultCulture) && !languagesCodes.Contains(c.Name));
|
}
|
||||||
await LoadTranslations();
|
|
||||||
|
|
||||||
if (_supportedCultures.Count() == 1)
|
private async Task LoadCultures()
|
||||||
{
|
{
|
||||||
_message = Localizer["OnlyEnglish"];
|
_cultures = await LocalizationService.GetCulturesAsync(bool.Parse(_translated));
|
||||||
}
|
_cultures = _cultures.Where(c => !c.Name.Equals(Constants.DefaultCulture) && !_languages.Contains(c.Name));
|
||||||
else if (_availableCultures.Count() == 0)
|
_code = "-";
|
||||||
{
|
}
|
||||||
_message = Localizer["AllLanguages"];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task LoadTranslations()
|
private async void TranslatedChanged(ChangeEventArgs e)
|
||||||
{
|
{
|
||||||
_packages = await PackageService.GetPackagesAsync("translation", _search, _price, "");
|
_translated = (string)e.Value;
|
||||||
}
|
await LoadCultures();
|
||||||
|
StateHasChanged();
|
||||||
|
}
|
||||||
|
|
||||||
private async void PriceChanged(ChangeEventArgs e)
|
private async Task SaveLanguage()
|
||||||
{
|
{
|
||||||
try
|
validated = true;
|
||||||
{
|
var interop = new Interop(JSRuntime);
|
||||||
_price = (string)e.Value;
|
if (await interop.FormValid(form) && _code != "-")
|
||||||
_search = "";
|
{
|
||||||
await LoadTranslations();
|
var language = new Language
|
||||||
StateHasChanged();
|
{
|
||||||
}
|
SiteId = PageState.Page.SiteId,
|
||||||
catch (Exception ex)
|
Name = CultureInfo.GetCultureInfo(_code).DisplayName,
|
||||||
{
|
Code = _code,
|
||||||
await logger.LogError(ex, "Error On PriceChanged");
|
IsDefault = (_default == null ? false : Boolean.Parse(_default))
|
||||||
}
|
};
|
||||||
}
|
|
||||||
|
|
||||||
private async Task Search()
|
try
|
||||||
{
|
{
|
||||||
try
|
language = await LanguageService.AddLanguageAsync(language);
|
||||||
{
|
|
||||||
await LoadTranslations();
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
await logger.LogError(ex, "Error On Search");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task Reset()
|
if (language.IsDefault)
|
||||||
{
|
{
|
||||||
try
|
await SetCultureAsync(language.Code);
|
||||||
{
|
}
|
||||||
_search = "";
|
|
||||||
await LoadTranslations();
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
await logger.LogError(ex, "Error On Reset");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task SaveLanguage()
|
await logger.LogInformation("Language Added {Language}", language);
|
||||||
{
|
|
||||||
validated = true;
|
|
||||||
var interop = new Interop(JSRuntime);
|
|
||||||
if (await interop.FormValid(form))
|
|
||||||
{
|
|
||||||
var language = new Language
|
|
||||||
{
|
|
||||||
SiteId = PageState.Page.SiteId,
|
|
||||||
Name = CultureInfo.GetCultureInfo(_code).DisplayName,
|
|
||||||
Code = _code,
|
|
||||||
IsDefault = (_isDefault == null ? false : Boolean.Parse(_isDefault))
|
|
||||||
};
|
|
||||||
|
|
||||||
try
|
NavigationManager.NavigateTo(NavigateUrl());
|
||||||
{
|
}
|
||||||
language = await LanguageService.AddLanguageAsync(language);
|
catch (Exception ex)
|
||||||
|
{
|
||||||
if (language.IsDefault)
|
await logger.LogError(ex, "Error Adding Language {Language} {Error}", language, ex.Message);
|
||||||
{
|
AddModuleMessage(Localizer["Error.Language.Add"], MessageType.Error);
|
||||||
await SetCultureAsync(language.Code);
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
await logger.LogInformation("Language Added {Language}", language);
|
|
||||||
|
|
||||||
NavigationManager.NavigateTo(NavigateUrl());
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
await logger.LogError(ex, "Error Adding Language {Language} {Error}", language, ex.Message);
|
|
||||||
AddModuleMessage(Localizer["Error.Language.Add"], MessageType.Error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
AddModuleMessage(SharedLocalizer["Message.InfoRequired"], MessageType.Warning);
|
AddModuleMessage(SharedLocalizer["Message.InfoRequired"], MessageType.Warning);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async Task InstallTranslations()
|
||||||
private void HideModal()
|
|
||||||
{
|
|
||||||
_productname = "";
|
|
||||||
_license = "";
|
|
||||||
StateHasChanged();
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task GetPackage(string packageid, string version)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var package = await PackageService.GetPackageAsync(packageid, version);
|
|
||||||
if (package != null)
|
|
||||||
{
|
|
||||||
_productname = package.Name;
|
|
||||||
if (!string.IsNullOrEmpty(package.License))
|
|
||||||
{
|
|
||||||
_license = package.License.Replace("\n", "<br />");
|
|
||||||
}
|
|
||||||
_packageid = package.PackageId;
|
|
||||||
_version = package.Version;
|
|
||||||
}
|
|
||||||
StateHasChanged();
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
await logger.LogError(ex, "Error Getting Package {PackageId} {Version}", packageid, version);
|
|
||||||
AddModuleMessage(Localizer["Error.Module.Download"], MessageType.Error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task DownloadPackage()
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
await PackageService.DownloadPackageAsync(_packageid, _version, Constants.PackagesFolder);
|
|
||||||
await logger.LogInformation("Language Package {Name} {Version} Downloaded Successfully", _packageid, _version);
|
|
||||||
AddModuleMessage(Localizer["Success.Language.Download"], MessageType.Success);
|
|
||||||
StateHasChanged();
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
await logger.LogError(ex, "Error Downloading Translation {Name} {Version}", _packageid, _version);
|
|
||||||
AddModuleMessage(Localizer["Error.Language.Download"], MessageType.Error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task InstallLanguages()
|
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
@ -19,94 +19,198 @@ else
|
|||||||
<th style="width: 1px;"> </th>
|
<th style="width: 1px;"> </th>
|
||||||
<th>@SharedLocalizer["Name"]</th>
|
<th>@SharedLocalizer["Name"]</th>
|
||||||
<th>@Localizer["Code"]</th>
|
<th>@Localizer["Code"]</th>
|
||||||
<th>@Localizer["Translation"]</th>
|
|
||||||
<th>@Localizer["Default"]</th>
|
<th>@Localizer["Default"]</th>
|
||||||
<th style="width: 1px;"> </th>
|
@if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host))
|
||||||
|
{
|
||||||
|
<th style="width: 1px;">@Localizer["Translation"]</th>
|
||||||
|
<th style="width: 1px;"> </th>
|
||||||
|
}
|
||||||
</Header>
|
</Header>
|
||||||
<Row>
|
<Row>
|
||||||
<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><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.Name</td>
|
||||||
<td>@context.Code</td>
|
<td>@context.Code</td>
|
||||||
<td>@context.Version</td>
|
|
||||||
<td><TriStateCheckBox Value="@(context.IsDefault)" Disabled="true"></TriStateCheckBox></td>
|
<td><TriStateCheckBox Value="@(context.IsDefault)" Disabled="true"></TriStateCheckBox></td>
|
||||||
<td>
|
@if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host))
|
||||||
@if (UpgradeAvailable(context.Code, context.Version))
|
{
|
||||||
{
|
<td>@((string.IsNullOrEmpty(context.Version)) ? "---" : context.Version)</td>
|
||||||
<button type="button" class="btn btn-success" @onclick=@(async () => await DownloadLanguage(context.Code))>@SharedLocalizer["Upgrade"]</button>
|
<td>
|
||||||
}
|
@switch (TranslationAvailable(context.Code, context.Version))
|
||||||
</td>
|
{
|
||||||
|
case "install":
|
||||||
|
<button type="button" class="btn btn-success" @onclick=@(async () => await GetPackage(context.Code))>@SharedLocalizer["Download"]</button>
|
||||||
|
break;
|
||||||
|
case "upgrade":
|
||||||
|
<button type="button" class="btn btn-success" @onclick=@(async () => await GetPackage(context.Code))>@SharedLocalizer["Upgrade"]</button>
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
</td>
|
||||||
|
}
|
||||||
</Row>
|
</Row>
|
||||||
</Pager>
|
</Pager>
|
||||||
|
@if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host) && _install)
|
||||||
|
{
|
||||||
|
<button type="button" class="btn btn-success" @onclick="InstallTranslations">@SharedLocalizer["Install"]</button>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@if (_package != null)
|
||||||
|
{
|
||||||
|
<div class="app-actiondialog">
|
||||||
|
<div class="modal" tabindex="-1" role="dialog">
|
||||||
|
<div class="modal-dialog">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h5 class="modal-title">@SharedLocalizer["Review License Terms"]</h5>
|
||||||
|
<button type="button" class="btn-close" aria-label="Close" @onclick="HideModal"></button>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<p style="height: 200px; overflow-y: scroll;">
|
||||||
|
<h4 style="display: inline;"><a href="@_package.ProductUrl" target="_new">@_package.Name</a></h4><br />
|
||||||
|
@SharedLocalizer["Search.By"]: <strong><a href="@_package.OwnerUrl" target="new">@_package.Owner</a></strong><br />
|
||||||
|
@(_package.Description.Length > 400 ? (_package.Description.Substring(0, 400) + "...") : _package.Description)<br />
|
||||||
|
<strong>@(String.Format("{0:n0}", _package.Downloads))</strong> @SharedLocalizer["Search.Downloads"] |
|
||||||
|
@SharedLocalizer["Search.Released"]: <strong>@_package.ReleaseDate.ToString("MMM dd, yyyy")</strong> |
|
||||||
|
@SharedLocalizer["Search.Version"]: <strong>@_package.Version</strong>
|
||||||
|
@((MarkupString)(!string.IsNullOrEmpty(_package.PackageUrl) ? " | " + SharedLocalizer["Search.Source"] + ": <strong>" + new Uri(_package.PackageUrl).Host + "</strong>" : ""))
|
||||||
|
<br /><br />
|
||||||
|
@if (!string.IsNullOrEmpty(_package.License))
|
||||||
|
{
|
||||||
|
@((MarkupString)_package.License.Replace("\n", "<br />"))
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
@SharedLocalizer["License Not Specified"]
|
||||||
|
}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button type="button" class="btn btn-success" @onclick="DownloadPackage">@SharedLocalizer["Accept"]</button>
|
||||||
|
<button type="button" class="btn btn-secondary" @onclick="HideModal">@SharedLocalizer["Cancel"]</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
}
|
}
|
||||||
|
|
||||||
@code {
|
@code {
|
||||||
private List<Language> _languages;
|
private List<Language> _languages;
|
||||||
private List<Package> _packages;
|
private List<Package> _packages;
|
||||||
|
private Package _package;
|
||||||
|
private bool _install = false;
|
||||||
|
|
||||||
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Admin;
|
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Admin;
|
||||||
|
|
||||||
protected override async Task OnParametersSetAsync()
|
protected override async Task OnParametersSetAsync()
|
||||||
{
|
{
|
||||||
_languages = await LanguageService.GetLanguagesAsync(PageState.Site.SiteId, Constants.PackageId);
|
await GetLanguages();
|
||||||
|
|
||||||
var cultures = await LocalizationService.GetCulturesAsync();
|
if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host))
|
||||||
var culture = cultures.First(c => c.Name.Equals(Constants.DefaultCulture));
|
{
|
||||||
|
_packages = await PackageService.GetPackagesAsync("translation");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host))
|
private async Task GetLanguages()
|
||||||
{
|
{
|
||||||
_packages = await PackageService.GetPackagesAsync("translation");
|
_languages = await LanguageService.GetLanguagesAsync(PageState.Site.SiteId, Constants.ClientId);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
private async Task DeleteLanguage(Language language)
|
private async Task DeleteLanguage(Language language)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
await LanguageService.DeleteLanguageAsync(language.LanguageId);
|
await LanguageService.DeleteLanguageAsync(language.LanguageId);
|
||||||
await logger.LogInformation("Language Deleted {Language}", language);
|
await logger.LogInformation("Language Deleted {Language}", language);
|
||||||
|
await GetLanguages();
|
||||||
|
StateHasChanged();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
await logger.LogError(ex, "Error Deleting Language {Language} {Error}", language, ex.Message);
|
||||||
|
|
||||||
StateHasChanged();
|
AddModuleMessage(Localizer["Error.Language.Delete"], MessageType.Error);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
}
|
||||||
{
|
|
||||||
await logger.LogError(ex, "Error Deleting Language {Language} {Error}", language, ex.Message);
|
|
||||||
|
|
||||||
AddModuleMessage(Localizer["Error.Language.Delete"], MessageType.Error);
|
private string TranslationAvailable(string code, string version)
|
||||||
}
|
{
|
||||||
}
|
if (_packages != null)
|
||||||
|
{
|
||||||
private bool UpgradeAvailable(string code, string version)
|
|
||||||
{
|
|
||||||
var upgradeavailable = false;
|
|
||||||
if (_packages != null)
|
|
||||||
{
|
|
||||||
var package = _packages.Where(item => item.PackageId == (Constants.PackageId + "." + code)).FirstOrDefault();
|
var package = _packages.Where(item => item.PackageId == (Constants.PackageId + "." + code)).FirstOrDefault();
|
||||||
if (package != null)
|
if (package != null)
|
||||||
{
|
{
|
||||||
upgradeavailable = (Version.Parse(package.Version).CompareTo(Version.Parse(Constants.Version)) == 0) &&
|
// package version needs to match current framework version
|
||||||
(Version.Parse(package.Version).CompareTo(Version.Parse(version)) > 0);
|
if (Version.Parse(package.Version).CompareTo(Version.Parse(Constants.Version)) == 0)
|
||||||
}
|
{
|
||||||
|
if (string.IsNullOrEmpty(version))
|
||||||
|
{
|
||||||
|
return "install";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (Version.Parse(package.Version).CompareTo(Version.Parse(version)) > 0)
|
||||||
|
{
|
||||||
|
return "upgrade";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
}
|
private async Task GetPackage(string code)
|
||||||
return upgradeavailable;
|
{
|
||||||
}
|
try
|
||||||
|
{
|
||||||
|
_package = await PackageService.GetPackageAsync(Constants.PackageId + "." + code, Constants.Version);
|
||||||
|
StateHasChanged();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
await logger.LogError(ex, "Error Getting Package {PackageId} {Version}", Constants.PackageId + "." + code, Constants.Version);
|
||||||
|
AddModuleMessage(Localizer["Error.Translation.Download"], MessageType.Error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private async Task DownloadLanguage(string code)
|
private async Task DownloadPackage()
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host))
|
await PackageService.DownloadPackageAsync(_package.PackageId, _package.Version, Constants.PackagesFolder);
|
||||||
{
|
await logger.LogInformation("Language Package {Name} {Version} Downloaded Successfully", _package.PackageId, _package.Version);
|
||||||
await PackageService.DownloadPackageAsync(Constants.PackageId + "." + code, Constants.Version, Constants.PackagesFolder);
|
AddModuleMessage(Localizer["Success.Language.Download"], MessageType.Success);
|
||||||
await logger.LogInformation("Translation Downloaded {Code} {Version}", code, Constants.Version);
|
_package = null;
|
||||||
await PackageService.InstallPackagesAsync();
|
_install = true;
|
||||||
AddModuleMessage(string.Format(Localizer["Success.Language.Install"], NavigateUrl("admin/system")), MessageType.Success);
|
StateHasChanged();
|
||||||
}
|
}
|
||||||
}
|
catch (Exception ex)
|
||||||
catch (Exception ex)
|
{
|
||||||
{
|
await logger.LogError(ex, "Error Downloading Translation {Name} {Version}", _package.PackageId, _package.Version);
|
||||||
await logger.LogError(ex, "Error Downloading Translation {Code} {Version} {Error}", code, Constants.Version, ex.Message);
|
AddModuleMessage(Localizer["Error.Language.Download"], MessageType.Error);
|
||||||
AddModuleMessage(Localizer["Error.Language.Download"], MessageType.Error);
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
private void HideModal()
|
||||||
|
{
|
||||||
|
_package = null;
|
||||||
|
StateHasChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task InstallTranslations()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await PackageService.InstallPackagesAsync();
|
||||||
|
AddModuleMessage(string.Format(Localizer["Success.Translation.Install"], NavigateUrl("admin/system")), MessageType.Success);
|
||||||
|
_install = false;
|
||||||
|
StateHasChanged();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
await logger.LogError(ex, "Error Installing Translations");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -109,7 +109,7 @@ else
|
|||||||
// external link to log item will display Details component
|
// external link to log item will display Details component
|
||||||
if (PageState.QueryString.ContainsKey("id") && int.TryParse(PageState.QueryString["id"], out int id))
|
if (PageState.QueryString.ContainsKey("id") && int.TryParse(PageState.QueryString["id"], out int id))
|
||||||
{
|
{
|
||||||
NavigationManager.NavigateTo(EditUrl(PageState.Page.Path, ModuleState.ModuleId, "Detail", $"id={id}"));
|
NavigationManager.NavigateTo(EditUrl(PageState.Page.Path, ModuleState.ModuleId, "Detail", $"/{id}"));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (UrlParameters.ContainsKey("level"))
|
if (UrlParameters.ContainsKey("level"))
|
||||||
|
@ -131,7 +131,7 @@ else
|
|||||||
moduleDefinition = await ModuleDefinitionService.CreateModuleDefinitionAsync(moduleDefinition);
|
moduleDefinition = await ModuleDefinitionService.CreateModuleDefinitionAsync(moduleDefinition);
|
||||||
|
|
||||||
var settings = await SettingService.GetModuleSettingsAsync(ModuleState.ModuleId);
|
var settings = await SettingService.GetModuleSettingsAsync(ModuleState.ModuleId);
|
||||||
SettingService.SetSetting(settings, "ModuleDefinitionName", moduleDefinition.ModuleDefinitionName);
|
settings = SettingService.SetSetting(settings, "ModuleDefinitionName", moduleDefinition.ModuleDefinitionName);
|
||||||
await SettingService.UpdateModuleSettingsAsync(settings, ModuleState.ModuleId);
|
await SettingService.UpdateModuleSettingsAsync(settings, ModuleState.ModuleId);
|
||||||
|
|
||||||
GetLocation();
|
GetLocation();
|
||||||
|
@ -102,51 +102,56 @@
|
|||||||
<NavLink class="btn btn-secondary" href="@NavigateUrl()">@SharedLocalizer["Cancel"]</NavLink>
|
<NavLink class="btn btn-secondary" href="@NavigateUrl()">@SharedLocalizer["Cancel"]</NavLink>
|
||||||
</TabPanel>
|
</TabPanel>
|
||||||
<TabPanel Name="Translations" ResourceKey="Translations">
|
<TabPanel Name="Translations" ResourceKey="Translations">
|
||||||
@if (_languages != null)
|
@if (_languages != null && _languages.Count > 0)
|
||||||
{
|
{
|
||||||
@if (_languages.Count > 0)
|
<Pager Items="@_languages">
|
||||||
|
<Header>
|
||||||
|
<th>@SharedLocalizer["Name"]</th>
|
||||||
|
<th>@Localizer["Code"]</th>
|
||||||
|
<th style="width: 1px;">@Localizer["Version"]</th>
|
||||||
|
<th style="width: 1px;"> </th>
|
||||||
|
</Header>
|
||||||
|
<Row>
|
||||||
|
<td>@context.Name</td>
|
||||||
|
<td>@context.Code</td>
|
||||||
|
<td>@((string.IsNullOrEmpty(context.Version)) ? "---" : context.Version)</td>
|
||||||
|
<td>
|
||||||
|
@switch (TranslationAvailable(_packagename + "." + context.Code, context.Version))
|
||||||
|
{
|
||||||
|
case "install":
|
||||||
|
<button type="button" class="btn btn-success" @onclick=@(async () => await GetPackage(_packagename + "." + context.Code))>@SharedLocalizer["Download"]</button>
|
||||||
|
break;
|
||||||
|
case "upgrade":
|
||||||
|
<button type="button" class="btn btn-success" @onclick=@(async () => await GetPackage(_packagename + "." + context.Code))>@SharedLocalizer["Upgrade"]</button>
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
</td>
|
||||||
|
</Row>
|
||||||
|
</Pager>
|
||||||
|
@if (_install)
|
||||||
{
|
{
|
||||||
<Pager Items="@_languages">
|
|
||||||
<Header>
|
|
||||||
<th>@SharedLocalizer["Name"]</th>
|
|
||||||
<th>@Localizer["Code"]</th>
|
|
||||||
<th>@Localizer["Version"]</th>
|
|
||||||
<th style="width: 1px;"> </th>
|
|
||||||
</Header>
|
|
||||||
<Row>
|
|
||||||
<td>@context.Name</td>
|
|
||||||
<td>@context.Code</td>
|
|
||||||
<td>@context.Version</td>
|
|
||||||
<td>
|
|
||||||
@if (context.IsDefault)
|
|
||||||
{
|
|
||||||
<button type="button" class="btn btn-primary" @onclick=@(async () => await GetPackage(_packagename + "." + context.Code))>@SharedLocalizer["Download"]</button>
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (UpgradeAvailable(_packagename + "." + context.Code, context.Version))
|
|
||||||
{
|
|
||||||
<button type="button" class="btn btn-primary" @onclick=@(async () => await DownloadPackage(_packagename + "." + context.Code))>@SharedLocalizer["Upgrade"]</button>
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</td>
|
|
||||||
</Row>
|
|
||||||
</Pager>
|
|
||||||
<button type="button" class="btn btn-success" @onclick="InstallTranslations">@SharedLocalizer["Install"]</button>
|
<button type="button" class="btn btn-success" @onclick="InstallTranslations">@SharedLocalizer["Install"]</button>
|
||||||
}
|
}
|
||||||
else
|
}
|
||||||
{
|
else
|
||||||
<br />
|
{
|
||||||
<div class="mx-auto text-center">
|
<br />
|
||||||
@Localizer["Search.NoResults"]
|
<div class="mx-auto text-center">
|
||||||
</div>
|
@if (string.IsNullOrEmpty(_packagename))
|
||||||
<br />
|
{
|
||||||
}
|
@Localizer["Search.PackageNameMissing"]
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
@Localizer["Search.NoResults"]
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
<br />
|
||||||
}
|
}
|
||||||
</TabPanel>
|
</TabPanel>
|
||||||
</TabStrip>
|
</TabStrip>
|
||||||
|
|
||||||
@if (_productname != "")
|
@if (_package != null)
|
||||||
{
|
{
|
||||||
<div class="app-actiondialog">
|
<div class="app-actiondialog">
|
||||||
<div class="modal" tabindex="-1" role="dialog">
|
<div class="modal" tabindex="-1" role="dialog">
|
||||||
@ -158,10 +163,17 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
<p style="height: 200px; overflow-y: scroll;">
|
<p style="height: 200px; overflow-y: scroll;">
|
||||||
<h3>@_productname</h3>
|
<h4 style="display: inline;"><a href="@_package.ProductUrl" target="_new">@_package.Name</a></h4><br />
|
||||||
@if (!string.IsNullOrEmpty(_packagelicense))
|
@SharedLocalizer["Search.By"]: <strong><a href="@_package.OwnerUrl" target="new">@_package.Owner</a></strong><br />
|
||||||
|
@(_package.Description.Length > 400 ? (_package.Description.Substring(0, 400) + "...") : _package.Description)<br />
|
||||||
|
<strong>@(String.Format("{0:n0}", _package.Downloads))</strong> @SharedLocalizer["Search.Downloads"] |
|
||||||
|
@SharedLocalizer["Search.Released"]: <strong>@_package.ReleaseDate.ToString("MMM dd, yyyy")</strong> |
|
||||||
|
@SharedLocalizer["Search.Version"]: <strong>@_package.Version</strong>
|
||||||
|
@((MarkupString)(!string.IsNullOrEmpty(_package.PackageUrl) ? " | " + SharedLocalizer["Search.Source"] + ": <strong>" + new Uri(_package.PackageUrl).Host + "</strong>" : ""))
|
||||||
|
<br /><br />
|
||||||
|
@if (!string.IsNullOrEmpty(_package.License))
|
||||||
{
|
{
|
||||||
@((MarkupString)_packagelicense)
|
@((MarkupString)_package.License.Replace("\n", "<br />"))
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -170,7 +182,7 @@
|
|||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-footer">
|
<div class="modal-footer">
|
||||||
<button type="button" class="btn btn-success" @onclick=@(async () => await DownloadPackage(_packageid))>@SharedLocalizer["Accept"]</button>
|
<button type="button" class="btn btn-success" @onclick="DownloadPackage">@SharedLocalizer["Accept"]</button>
|
||||||
<button type="button" class="btn btn-secondary" @onclick="HideModal">@SharedLocalizer["Cancel"]</button>
|
<button type="button" class="btn btn-secondary" @onclick="HideModal">@SharedLocalizer["Cancel"]</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -206,9 +218,8 @@
|
|||||||
|
|
||||||
private List<Package> _packages;
|
private List<Package> _packages;
|
||||||
private List<Language> _languages;
|
private List<Language> _languages;
|
||||||
private string _productname = "";
|
private Package _package;
|
||||||
private string _packagelicense = "";
|
private bool _install = false;
|
||||||
private string _packageid = "";
|
|
||||||
|
|
||||||
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Admin;
|
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Admin;
|
||||||
|
|
||||||
@ -237,17 +248,20 @@
|
|||||||
_modifiedby = moduleDefinition.ModifiedBy;
|
_modifiedby = moduleDefinition.ModifiedBy;
|
||||||
_modifiedon = moduleDefinition.ModifiedOn;
|
_modifiedon = moduleDefinition.ModifiedOn;
|
||||||
|
|
||||||
_packages = await PackageService.GetPackagesAsync("translation", "", "", _packagename);
|
if (!string.IsNullOrEmpty(_packagename))
|
||||||
_languages = await LanguageService.GetLanguagesAsync(-1, _packagename);
|
|
||||||
foreach (var package in _packages)
|
|
||||||
{
|
{
|
||||||
var code = package.PackageId.Split('.').Last();
|
_packages = await PackageService.GetPackagesAsync("translation", "", "", _packagename);
|
||||||
if (!_languages.Any(item => item.Code == code))
|
_languages = await LanguageService.GetLanguagesAsync(-1, _packagename);
|
||||||
|
foreach (var package in _packages)
|
||||||
{
|
{
|
||||||
_languages.Add(new Language { Code = code, Name = CultureInfo.GetCultureInfo(code).DisplayName, Version = package.Version, IsDefault = true });
|
var code = package.PackageId.Split('.').Last();
|
||||||
|
if (!_languages.Any(item => item.Code == code))
|
||||||
|
{
|
||||||
|
_languages.Add(new Language { Code = code, Name = CultureInfo.GetCultureInfo(code).DisplayName, Version = package.Version, IsDefault = true });
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
_languages = _languages.OrderBy(item => item.Name).ToList();
|
||||||
}
|
}
|
||||||
_languages = _languages.OrderBy(item => item.Name).ToList();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
@ -297,24 +311,31 @@
|
|||||||
|
|
||||||
private void HideModal()
|
private void HideModal()
|
||||||
{
|
{
|
||||||
_productname = "";
|
_package = null;
|
||||||
_packagelicense = "";
|
|
||||||
StateHasChanged();
|
StateHasChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool UpgradeAvailable(string packagename, string version)
|
private string TranslationAvailable(string packagename, string version)
|
||||||
{
|
{
|
||||||
var upgradeavailable = false;
|
|
||||||
if (_packages != null)
|
if (_packages != null)
|
||||||
{
|
{
|
||||||
var package = _packages.Where(item => item.PackageId == packagename).FirstOrDefault();
|
var package = _packages.Where(item => item.PackageId == packagename).FirstOrDefault();
|
||||||
if (package != null)
|
if (package != null)
|
||||||
{
|
{
|
||||||
upgradeavailable = (Version.Parse(package.Version).CompareTo(Version.Parse(version)) > 0);
|
if (string.IsNullOrEmpty(version))
|
||||||
|
{
|
||||||
|
return "install";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (Version.Parse(package.Version).CompareTo(Version.Parse(version)) > 0)
|
||||||
|
{
|
||||||
|
return "upgrade";
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
return upgradeavailable;
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task GetPackage(string packagename)
|
private async Task GetPackage(string packagename)
|
||||||
@ -322,16 +343,7 @@
|
|||||||
var version = _packages.Where(item => item.PackageId == packagename).FirstOrDefault().Version;
|
var version = _packages.Where(item => item.PackageId == packagename).FirstOrDefault().Version;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var package = await PackageService.GetPackageAsync(packagename, version);
|
_package = await PackageService.GetPackageAsync(packagename, version);
|
||||||
if (package != null)
|
|
||||||
{
|
|
||||||
_productname = package.Name;
|
|
||||||
if (!string.IsNullOrEmpty(package.License))
|
|
||||||
{
|
|
||||||
_packagelicense = package.License.Replace("\n", "<br />");
|
|
||||||
}
|
|
||||||
_packageid = package.PackageId;
|
|
||||||
}
|
|
||||||
StateHasChanged();
|
StateHasChanged();
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
@ -341,16 +353,15 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task DownloadPackage(string packagename)
|
private async Task DownloadPackage()
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var version = _packages.Where(item => item.PackageId == packagename).FirstOrDefault().Version;
|
await PackageService.DownloadPackageAsync(_package.PackageId, _package.Version, Constants.PackagesFolder);
|
||||||
await PackageService.DownloadPackageAsync(packagename, version, Constants.PackagesFolder);
|
await logger.LogInformation("Package {PackageId} {Version} Downloaded Successfully", _package.PackageId, _package.Version);
|
||||||
await logger.LogInformation("Package {PackageId} {Version} Downloaded Successfully", packagename, version);
|
|
||||||
AddModuleMessage(Localizer["Success.Translation.Download"], MessageType.Success);
|
AddModuleMessage(Localizer["Success.Translation.Download"], MessageType.Success);
|
||||||
_productname = "";
|
_package = null;
|
||||||
_packagelicense = "";
|
_install = true;
|
||||||
StateHasChanged();
|
StateHasChanged();
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
@ -366,6 +377,8 @@
|
|||||||
{
|
{
|
||||||
await PackageService.InstallPackagesAsync();
|
await PackageService.InstallPackagesAsync();
|
||||||
AddModuleMessage(string.Format(Localizer["Success.Translation.Install"], NavigateUrl("admin/system")), MessageType.Success);
|
AddModuleMessage(string.Format(Localizer["Success.Translation.Install"], NavigateUrl("admin/system")), MessageType.Success);
|
||||||
|
_install = false;
|
||||||
|
StateHasChanged();
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
|
@ -50,7 +50,7 @@ else
|
|||||||
<Row>
|
<Row>
|
||||||
<td><ActionLink Action="Edit" Parameters="@($"id=" + context.ModuleDefinitionId.ToString())" ResourceKey="EditModule" /></td>
|
<td><ActionLink Action="Edit" Parameters="@($"id=" + context.ModuleDefinitionId.ToString())" ResourceKey="EditModule" /></td>
|
||||||
<td>
|
<td>
|
||||||
@if (context.AssemblyName != "Oqtane.Client")
|
@if (context.AssemblyName != Constants.ClientId)
|
||||||
{
|
{
|
||||||
<ActionDialog Header="Delete Module" Message="@string.Format(Localizer["Confirm.Module.Delete", context.Name])" Action="Delete" Security="SecurityAccessLevel.Host" Class="btn btn-danger" OnClick="@(async () => await DeleteModule(context))" ResourceKey="DeleteModule" />
|
<ActionDialog Header="Delete Module" Message="@string.Format(Localizer["Confirm.Module.Delete", context.Name])" Action="Delete" Security="SecurityAccessLevel.Host" Class="btn btn-danger" OnClick="@(async () => await DeleteModule(context))" ResourceKey="DeleteModule" />
|
||||||
}
|
}
|
||||||
@ -58,7 +58,7 @@ else
|
|||||||
<td>@context.Name</td>
|
<td>@context.Name</td>
|
||||||
<td>@context.Version</td>
|
<td>@context.Version</td>
|
||||||
<td>
|
<td>
|
||||||
@if(context.AssemblyName == "Oqtane.Client" || PageState.Modules.Where(m => m.ModuleDefinition?.ModuleDefinitionId == context.ModuleDefinitionId).FirstOrDefault() != null)
|
@if (context.AssemblyName == Constants.ClientId || PageState.Modules.Where(m => m.ModuleDefinition?.ModuleDefinitionId == context.ModuleDefinitionId).FirstOrDefault() != null)
|
||||||
{
|
{
|
||||||
<span>@SharedLocalizer["Yes"]</span>
|
<span>@SharedLocalizer["Yes"]</span>
|
||||||
}
|
}
|
||||||
|
@ -157,7 +157,8 @@
|
|||||||
</TabPanel>
|
</TabPanel>
|
||||||
}
|
}
|
||||||
</TabStrip>
|
</TabStrip>
|
||||||
<button type="button" class="btn btn-success" @onclick="SavePage">@SharedLocalizer["Save"]</button>
|
<br />
|
||||||
|
<button type="button" class="btn btn-success" @onclick="SavePage">@SharedLocalizer["Save"]</button>
|
||||||
<button type="button" class="btn btn-secondary" @onclick="Cancel">@SharedLocalizer["Cancel"]</button>
|
<button type="button" class="btn btn-secondary" @onclick="Cancel">@SharedLocalizer["Cancel"]</button>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
|
@ -148,7 +148,8 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Section>
|
</Section>
|
||||||
<br /><br />
|
<br />
|
||||||
|
<br />
|
||||||
<AuditInfo CreatedBy="@_createdby" CreatedOn="@_createdon" ModifiedBy="@_modifiedby" ModifiedOn="@_modifiedon" DeletedBy="@_deletedby" DeletedOn="@_deletedon"></AuditInfo>
|
<AuditInfo CreatedBy="@_createdby" CreatedOn="@_createdon" ModifiedBy="@_modifiedby" ModifiedOn="@_modifiedon" DeletedBy="@_deletedby" DeletedOn="@_deletedon"></AuditInfo>
|
||||||
}
|
}
|
||||||
</TabPanel>
|
</TabPanel>
|
||||||
@ -189,7 +190,8 @@
|
|||||||
<br />
|
<br />
|
||||||
}
|
}
|
||||||
</TabStrip>
|
</TabStrip>
|
||||||
<button type="button" class="btn btn-success" @onclick="SavePage">@SharedLocalizer["Save"]</button>
|
<br />
|
||||||
|
<button type="button" class="btn btn-success" @onclick="SavePage">@SharedLocalizer["Save"]</button>
|
||||||
<button type="button" class="btn btn-secondary" @onclick="Cancel">@SharedLocalizer["Cancel"]</button>
|
<button type="button" class="btn btn-secondary" @onclick="Cancel">@SharedLocalizer["Cancel"]</button>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
|
@ -104,7 +104,7 @@
|
|||||||
private string modifiedby;
|
private string modifiedby;
|
||||||
private DateTime modifiedon;
|
private DateTime modifiedon;
|
||||||
|
|
||||||
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Admin;
|
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Edit;
|
||||||
|
|
||||||
public override string Actions => "Add,Edit";
|
public override string Actions => "Add,Edit";
|
||||||
|
|
||||||
|
@ -10,7 +10,7 @@
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
<ActionLink Action="Add" Security="SecurityAccessLevel.Admin" Text="Add Profile" ResourceKey="AddProfile" />
|
<ActionLink Action="Add" Text="Add Profile" Security="SecurityAccessLevel.Edit" ResourceKey="AddProfile" />
|
||||||
|
|
||||||
<Pager Items="@_profiles">
|
<Pager Items="@_profiles">
|
||||||
<Header>
|
<Header>
|
||||||
@ -19,8 +19,8 @@ else
|
|||||||
<th>@SharedLocalizer["Name"]</th>
|
<th>@SharedLocalizer["Name"]</th>
|
||||||
</Header>
|
</Header>
|
||||||
<Row>
|
<Row>
|
||||||
<td><ActionLink Action="Edit" Parameters="@($"id=" + context.ProfileId.ToString())" ResourceKey="EditProfile" /></td>
|
<td><ActionLink Action="Edit" Parameters="@($"id=" + context.ProfileId.ToString())" Security="SecurityAccessLevel.Edit" ResourceKey="EditProfile" /></td>
|
||||||
<td><ActionDialog Header="Delete Profile" Message="@string.Format(Localizer["Confirm.Profile.Delete"], context.Name)" Action="Delete" Class="btn btn-danger" OnClick="@(async () => await DeleteProfile(context.ProfileId))" ResourceKey="DeleteProfile" /></td>
|
<td><ActionDialog Header="Delete Profile" Message="@string.Format(Localizer["Confirm.Profile.Delete"], context.Name)" Action="Delete" Security="SecurityAccessLevel.Edit" Class="btn btn-danger" OnClick="@(async () => await DeleteProfile(context.ProfileId))" ResourceKey="DeleteProfile" /></td>
|
||||||
<td>@context.Name</td>
|
<td>@context.Name</td>
|
||||||
</Row>
|
</Row>
|
||||||
</Pager>
|
</Pager>
|
||||||
@ -29,7 +29,7 @@ else
|
|||||||
@code {
|
@code {
|
||||||
private List<Profile> _profiles;
|
private List<Profile> _profiles;
|
||||||
|
|
||||||
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Admin;
|
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.View;
|
||||||
|
|
||||||
protected override async Task OnParametersSetAsync()
|
protected override async Task OnParametersSetAsync()
|
||||||
{
|
{
|
||||||
|
20
Oqtane.Client/Modules/Admin/Profiles/ModuleInfo.cs
Normal file
20
Oqtane.Client/Modules/Admin/Profiles/ModuleInfo.cs
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
using Oqtane.Documentation;
|
||||||
|
using Oqtane.Models;
|
||||||
|
using Oqtane.Shared;
|
||||||
|
|
||||||
|
namespace Oqtane.Modules.Admin.Profiles
|
||||||
|
{
|
||||||
|
[PrivateApi("Mark this as private, since it's not very useful in the public docs")]
|
||||||
|
public class ModuleInfo : IModule
|
||||||
|
{
|
||||||
|
public ModuleDefinition ModuleDefinition => new ModuleDefinition
|
||||||
|
{
|
||||||
|
Name = "Profiles",
|
||||||
|
Description = "Manage Profiles",
|
||||||
|
Categories = "Admin",
|
||||||
|
Version = Constants.Version,
|
||||||
|
PermissionNames = $"{PermissionNames.View},{PermissionNames.Edit}," +
|
||||||
|
$"{EntityNames.Profile}:{PermissionNames.Write}:{RoleNames.Admin}"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
@ -7,75 +7,77 @@
|
|||||||
@inject IStringLocalizer<Index> Localizer
|
@inject IStringLocalizer<Index> Localizer
|
||||||
@inject IStringLocalizer<SharedResources> SharedLocalizer
|
@inject IStringLocalizer<SharedResources> SharedLocalizer
|
||||||
|
|
||||||
<TabStrip>
|
@if (_pages == null || _modules == null)
|
||||||
<TabPanel Name="Pages" ResourceKey="Pages">
|
{
|
||||||
@if (_pages == null)
|
<p><em>@SharedLocalizer["Loading"]</em></p>
|
||||||
{
|
}
|
||||||
<br />
|
else
|
||||||
<p>@Localizer["NoPage.Deleted"]</p>
|
{
|
||||||
}
|
<TabStrip>
|
||||||
else
|
<TabPanel Name="Pages" ResourceKey="Pages">
|
||||||
{
|
@if (!_pages.Where(item => item.IsDeleted).Any())
|
||||||
<Pager Items="@_pages">
|
{
|
||||||
<Header>
|
<br />
|
||||||
<th style="width: 1px;"> </th>
|
<p>@Localizer["NoPage.Deleted"]</p>
|
||||||
<th style="width: 1px;"> </th>
|
}
|
||||||
<th>@SharedLocalizer["Name"]</th>
|
else
|
||||||
<th>@Localizer["DeletedBy"]</th>
|
{
|
||||||
<th>@Localizer["DeletedOn"]</th>
|
<Pager Items="@_pages.Where(item => item.IsDeleted)">
|
||||||
</Header>
|
<Header>
|
||||||
<Row>
|
<th style="width: 1px;"> </th>
|
||||||
<td><button type="button" @onclick="@(() => RestorePage(context))" class="btn btn-success" title="Restore">Restore</button></td>
|
<th style="width: 1px;"> </th>
|
||||||
<td><ActionDialog Header="Delete Page" Message="@string.Format(Localizer["Confirm.Page.Delete"], context.Name)" Action="Delete" Security="SecurityAccessLevel.Admin" Class="btn btn-danger" OnClick="@(async () => await DeletePage(context))" ResourceKey="DeletePage" /></td>
|
<th>@SharedLocalizer["Name"]</th>
|
||||||
<td>@context.Name</td>
|
<th>@Localizer["DeletedBy"]</th>
|
||||||
<td>@context.DeletedBy</td>
|
<th>@Localizer["DeletedOn"]</th>
|
||||||
<td>@context.DeletedOn</td>
|
</Header>
|
||||||
</Row>
|
<Row>
|
||||||
</Pager>
|
<td><button type="button" @onclick="@(() => RestorePage(context))" class="btn btn-success" title="Restore">Restore</button></td>
|
||||||
@if (_pages.Any())
|
<td><ActionDialog Header="Delete Page" Message="@string.Format(Localizer["Confirm.Page.Delete"], context.Name)" Action="Delete" Security="SecurityAccessLevel.Admin" Class="btn btn-danger" OnClick="@(async () => await DeletePage(context))" ResourceKey="DeletePage" /></td>
|
||||||
{
|
<td>@context.Name</td>
|
||||||
<br /><ActionDialog Header="Delete All Pages" Message="Are You Sure You Wish To Permanently Delete All Pages?" Action="Delete All Pages" Security="SecurityAccessLevel.Admin" Class="btn btn-danger" OnClick="@(async () => await DeleteAllPages())" ResourceKey="DeleteAllPages" />
|
<td>@context.DeletedBy</td>
|
||||||
}
|
<td>@context.DeletedOn</td>
|
||||||
}
|
</Row>
|
||||||
</TabPanel>
|
</Pager>
|
||||||
<TabPanel Name="Modules" ResourceKey="Modules">
|
<br />
|
||||||
@if (_modules == null)
|
<ActionDialog Header="Remove All Deleted Pages" Message="Are You Sure You Wish To Permanently Remove All Deleted Pages?" Action="Remove All Deleted Pages" Security="SecurityAccessLevel.Admin" Class="btn btn-danger" OnClick="@(async () => await DeleteAllPages())" ResourceKey="DeleteAllPages" />
|
||||||
{
|
}
|
||||||
<br />
|
</TabPanel>
|
||||||
<p>@Localizer["NoModule.Deleted"]</p>
|
<TabPanel Name="Modules" ResourceKey="Modules">
|
||||||
}
|
@if (!_modules.Where(item => item.IsDeleted).Any())
|
||||||
else
|
{
|
||||||
{
|
<br />
|
||||||
<Pager Items="@_modules">
|
<p>@Localizer["NoModule.Deleted"]</p>
|
||||||
<Header>
|
}
|
||||||
<th style="width: 1px;"> </th>
|
else
|
||||||
<th style="width: 1px;"> </th>
|
{
|
||||||
<th>@Localizer["Page"]</th>
|
<Pager Items="@_modules.Where(item => item.IsDeleted)">
|
||||||
<th>@Localizer["Module"]</th>
|
<Header>
|
||||||
<th>@Localizer["DeletedBy"]</th>
|
<th style="width: 1px;"> </th>
|
||||||
<th>@Localizer["DeletedOn"]</th>
|
<th style="width: 1px;"> </th>
|
||||||
</Header>
|
<th>@Localizer["Page"]</th>
|
||||||
<Row>
|
<th>@Localizer["Module"]</th>
|
||||||
<td><button type="button" @onclick="@(() => RestoreModule(context))" class="btn btn-success" title="Restore">@Localizer["Restore"]</button></td>
|
<th>@Localizer["DeletedBy"]</th>
|
||||||
<td><ActionDialog Header="Delete Module" Message="@string.Format(Localizer["Confirm.Module.Delete"], context.Title)" Action="Delete" Security="SecurityAccessLevel.Admin" Class="btn btn-danger" OnClick="@(async () => await DeleteModule(context))" ResourceKey="DeleteModule" /></td>
|
<th>@Localizer["DeletedOn"]</th>
|
||||||
<td>@_pages.Find(item => item.PageId == context.PageId).Name</td>
|
</Header>
|
||||||
<td>@context.Title</td>
|
<Row>
|
||||||
<td>@context.DeletedBy</td>
|
<td><button type="button" @onclick="@(() => RestoreModule(context))" class="btn btn-success" title="Restore">@Localizer["Restore"]</button></td>
|
||||||
<td>@context.DeletedOn</td>
|
<td><ActionDialog Header="Delete Module" Message="@string.Format(Localizer["Confirm.Module.Delete"], context.Title)" Action="Delete" Security="SecurityAccessLevel.Admin" Class="btn btn-danger" OnClick="@(async () => await DeleteModule(context))" ResourceKey="DeleteModule" /></td>
|
||||||
</Row>
|
<td>@_pages.Find(item => item.PageId == context.PageId).Name</td>
|
||||||
</Pager>
|
<td>@context.Title</td>
|
||||||
@if (_modules.Any())
|
<td>@context.DeletedBy</td>
|
||||||
{
|
<td>@context.DeletedOn</td>
|
||||||
<br /><ActionDialog Header="Delete All Modules" Message="Are You Sure You Wish To Permanently Delete All Modules?" Action="Delete All Modules" Security="SecurityAccessLevel.Admin" Class="btn btn-danger" OnClick="@(async () => await DeleteAllModules())" ResourceKey="DeleteAllModules" />
|
</Row>
|
||||||
}
|
</Pager>
|
||||||
|
<br />
|
||||||
}
|
<ActionDialog Header="Remove All Deleted Modules" Message="Are You Sure You Wish To Permanently Remove All Deleted Modules?" Action="Remove All Deleted Modules" Security="SecurityAccessLevel.Admin" Class="btn btn-danger" OnClick="@(async () => await DeleteAllModules())" ResourceKey="DeleteAllModules" />
|
||||||
</TabPanel>
|
}
|
||||||
</TabStrip>
|
</TabPanel>
|
||||||
|
</TabStrip>
|
||||||
|
}
|
||||||
|
|
||||||
@code {
|
@code {
|
||||||
private List<Page> _pages;
|
private List<Page> _pages;
|
||||||
private List<Module> _modules;
|
private List<Module> _modules;
|
||||||
|
|
||||||
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Admin;
|
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Admin;
|
||||||
|
|
||||||
@ -95,10 +97,7 @@
|
|||||||
private async Task Load()
|
private async Task Load()
|
||||||
{
|
{
|
||||||
_pages = await PageService.GetPagesAsync(PageState.Site.SiteId);
|
_pages = await PageService.GetPagesAsync(PageState.Site.SiteId);
|
||||||
_pages = _pages.Where(item => item.IsDeleted).ToList();
|
|
||||||
|
|
||||||
_modules = await ModuleService.GetModulesAsync(PageState.Site.SiteId);
|
_modules = await ModuleService.GetModulesAsync(PageState.Site.SiteId);
|
||||||
_modules = _modules.Where(item => item.IsDeleted).ToList();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task RestorePage(Page page)
|
private async Task RestorePage(Page page)
|
||||||
@ -141,7 +140,7 @@
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
ModuleInstance.ShowProgressIndicator();
|
ModuleInstance.ShowProgressIndicator();
|
||||||
foreach (Page page in _pages)
|
foreach (Page page in _pages.Where(item => item.IsDeleted))
|
||||||
{
|
{
|
||||||
await PageService.DeletePageAsync(page.PageId);
|
await PageService.DeletePageAsync(page.PageId);
|
||||||
await logger.LogInformation("Page Permanently Deleted {Page}", page);
|
await logger.LogInformation("Page Permanently Deleted {Page}", page);
|
||||||
@ -184,9 +183,8 @@
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
await PageModuleService.DeletePageModuleAsync(module.PageModuleId);
|
await PageModuleService.DeletePageModuleAsync(module.PageModuleId);
|
||||||
// check if there are any remaining module instances in the site
|
|
||||||
_modules = await ModuleService.GetModulesAsync(PageState.Site.SiteId);
|
|
||||||
|
|
||||||
|
// check if there are any remaining module instances in the site
|
||||||
if (!_modules.Exists(item => item.ModuleId == module.ModuleId))
|
if (!_modules.Exists(item => item.ModuleId == module.ModuleId))
|
||||||
{
|
{
|
||||||
await ModuleService.DeleteModuleAsync(module.ModuleId);
|
await ModuleService.DeleteModuleAsync(module.ModuleId);
|
||||||
@ -208,12 +206,11 @@
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
ModuleInstance.ShowProgressIndicator();
|
ModuleInstance.ShowProgressIndicator();
|
||||||
foreach (Module module in _modules)
|
foreach (Module module in _modules.Where(item => item.IsDeleted))
|
||||||
{
|
{
|
||||||
await PageModuleService.DeletePageModuleAsync(module.PageModuleId);
|
await PageModuleService.DeletePageModuleAsync(module.PageModuleId);
|
||||||
// check if there are any remaining module instances in the site
|
|
||||||
_modules = await ModuleService.GetModulesAsync(PageState.Site.SiteId);
|
|
||||||
|
|
||||||
|
// check if there are any remaining module instances in the site
|
||||||
if (!_modules.Exists(item => item.ModuleId == module.ModuleId))
|
if (!_modules.Exists(item => item.ModuleId == module.ModuleId))
|
||||||
{
|
{
|
||||||
await ModuleService.DeleteModuleAsync(module.ModuleId);
|
await ModuleService.DeleteModuleAsync(module.ModuleId);
|
||||||
|
@ -42,7 +42,7 @@
|
|||||||
private string _description = string.Empty;
|
private string _description = string.Empty;
|
||||||
private string _isautoassigned = "False";
|
private string _isautoassigned = "False";
|
||||||
|
|
||||||
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Admin;
|
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Edit;
|
||||||
|
|
||||||
private async Task SaveRole()
|
private async Task SaveRole()
|
||||||
{
|
{
|
||||||
|
@ -49,7 +49,7 @@
|
|||||||
private string _modifiedby;
|
private string _modifiedby;
|
||||||
private DateTime _modifiedon;
|
private DateTime _modifiedon;
|
||||||
|
|
||||||
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Admin;
|
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Edit;
|
||||||
|
|
||||||
protected override async Task OnInitializedAsync()
|
protected override async Task OnInitializedAsync()
|
||||||
{
|
{
|
||||||
|
@ -10,7 +10,7 @@
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
<ActionLink Action="Add" Text="Add Role" ResourceKey="AddRole" />
|
<ActionLink Action="Add" Text="Add Role" Security="SecurityAccessLevel.Edit" ResourceKey="AddRole" />
|
||||||
|
|
||||||
<Pager Items="@_roles">
|
<Pager Items="@_roles">
|
||||||
<Header>
|
<Header>
|
||||||
@ -20,9 +20,9 @@ else
|
|||||||
<th>@SharedLocalizer["Name"]</th>
|
<th>@SharedLocalizer["Name"]</th>
|
||||||
</Header>
|
</Header>
|
||||||
<Row>
|
<Row>
|
||||||
<td><ActionLink Action="Edit" Parameters="@($"id=" + context.RoleId.ToString())" Disabled="@(context.IsSystem)" ResourceKey="Edit" /></td>
|
<td><ActionLink Action="Edit" Parameters="@($"id=" + context.RoleId.ToString())" Security="SecurityAccessLevel.Edit" Disabled="@(context.IsSystem)" ResourceKey="Edit" /></td>
|
||||||
<td><ActionDialog Header="Delete Role" Message="@string.Format(Localizer["Confirm.DeleteUser"], context.Name)" Action="Delete" Security="SecurityAccessLevel.Admin" Class="btn btn-danger" OnClick="@(async () => await DeleteRole(context))" Disabled="@(context.IsSystem)" ResourceKey="DeleteRole" /></td>
|
<td><ActionDialog Header="Delete Role" Message="@string.Format(Localizer["Confirm.DeleteUser"], context.Name)" Action="Delete" Security="SecurityAccessLevel.Edit" Class="btn btn-danger" OnClick="@(async () => await DeleteRole(context))" Disabled="@(context.IsSystem)" ResourceKey="DeleteRole" /></td>
|
||||||
<td><ActionLink Action="Users" Parameters="@($"id=" + context.RoleId.ToString())" ResourceKey="Users" /></td>
|
<td><ActionLink Action="Users" Parameters="@($"id=" + context.RoleId.ToString())" Security="SecurityAccessLevel.Edit" ResourceKey="Users" /></td>
|
||||||
<td>@context.Name</td>
|
<td>@context.Name</td>
|
||||||
</Row>
|
</Row>
|
||||||
</Pager>
|
</Pager>
|
||||||
@ -31,7 +31,7 @@ else
|
|||||||
@code {
|
@code {
|
||||||
private List<Role> _roles;
|
private List<Role> _roles;
|
||||||
|
|
||||||
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Admin;
|
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.View;
|
||||||
|
|
||||||
protected override async Task OnParametersSetAsync()
|
protected override async Task OnParametersSetAsync()
|
||||||
{
|
{
|
||||||
|
21
Oqtane.Client/Modules/Admin/Roles/ModuleInfo.cs
Normal file
21
Oqtane.Client/Modules/Admin/Roles/ModuleInfo.cs
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
using Oqtane.Documentation;
|
||||||
|
using Oqtane.Models;
|
||||||
|
using Oqtane.Shared;
|
||||||
|
|
||||||
|
namespace Oqtane.Modules.Admin.Roles
|
||||||
|
{
|
||||||
|
[PrivateApi("Mark this as private, since it's not very useful in the public docs")]
|
||||||
|
public class ModuleInfo : IModule
|
||||||
|
{
|
||||||
|
public ModuleDefinition ModuleDefinition => new ModuleDefinition
|
||||||
|
{
|
||||||
|
Name = "Roles",
|
||||||
|
Description = "Manage Roles",
|
||||||
|
Categories = "Admin",
|
||||||
|
Version = Constants.Version,
|
||||||
|
PermissionNames = $"{PermissionNames.View},{PermissionNames.Edit}," +
|
||||||
|
$"{EntityNames.Role}:{PermissionNames.Write}:{RoleNames.Admin}," +
|
||||||
|
$"{EntityNames.UserRole}:{PermissionNames.Write}:{RoleNames.Admin}"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
@ -23,13 +23,7 @@ else
|
|||||||
<div class="row mb-1 align-items-center">
|
<div class="row mb-1 align-items-center">
|
||||||
<Label Class="col-sm-3" For="user" HelpText="Select a user" ResourceKey="User">User: </Label>
|
<Label Class="col-sm-3" For="user" HelpText="Select a user" ResourceKey="User">User: </Label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
<select id="user" class="form-select" @bind="@userid" required>
|
<AutoComplete OnSearch="GetUsers" Placeholder="@Localizer["User.Select"]" @ref="user" />
|
||||||
<option value="-1"><@Localizer["User.Select"]></option>
|
|
||||||
@foreach (UserRole userrole in users)
|
|
||||||
{
|
|
||||||
<option value="@(userrole.UserId)">@userrole.User.DisplayName</option>
|
|
||||||
}
|
|
||||||
</select>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row mb-1 align-items-center">
|
<div class="row mb-1 align-items-center">
|
||||||
@ -64,7 +58,7 @@ else
|
|||||||
<td>@context.EffectiveDate</td>
|
<td>@context.EffectiveDate</td>
|
||||||
<td>@context.ExpiryDate</td>
|
<td>@context.ExpiryDate</td>
|
||||||
<td>
|
<td>
|
||||||
<ActionDialog Header="Remove User" Message="@string.Format(Localizer["Confirm.User.DeleteRole"], context.User.DisplayName)" Action="Delete" Security="SecurityAccessLevel.Admin" Class="btn btn-danger" OnClick="@(async () => await DeleteUserRole(context.UserRoleId))" Disabled="@(context.Role.IsAutoAssigned || PageState.User.Username == UserNames.Host)" ResourceKey="DeleteUserRole" />
|
<ActionDialog Header="Remove User" Message="@string.Format(Localizer["Confirm.User.DeleteRole"], context.User.DisplayName)" Action="Delete" Security="SecurityAccessLevel.Edit" Class="btn btn-danger" OnClick="@(async () => await DeleteUserRole(context.UserRoleId))" Disabled="@(context.Role.IsAutoAssigned || context.User.Username == UserNames.Host || context.User.UserId == PageState.User.UserId)" ResourceKey="DeleteUserRole" />
|
||||||
</td>
|
</td>
|
||||||
</Row>
|
</Row>
|
||||||
</Pager>
|
</Pager>
|
||||||
@ -75,81 +69,96 @@ else
|
|||||||
}
|
}
|
||||||
|
|
||||||
@code {
|
@code {
|
||||||
private ElementReference form;
|
private ElementReference form;
|
||||||
private bool validated = false;
|
private bool validated = false;
|
||||||
|
|
||||||
private int roleid;
|
private int roleid;
|
||||||
private string name = string.Empty;
|
private string name = string.Empty;
|
||||||
private List<UserRole> users;
|
private AutoComplete user;
|
||||||
private int userid = -1;
|
private DateTime? effectivedate = null;
|
||||||
private DateTime? effectivedate = null;
|
private DateTime? expirydate = null;
|
||||||
private DateTime? expirydate = null;
|
private List<UserRole> userroles;
|
||||||
private List<UserRole> userroles;
|
|
||||||
|
|
||||||
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Admin;
|
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Edit;
|
||||||
|
|
||||||
protected override async Task OnInitializedAsync()
|
protected override async Task OnInitializedAsync()
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
roleid = Int32.Parse(PageState.QueryString["id"]);
|
roleid = Int32.Parse(PageState.QueryString["id"]);
|
||||||
Role role = await RoleService.GetRoleAsync(roleid);
|
Role role = await RoleService.GetRoleAsync(roleid);
|
||||||
name = role.Name;
|
name = role.Name;
|
||||||
users = await UserRoleService.GetUserRolesAsync(PageState.Site.SiteId, RoleNames.Registered);
|
await GetUserRoles();
|
||||||
await GetUserRoles();
|
}
|
||||||
}
|
catch (Exception ex)
|
||||||
catch (Exception ex)
|
{
|
||||||
{
|
await logger.LogError(ex, "Error Loading Users {Error}", ex.Message);
|
||||||
await logger.LogError(ex, "Error Loading Users {Error}", ex.Message);
|
AddModuleMessage(Localizer["Error.User.Load"], MessageType.Error);
|
||||||
AddModuleMessage(Localizer["Error.User.Load"], MessageType.Error);
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
private async Task GetUserRoles()
|
private async Task<Dictionary<string, string>> GetUsers(string filter)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
userroles = await UserRoleService.GetUserRolesAsync(PageState.Site.SiteId, name);
|
var users = await UserRoleService.GetUserRolesAsync(PageState.Site.SiteId, RoleNames.Registered);
|
||||||
}
|
return users.Where(item => item.User.DisplayName.Contains(filter, StringComparison.OrdinalIgnoreCase))
|
||||||
catch (Exception ex)
|
.ToDictionary(item => item.UserId.ToString(), item => item.User.DisplayName);
|
||||||
{
|
}
|
||||||
await logger.LogError(ex, "Error Loading User Roles {RoleId} {Error}", roleid, ex.Message);
|
catch (Exception ex)
|
||||||
AddModuleMessage(Localizer["Error.User.LoadRole"], MessageType.Error);
|
{
|
||||||
}
|
await logger.LogError(ex, "Error Loading Users {filter} {Error}", filter, ex.Message);
|
||||||
}
|
AddModuleMessage(Localizer["Error.User.Load"], MessageType.Error);
|
||||||
|
}
|
||||||
|
return new Dictionary<string, string>();
|
||||||
|
}
|
||||||
|
|
||||||
private async Task SaveUserRole()
|
private async Task GetUserRoles()
|
||||||
{
|
{
|
||||||
validated = true;
|
try
|
||||||
var interop = new Interop(JSRuntime);
|
{
|
||||||
if (await interop.FormValid(form))
|
userroles = await UserRoleService.GetUserRolesAsync(PageState.Site.SiteId, name);
|
||||||
{
|
}
|
||||||
try
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
if (userid != -1)
|
await logger.LogError(ex, "Error Loading User Roles {RoleId} {Error}", roleid, ex.Message);
|
||||||
{
|
AddModuleMessage(Localizer["Error.User.LoadRole"], MessageType.Error);
|
||||||
var userrole = userroles.Where(item => item.UserId == userid && item.RoleId == roleid).FirstOrDefault();
|
}
|
||||||
if (userrole != null)
|
}
|
||||||
{
|
|
||||||
userrole.EffectiveDate = effectivedate;
|
|
||||||
userrole.ExpiryDate = expirydate;
|
|
||||||
await UserRoleService.UpdateUserRoleAsync(userrole);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
userrole = new UserRole();
|
|
||||||
userrole.UserId = userid;
|
|
||||||
userrole.RoleId = roleid;
|
|
||||||
userrole.EffectiveDate = effectivedate;
|
|
||||||
userrole.ExpiryDate = expirydate;
|
|
||||||
|
|
||||||
await UserRoleService.AddUserRoleAsync(userrole);
|
private async Task SaveUserRole()
|
||||||
}
|
{
|
||||||
|
validated = true;
|
||||||
|
var interop = new Interop(JSRuntime);
|
||||||
|
if (await interop.FormValid(form))
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (!string.IsNullOrEmpty(user.Key) && int.TryParse(user.Key, out int userid))
|
||||||
|
{
|
||||||
|
var userrole = userroles.Where(item => item.UserId == userid && item.RoleId == roleid).FirstOrDefault();
|
||||||
|
if (userrole != null)
|
||||||
|
{
|
||||||
|
userrole.EffectiveDate = effectivedate;
|
||||||
|
userrole.ExpiryDate = expirydate;
|
||||||
|
await UserRoleService.UpdateUserRoleAsync(userrole);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
userrole = new UserRole();
|
||||||
|
userrole.UserId = userid;
|
||||||
|
userrole.RoleId = roleid;
|
||||||
|
userrole.EffectiveDate = effectivedate;
|
||||||
|
userrole.ExpiryDate = expirydate;
|
||||||
|
|
||||||
await logger.LogInformation("User Assigned To Role {UserRole}", userrole);
|
await UserRoleService.AddUserRoleAsync(userrole);
|
||||||
AddModuleMessage(Localizer["Success.User.AssignedRole"], MessageType.Success);
|
}
|
||||||
await GetUserRoles();
|
|
||||||
StateHasChanged();
|
await logger.LogInformation("User Assigned To Role {UserRole}", userrole);
|
||||||
|
AddModuleMessage(Localizer["Success.User.AssignedRole"], MessageType.Success);
|
||||||
|
await GetUserRoles();
|
||||||
|
user.Clear();
|
||||||
|
StateHasChanged();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -127,7 +127,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row mb-1 align-items-center">
|
<div class="row mb-1 align-items-center">
|
||||||
<Label Class="col-sm-3" For="username" HelpText="Enter the username for your SMTP account" ResourceKey="SmptUsername">Username: </Label>
|
<Label Class="col-sm-3" For="username" HelpText="Enter the username for your SMTP account" ResourceKey="SmtpUsername">Username: </Label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
<input id="username" class="form-control" @bind="@_smtpusername" />
|
<input id="username" class="form-control" @bind="@_smtpusername" />
|
||||||
</div>
|
</div>
|
||||||
@ -142,12 +142,21 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row mb-1 align-items-center">
|
<div class="row mb-1 align-items-center">
|
||||||
<Label Class="col-sm-3" For="sender" HelpText="Enter the email which emails will be sent from. Please note that this email address may need to be authorized with the SMTP server." ResourceKey="SmptSender">Email Sender: </Label>
|
<Label Class="col-sm-3" For="sender" HelpText="Enter the email which emails will be sent from. Please note that this email address may need to be authorized with the SMTP server." ResourceKey="SmtpSender">Email Sender: </Label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
<input id="sender" class="form-control" @bind="@_smtpsender" />
|
<input id="sender" class="form-control" @bind="@_smtpsender" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row mb-1 align-items-center">
|
<div class="row mb-1 align-items-center">
|
||||||
|
<Label Class="col-sm-3" For="relay" HelpText="Only specify this option if you have properly configured an SMTP Relay Service to route your outgoing mail. This option will send notifications from the user's email rather than from the Email Sender specified above." ResourceKey="SmtpRelay">Relay Configured? </Label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<select id="relay" class="form-select" @bind="@_smtprelay" 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="retention" HelpText="Number of days of notifications to retain" ResourceKey="Retention">Retention (Days): </Label>
|
<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">
|
<div class="col-sm-9">
|
||||||
<input id="retention" class="form-control" @bind="@_retention" />
|
<input id="retention" class="form-control" @bind="@_retention" />
|
||||||
@ -320,6 +329,7 @@
|
|||||||
private string _smtppasswordtype = "password";
|
private string _smtppasswordtype = "password";
|
||||||
private string _togglesmtppassword = string.Empty;
|
private string _togglesmtppassword = string.Empty;
|
||||||
private string _smtpsender = string.Empty;
|
private string _smtpsender = string.Empty;
|
||||||
|
private string _smtprelay = "False";
|
||||||
private string _retention = string.Empty;
|
private string _retention = string.Empty;
|
||||||
private string _pwaisenabled;
|
private string _pwaisenabled;
|
||||||
private int _pwaappiconfileid = -1;
|
private int _pwaappiconfileid = -1;
|
||||||
@ -393,6 +403,7 @@
|
|||||||
_smtppassword = SettingService.GetSetting(settings, "SMTPPassword", string.Empty);
|
_smtppassword = SettingService.GetSetting(settings, "SMTPPassword", string.Empty);
|
||||||
_togglesmtppassword = SharedLocalizer["ShowPassword"];
|
_togglesmtppassword = SharedLocalizer["ShowPassword"];
|
||||||
_smtpsender = SettingService.GetSetting(settings, "SMTPSender", string.Empty);
|
_smtpsender = SettingService.GetSetting(settings, "SMTPSender", string.Empty);
|
||||||
|
_smtprelay = SettingService.GetSetting(settings, "SMTPRelay", "False");
|
||||||
_retention = SettingService.GetSetting(settings, "NotificationRetention", "30");
|
_retention = SettingService.GetSetting(settings, "NotificationRetention", "30");
|
||||||
|
|
||||||
if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host))
|
if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host))
|
||||||
@ -444,7 +455,7 @@
|
|||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
await logger.LogError(ex, "Error Loading Pane Layouts For Theme {ThemeType} {Error}", _themetype, ex.Message);
|
await logger.LogError(ex, "Error Loading Containers For Theme {ThemeType} {Error}", _themetype, ex.Message);
|
||||||
AddModuleMessage(Localizer["Error.Theme.LoadPane"], MessageType.Error);
|
AddModuleMessage(Localizer["Error.Theme.LoadPane"], MessageType.Error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -532,6 +543,7 @@
|
|||||||
settings = SettingService.SetSetting(settings, "SMTPUsername", _smtpusername, true);
|
settings = SettingService.SetSetting(settings, "SMTPUsername", _smtpusername, true);
|
||||||
settings = SettingService.SetSetting(settings, "SMTPPassword", _smtppassword, true);
|
settings = SettingService.SetSetting(settings, "SMTPPassword", _smtppassword, true);
|
||||||
settings = SettingService.SetSetting(settings, "SMTPSender", _smtpsender, true);
|
settings = SettingService.SetSetting(settings, "SMTPSender", _smtpsender, true);
|
||||||
|
settings = SettingService.SetSetting(settings, "SMTPRelay", _smtprelay, true);
|
||||||
settings = SettingService.SetSetting(settings, "NotificationRetention", _retention, true);
|
settings = SettingService.SetSetting(settings, "NotificationRetention", _retention, true);
|
||||||
await SettingService.UpdateSiteSettingsAsync(settings, site.SiteId);
|
await SettingService.UpdateSiteSettingsAsync(settings, site.SiteId);
|
||||||
|
|
||||||
@ -602,12 +614,12 @@
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
var settings = await SettingService.GetSiteSettingsAsync(PageState.Site.SiteId);
|
var settings = await SettingService.GetSiteSettingsAsync(PageState.Site.SiteId);
|
||||||
SettingService.SetSetting(settings, "SMTPHost", _smtphost, true);
|
settings = SettingService.SetSetting(settings, "SMTPHost", _smtphost, true);
|
||||||
SettingService.SetSetting(settings, "SMTPPort", _smtpport, true);
|
settings = SettingService.SetSetting(settings, "SMTPPort", _smtpport, true);
|
||||||
SettingService.SetSetting(settings, "SMTPSSL", _smtpssl, true);
|
settings = SettingService.SetSetting(settings, "SMTPSSL", _smtpssl, true);
|
||||||
SettingService.SetSetting(settings, "SMTPUsername", _smtpusername, true);
|
settings = SettingService.SetSetting(settings, "SMTPUsername", _smtpusername, true);
|
||||||
SettingService.SetSetting(settings, "SMTPPassword", _smtppassword, true);
|
settings = SettingService.SetSetting(settings, "SMTPPassword", _smtppassword, true);
|
||||||
SettingService.SetSetting(settings, "SMTPSender", _smtpsender, true);
|
settings = SettingService.SetSetting(settings, "SMTPSender", _smtpsender, true);
|
||||||
await SettingService.UpdateSiteSettingsAsync(settings, PageState.Site.SiteId);
|
await SettingService.UpdateSiteSettingsAsync(settings, PageState.Site.SiteId);
|
||||||
await logger.LogInformation("Site SMTP Settings Saved");
|
await logger.LogInformation("Site SMTP Settings Saved");
|
||||||
|
|
||||||
|
@ -16,7 +16,7 @@ else
|
|||||||
<div class="container">
|
<div class="container">
|
||||||
|
|
||||||
<div class="row mb-1 align-items-center">
|
<div class="row mb-1 align-items-center">
|
||||||
<Label Class="col-sm-3" For="tenant" HelpText="Select the tenant for the SQL server" ResourceKey="Tenant">Tenant: </Label>
|
<Label Class="col-sm-3" For="tenant" HelpText="Select the tenant associated with the database server" ResourceKey="Tenant">Tenant: </Label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
<select id="tenant" class="form-select" value="@_tenantid" @onchange="(e => TenantChanged(e))">
|
<select id="tenant" class="form-select" value="@_tenantid" @onchange="(e => TenantChanged(e))">
|
||||||
<option value="-1"><@Localizer["Tenant.Select"]></option>
|
<option value="-1"><@Localizer["Tenant.Select"]></option>
|
||||||
@ -38,13 +38,16 @@ else
|
|||||||
<div class="row mb-1 align-items-center">
|
<div class="row mb-1 align-items-center">
|
||||||
<Label Class="col-sm-3" For="connectionstring" HelpText="The connection information for the database" ResourceKey="ConnectionString">Connection: </Label>
|
<Label Class="col-sm-3" For="connectionstring" HelpText="The connection information for the database" ResourceKey="ConnectionString">Connection: </Label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
<textarea id="connectionstring" class="form-control" @bind="@_connectionstring" rows="2" readonly></textarea>
|
<div class="input-group">
|
||||||
</div>
|
<input id="connectionstring" type="@_connectionstringtype" class="form-control" @bind="@_connectionstring" readonly />
|
||||||
</div>
|
<button type="button" class="btn btn-secondary" @onclick="@ToggleConnectionString">@_connectionstringtoggle</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div class="row mb-1 align-items-center">
|
<div class="row mb-1 align-items-center">
|
||||||
<Label Class="col-sm-3" For="sqlQeury" HelpText="Enter the query for the SQL server" ResourceKey="SqlQuery">SQL Query: </Label>
|
<Label Class="col-sm-3" For="sqlQuery" HelpText="Enter the SQL query for the database server" ResourceKey="SqlQuery">SQL Query: </Label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
<textarea id="sqlQeury" class="form-control" @bind="@_sql" rows="3"></textarea>
|
<textarea id="sqlQuery" class="form-control" @bind="@_sql" rows="3"></textarea>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
@ -85,8 +88,10 @@ else
|
|||||||
private List<Tenant> _tenants;
|
private List<Tenant> _tenants;
|
||||||
private string _tenantid = "-1";
|
private string _tenantid = "-1";
|
||||||
private string _database = string.Empty;
|
private string _database = string.Empty;
|
||||||
private string _connectionstring = string.Empty;
|
private string _connectionstring = string.Empty;
|
||||||
private string _sql = string.Empty;
|
private string _connectionstringtype = "password";
|
||||||
|
private string _connectionstringtoggle = string.Empty;
|
||||||
|
private string _sql = string.Empty;
|
||||||
private List<Dictionary<string, string>> _results;
|
private List<Dictionary<string, string>> _results;
|
||||||
|
|
||||||
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Host;
|
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Host;
|
||||||
@ -96,7 +101,8 @@ else
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
_tenants = await TenantService.GetTenantsAsync();
|
_tenants = await TenantService.GetTenantsAsync();
|
||||||
}
|
_connectionstringtoggle = SharedLocalizer["ShowPassword"];
|
||||||
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
await logger.LogError(ex, "Error Loading Tenants {Error}", ex.Message);
|
await logger.LogError(ex, "Error Loading Tenants {Error}", ex.Message);
|
||||||
@ -126,7 +132,21 @@ else
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task Execute()
|
private void ToggleConnectionString()
|
||||||
|
{
|
||||||
|
if (_connectionstringtype == "password")
|
||||||
|
{
|
||||||
|
_connectionstringtype = "text";
|
||||||
|
_connectionstringtoggle = SharedLocalizer["HidePassword"];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_connectionstringtype = "password";
|
||||||
|
_connectionstringtoggle = SharedLocalizer["ShowPassword"];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task Execute()
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
@ -147,6 +147,16 @@
|
|||||||
<a class="btn btn-primary" href="swagger/index.html" target="_new">@Localizer["Access.ApiFramework"]</a>
|
<a class="btn btn-primary" href="swagger/index.html" target="_new">@Localizer["Access.ApiFramework"]</a>
|
||||||
<ActionDialog Header="Restart Application" Message="Are You Sure You Wish To Restart The Application?" Action="Restart Application" Security="SecurityAccessLevel.Host" Class="btn btn-danger" OnClick="@(async () => await RestartApplication())" ResourceKey="RestartApplication" />
|
<ActionDialog Header="Restart Application" Message="Are You Sure You Wish To Restart The Application?" Action="Restart Application" Security="SecurityAccessLevel.Host" Class="btn btn-danger" OnClick="@(async () => await RestartApplication())" ResourceKey="RestartApplication" />
|
||||||
</TabPanel>
|
</TabPanel>
|
||||||
|
<TabPanel Name="Log" Heading="Log" ResourceKey="Log">
|
||||||
|
<div class="container">
|
||||||
|
<div class="row mb-1 align-items-center">
|
||||||
|
<Label Class="col-sm-3" For="log" HelpText="System log information for current day" ResourceKey="Log">Log: </Label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<textarea id="log" class="form-control" rows="10" @bind="@_log" readonly />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</TabPanel>
|
||||||
</TabStrip>
|
</TabStrip>
|
||||||
<br /><br />
|
<br /><br />
|
||||||
|
|
||||||
@ -172,6 +182,8 @@
|
|||||||
private string _swagger = string.Empty;
|
private string _swagger = string.Empty;
|
||||||
private string _packageservice = string.Empty;
|
private string _packageservice = string.Empty;
|
||||||
|
|
||||||
|
private string _log = string.Empty;
|
||||||
|
|
||||||
protected override async Task OnInitializedAsync()
|
protected override async Task OnInitializedAsync()
|
||||||
{
|
{
|
||||||
_version = Constants.Version;
|
_version = Constants.Version;
|
||||||
@ -191,19 +203,25 @@
|
|||||||
_workingset = (Convert.ToInt64(systeminfo["WorkingSet"].ToString()) / 1000000).ToString() + " MB";
|
_workingset = (Convert.ToInt64(systeminfo["WorkingSet"].ToString()) / 1000000).ToString() + " MB";
|
||||||
}
|
}
|
||||||
|
|
||||||
systeminfo = await SystemService.GetSystemInfoAsync();
|
systeminfo = await SystemService.GetSystemInfoAsync("configuration");
|
||||||
if (systeminfo != null)
|
if (systeminfo != null)
|
||||||
{
|
{
|
||||||
_installationid = systeminfo["InstallationId"].ToString();
|
_installationid = systeminfo["InstallationId"].ToString();
|
||||||
_detailederrors = systeminfo["DetailedErrors"].ToString();
|
_detailederrors = systeminfo["DetailedErrors"].ToString();
|
||||||
_logginglevel = systeminfo["Logging:LogLevel:Default"].ToString();
|
_logginglevel = systeminfo["Logging:LogLevel:Default"].ToString();
|
||||||
_notificationlevel = systeminfo["Logging:LogLevel:Notify"].ToString();
|
_notificationlevel = systeminfo["Logging:LogLevel:Notify"].ToString();
|
||||||
_swagger = systeminfo["UseSwagger"].ToString();
|
_swagger = systeminfo["UseSwagger"].ToString();
|
||||||
_packageservice = systeminfo["PackageService"].ToString();
|
_packageservice = systeminfo["PackageService"].ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
systeminfo = await SystemService.GetSystemInfoAsync("log");
|
||||||
|
if (systeminfo != null)
|
||||||
|
{
|
||||||
|
_log = systeminfo["Log"].ToString();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task SaveConfig()
|
private async Task SaveConfig()
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
@ -29,7 +29,7 @@ else
|
|||||||
<Row>
|
<Row>
|
||||||
<td><ActionLink Action="View" Parameters="@($"name=" + WebUtility.UrlEncode(context.ThemeName))" ResourceKey="ViewTheme" /></td>
|
<td><ActionLink Action="View" Parameters="@($"name=" + WebUtility.UrlEncode(context.ThemeName))" ResourceKey="ViewTheme" /></td>
|
||||||
<td>
|
<td>
|
||||||
@if (context.AssemblyName != "Oqtane.Client")
|
@if (context.AssemblyName != Constants.ClientId)
|
||||||
{
|
{
|
||||||
<ActionDialog Header="Delete Theme" Message="@string.Format(Localizer["Confirm.Theme.Delete"], context.Name)" Action="Delete" Security="SecurityAccessLevel.Host" Class="btn btn-danger" OnClick="@(async () => await DeleteTheme(context))" ResourceKey="DeleteTheme" />
|
<ActionDialog Header="Delete Theme" Message="@string.Format(Localizer["Confirm.Theme.Delete"], context.Name)" Action="Delete" Security="SecurityAccessLevel.Host" Class="btn btn-danger" OnClick="@(async () => await DeleteTheme(context))" ResourceKey="DeleteTheme" />
|
||||||
}
|
}
|
||||||
|
@ -224,126 +224,140 @@ else
|
|||||||
<br /><br />
|
<br /><br />
|
||||||
|
|
||||||
@code {
|
@code {
|
||||||
private string username = string.Empty;
|
private string username = string.Empty;
|
||||||
private string _password = string.Empty;
|
private string _password = string.Empty;
|
||||||
private string _passwordtype = "password";
|
private string _passwordtype = "password";
|
||||||
private string _togglepassword = string.Empty;
|
private string _togglepassword = string.Empty;
|
||||||
private string confirm = string.Empty;
|
private string confirm = string.Empty;
|
||||||
private bool allowtwofactor = false;
|
private bool allowtwofactor = false;
|
||||||
private string twofactor = "False";
|
private string twofactor = "False";
|
||||||
private string email = string.Empty;
|
private string email = string.Empty;
|
||||||
private string displayname = string.Empty;
|
private string displayname = string.Empty;
|
||||||
private FileManager filemanager;
|
private FileManager filemanager;
|
||||||
private int folderid = -1;
|
private int folderid = -1;
|
||||||
private int photofileid = -1;
|
private int photofileid = -1;
|
||||||
private File photo = null;
|
private File photo = null;
|
||||||
private List<Profile> profiles;
|
private List<Profile> profiles;
|
||||||
private Dictionary<string, string> settings;
|
private Dictionary<string, string> settings;
|
||||||
private string category = string.Empty;
|
private string category = string.Empty;
|
||||||
private string filter = "to";
|
private string filter = "to";
|
||||||
private List<Notification> notifications;
|
private List<Notification> notifications;
|
||||||
|
|
||||||
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.View;
|
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.View;
|
||||||
|
|
||||||
protected override async Task OnParametersSetAsync()
|
protected override async Task OnParametersSetAsync()
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
_togglepassword = SharedLocalizer["ShowPassword"];
|
_togglepassword = SharedLocalizer["ShowPassword"];
|
||||||
|
|
||||||
if (PageState.Site.Settings.ContainsKey("LoginOptions:TwoFactor") && !string.IsNullOrEmpty(PageState.Site.Settings["LoginOptions:TwoFactor"]))
|
if (PageState.Site.Settings.ContainsKey("LoginOptions:TwoFactor") && !string.IsNullOrEmpty(PageState.Site.Settings["LoginOptions:TwoFactor"]))
|
||||||
{
|
{
|
||||||
allowtwofactor = (PageState.Site.Settings["LoginOptions:TwoFactor"] == "true");
|
allowtwofactor = (PageState.Site.Settings["LoginOptions:TwoFactor"] == "true");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (PageState.User != null)
|
if (PageState.User != null)
|
||||||
{
|
{
|
||||||
username = PageState.User.Username;
|
username = PageState.User.Username;
|
||||||
twofactor = PageState.User.TwoFactorRequired.ToString();
|
twofactor = PageState.User.TwoFactorRequired.ToString();
|
||||||
email = PageState.User.Email;
|
email = PageState.User.Email;
|
||||||
displayname = PageState.User.DisplayName;
|
displayname = PageState.User.DisplayName;
|
||||||
|
|
||||||
// get user folder
|
// get user folder
|
||||||
var folder = await FolderService.GetFolderAsync(ModuleState.SiteId, PageState.User.FolderPath);
|
var folder = await FolderService.GetFolderAsync(ModuleState.SiteId, PageState.User.FolderPath);
|
||||||
if (folder != null)
|
if (folder != null)
|
||||||
{
|
{
|
||||||
folderid = folder.FolderId;
|
folderid = folder.FolderId;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (PageState.User.PhotoFileId != null)
|
if (PageState.User.PhotoFileId != null)
|
||||||
{
|
{
|
||||||
photofileid = PageState.User.PhotoFileId.Value;
|
photofileid = PageState.User.PhotoFileId.Value;
|
||||||
photo = await FileService.GetFileAsync(photofileid);
|
photo = await FileService.GetFileAsync(photofileid);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
photofileid = -1;
|
photofileid = -1;
|
||||||
photo = null;
|
photo = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
profiles = await ProfileService.GetProfilesAsync(ModuleState.SiteId);
|
profiles = await ProfileService.GetProfilesAsync(ModuleState.SiteId);
|
||||||
settings = await SettingService.GetUserSettingsAsync(PageState.User.UserId);
|
settings = await SettingService.GetUserSettingsAsync(PageState.User.UserId);
|
||||||
|
|
||||||
await LoadNotificationsAsync();
|
await LoadNotificationsAsync();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
AddModuleMessage(Localizer["Message.User.NoLogIn"], MessageType.Warning);
|
AddModuleMessage(Localizer["Message.User.NoLogIn"], MessageType.Warning);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
await logger.LogError(ex, "Error Loading User Profile {Error}", ex.Message);
|
await logger.LogError(ex, "Error Loading User Profile {Error}", ex.Message);
|
||||||
AddModuleMessage(Localizer["Error.Profile.Load"], MessageType.Error);
|
AddModuleMessage(Localizer["Error.Profile.Load"], MessageType.Error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task LoadNotificationsAsync()
|
private async Task LoadNotificationsAsync()
|
||||||
{
|
{
|
||||||
notifications = await NotificationService.GetNotificationsAsync(PageState.Site.SiteId, filter, PageState.User.UserId);
|
notifications = await NotificationService.GetNotificationsAsync(PageState.Site.SiteId, filter, PageState.User.UserId);
|
||||||
notifications = notifications.Where(item => item.DeletedBy != PageState.User.Username).ToList();
|
notifications = notifications.Where(item => item.DeletedBy != PageState.User.Username).ToList();
|
||||||
}
|
}
|
||||||
|
|
||||||
private string GetProfileValue(string SettingName, string DefaultValue)
|
private string GetProfileValue(string SettingName, string DefaultValue)
|
||||||
=> SettingService.GetSetting(settings, SettingName, DefaultValue);
|
{
|
||||||
|
string value = SettingService.GetSetting(settings, SettingName, DefaultValue);
|
||||||
|
if (value.Contains("]"))
|
||||||
|
{
|
||||||
|
value = value.Substring(value.IndexOf("]") + 1);
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
private async Task Save()
|
private async Task Save()
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (username != string.Empty && email != string.Empty && ValidateProfiles())
|
if (username != string.Empty && email != string.Empty && ValidateProfiles())
|
||||||
{
|
{
|
||||||
if (_password == confirm)
|
if (_password == confirm)
|
||||||
{
|
{
|
||||||
var user = PageState.User;
|
var user = PageState.User;
|
||||||
user.Username = username;
|
user.Username = username;
|
||||||
user.Password = _password;
|
user.Password = _password;
|
||||||
user.TwoFactorRequired = bool.Parse(twofactor);
|
user.TwoFactorRequired = bool.Parse(twofactor);
|
||||||
user.Email = email;
|
user.Email = email;
|
||||||
user.DisplayName = (displayname == string.Empty ? username : displayname);
|
user.DisplayName = (displayname == string.Empty ? username : displayname);
|
||||||
user.PhotoFileId = filemanager.GetFileId();
|
user.PhotoFileId = filemanager.GetFileId();
|
||||||
if (user.PhotoFileId == -1)
|
if (user.PhotoFileId == -1)
|
||||||
{
|
{
|
||||||
user.PhotoFileId = null;
|
user.PhotoFileId = null;
|
||||||
}
|
}
|
||||||
if (user.PhotoFileId != null)
|
if (user.PhotoFileId != null)
|
||||||
{
|
{
|
||||||
photofileid = user.PhotoFileId.Value;
|
photofileid = user.PhotoFileId.Value;
|
||||||
photo = await FileService.GetFileAsync(photofileid);
|
photo = await FileService.GetFileAsync(photofileid);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
photofileid = -1;
|
photofileid = -1;
|
||||||
photo = null;
|
photo = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
await UserService.UpdateUserAsync(user);
|
user = await UserService.UpdateUserAsync(user);
|
||||||
await SettingService.UpdateUserSettingsAsync(settings, PageState.User.UserId);
|
if (user != null)
|
||||||
await logger.LogInformation("User Profile Saved");
|
{
|
||||||
|
await SettingService.UpdateUserSettingsAsync(settings, PageState.User.UserId);
|
||||||
|
await logger.LogInformation("User Profile Saved");
|
||||||
|
|
||||||
AddModuleMessage(Localizer["Success.Profile.Update"], MessageType.Success);
|
AddModuleMessage(Localizer["Success.Profile.Update"], MessageType.Success);
|
||||||
StateHasChanged();
|
StateHasChanged();
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
AddModuleMessage(Localizer["Message.Password.Complexity"], MessageType.Error);
|
||||||
|
}
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
AddModuleMessage(Localizer["Message.Password.Invalid"], MessageType.Warning);
|
AddModuleMessage(Localizer["Message.Password.Invalid"], MessageType.Warning);
|
||||||
|
@ -104,7 +104,7 @@
|
|||||||
private Dictionary<string, string> settings;
|
private Dictionary<string, string> settings;
|
||||||
private string category = string.Empty;
|
private string category = string.Empty;
|
||||||
|
|
||||||
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Admin;
|
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Edit;
|
||||||
|
|
||||||
protected override async Task OnInitializedAsync()
|
protected override async Task OnInitializedAsync()
|
||||||
{
|
{
|
||||||
@ -121,8 +121,15 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private string GetProfileValue(string SettingName, string DefaultValue)
|
private string GetProfileValue(string SettingName, string DefaultValue)
|
||||||
=> SettingService.GetSetting(settings, SettingName, DefaultValue);
|
{
|
||||||
|
string value = SettingService.GetSetting(settings, SettingName, DefaultValue);
|
||||||
|
if (value.Contains("]"))
|
||||||
|
{
|
||||||
|
value = value.Substring(value.IndexOf("]") + 1);
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
private async Task SaveUser()
|
private async Task SaveUser()
|
||||||
{
|
{
|
||||||
|
@ -174,7 +174,7 @@ else
|
|||||||
private string deletedby;
|
private string deletedby;
|
||||||
private DateTime? deletedon;
|
private DateTime? deletedon;
|
||||||
|
|
||||||
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Admin;
|
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Edit;
|
||||||
|
|
||||||
protected override async Task OnParametersSetAsync()
|
protected override async Task OnParametersSetAsync()
|
||||||
{
|
{
|
||||||
@ -201,58 +201,71 @@ else
|
|||||||
photofileid = -1;
|
photofileid = -1;
|
||||||
photo = null;
|
photo = null;
|
||||||
}
|
}
|
||||||
isdeleted = user.IsDeleted.ToString();
|
isdeleted = user.IsDeleted.ToString();
|
||||||
lastlogin = string.Format("{0:MMM dd yyyy HH:mm:ss}", user.LastLoginOn);
|
lastlogin = string.Format("{0:MMM dd yyyy HH:mm:ss}", user.LastLoginOn);
|
||||||
lastipaddress = user.LastIPAddress;
|
lastipaddress = user.LastIPAddress;
|
||||||
|
|
||||||
settings = await SettingService.GetUserSettingsAsync(user.UserId);
|
settings = await SettingService.GetUserSettingsAsync(user.UserId);
|
||||||
createdby = user.CreatedBy;
|
createdby = user.CreatedBy;
|
||||||
createdon = user.CreatedOn;
|
createdon = user.CreatedOn;
|
||||||
modifiedby = user.ModifiedBy;
|
modifiedby = user.ModifiedBy;
|
||||||
modifiedon = user.ModifiedOn;
|
modifiedon = user.ModifiedOn;
|
||||||
deletedby = user.DeletedBy;
|
deletedby = user.DeletedBy;
|
||||||
deletedon = user.DeletedOn;
|
deletedon = user.DeletedOn;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
await logger.LogError(ex, "Error Loading User {UserId} {Error}", userid, ex.Message);
|
await logger.LogError(ex, "Error Loading User {UserId} {Error}", userid, ex.Message);
|
||||||
AddModuleMessage(Localizer["Error.User.Load"], MessageType.Error);
|
AddModuleMessage(Localizer["Error.User.Load"], MessageType.Error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private string GetProfileValue(string SettingName, string DefaultValue)
|
private string GetProfileValue(string SettingName, string DefaultValue)
|
||||||
=> SettingService.GetSetting(settings, SettingName, DefaultValue);
|
{
|
||||||
|
string value = SettingService.GetSetting(settings, SettingName, DefaultValue);
|
||||||
|
if (value.Contains("]"))
|
||||||
|
{
|
||||||
|
value = value.Substring(value.IndexOf("]") + 1);
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
private async Task SaveUser()
|
private async Task SaveUser()
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (username != string.Empty && email != string.Empty && ValidateProfiles())
|
if (username != string.Empty && email != string.Empty && ValidateProfiles())
|
||||||
{
|
{
|
||||||
if (_password == confirm)
|
if (_password == confirm)
|
||||||
{
|
{
|
||||||
var user = await UserService.GetUserAsync(userid, PageState.Site.SiteId);
|
var user = await UserService.GetUserAsync(userid, PageState.Site.SiteId);
|
||||||
user.SiteId = PageState.Site.SiteId;
|
user.SiteId = PageState.Site.SiteId;
|
||||||
user.Username = username;
|
user.Username = username;
|
||||||
user.Password = _password;
|
user.Password = _password;
|
||||||
user.Email = email;
|
user.Email = email;
|
||||||
user.DisplayName = string.IsNullOrWhiteSpace(displayname) ? username : displayname;
|
user.DisplayName = string.IsNullOrWhiteSpace(displayname) ? username : displayname;
|
||||||
user.PhotoFileId = null;
|
user.PhotoFileId = null;
|
||||||
user.PhotoFileId = filemanager.GetFileId();
|
user.PhotoFileId = filemanager.GetFileId();
|
||||||
if (user.PhotoFileId == -1)
|
if (user.PhotoFileId == -1)
|
||||||
{
|
{
|
||||||
user.PhotoFileId = null;
|
user.PhotoFileId = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
user.IsDeleted = (isdeleted == null ? true : Boolean.Parse(isdeleted));
|
user.IsDeleted = (isdeleted == null ? true : Boolean.Parse(isdeleted));
|
||||||
|
|
||||||
user = await UserService.UpdateUserAsync(user);
|
user = await UserService.UpdateUserAsync(user);
|
||||||
await SettingService.UpdateUserSettingsAsync(settings, user.UserId);
|
if (user != null)
|
||||||
await logger.LogInformation("User Saved {User}", user);
|
{
|
||||||
|
await SettingService.UpdateUserSettingsAsync(settings, user.UserId);
|
||||||
NavigationManager.NavigateTo(NavigateUrl());
|
await logger.LogInformation("User Saved {User}", user);
|
||||||
|
NavigationManager.NavigateTo(NavigateUrl());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
AddModuleMessage(Localizer["Message.Password.Complexity"], MessageType.Error);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -20,7 +20,7 @@ else
|
|||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="row mb-1 align-items-center">
|
<div class="row mb-1 align-items-center">
|
||||||
<div class="col-sm-4">
|
<div class="col-sm-4">
|
||||||
<ActionLink Action="Add" Text="Add User" ResourceKey="AddUser" />
|
<ActionLink Action="Add" Text="Add User" Security="SecurityAccessLevel.Edit" ResourceKey="AddUser" />
|
||||||
</div>
|
</div>
|
||||||
<div class="col-sm-4">
|
<div class="col-sm-4">
|
||||||
<input class="form-control" @bind="@_search" />
|
<input class="form-control" @bind="@_search" />
|
||||||
@ -41,21 +41,21 @@ else
|
|||||||
</Header>
|
</Header>
|
||||||
<Row>
|
<Row>
|
||||||
<td>
|
<td>
|
||||||
<ActionLink Action="Edit" Parameters="@($"id=" + context.UserId.ToString())" ResourceKey="EditUser" />
|
<ActionLink Action="Edit" Parameters="@($"id=" + context.UserId.ToString())" Security="SecurityAccessLevel.Edit" ResourceKey="EditUser" />
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<ActionDialog Header="Delete User" Message="@string.Format(Localizer["Confirm.User.Delete"], context.User.DisplayName)" Action="Delete" Security="SecurityAccessLevel.Admin" Class="btn btn-danger" OnClick="@(async () => await DeleteUser(context))" Disabled="@(context.UserId == PageState.User.UserId)" ResourceKey="DeleteUser" />
|
<ActionDialog Header="Delete User" Message="@string.Format(Localizer["Confirm.User.Delete"], context.User.DisplayName)" Action="Delete" Security="SecurityAccessLevel.Edit" Class="btn btn-danger" OnClick="@(async () => await DeleteUser(context))" Disabled="@(context.UserId == PageState.User.UserId)" ResourceKey="DeleteUser" />
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<ActionLink Action="Roles" Parameters="@($"id=" + context.UserId.ToString())" ResourceKey="Roles" />
|
<ActionLink Action="Roles" Parameters="@($"id=" + context.UserId.ToString())" Security="SecurityAccessLevel.Edit" ResourceKey="Roles" />
|
||||||
</td>
|
</td>
|
||||||
<td>@context.User.Username</td>
|
<td>@context.User.Username</td>
|
||||||
<td>@((MarkupString)string.Format("<a href=\"mailto:{0}\">{1}</a>", @context.User.Email, @context.User.DisplayName))</td>
|
<td>@((MarkupString)string.Format("<a href=\"mailto:{0}\">{1}</a>", @context.User.Email, @context.User.DisplayName))</td>
|
||||||
<td>@string.Format("{0:dd-MMM-yyyy HH:mm:ss}", context.User.LastLoginOn)</td>
|
<td>@((context.User.LastLoginOn != DateTime.MinValue) ? string.Format("{0:dd-MMM-yyyy HH:mm:ss}", context.User.LastLoginOn) : "")</td>
|
||||||
</Row>
|
</Row>
|
||||||
</Pager>
|
</Pager>
|
||||||
</TabPanel>
|
</TabPanel>
|
||||||
<TabPanel Name="Settings" Heading="Settings" ResourceKey="Settings">
|
<TabPanel Name="Settings" Heading="Settings" ResourceKey="Settings" Security="SecurityAccessLevel.Admin">
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<Section Name="User" Heading="User Settings" ResourceKey="UserSettings">
|
<Section Name="User" Heading="User Settings" ResourceKey="UserSettings">
|
||||||
<div class="row mb-1 align-items-center">
|
<div class="row mb-1 align-items-center">
|
||||||
@ -286,6 +286,15 @@ else
|
|||||||
<input id="emailclaimtype" class="form-control" @bind="@_emailclaimtype" />
|
<input id="emailclaimtype" class="form-control" @bind="@_emailclaimtype" />
|
||||||
</div>
|
</div>
|
||||||
</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>
|
||||||
|
}
|
||||||
<div class="row mb-1 align-items-center">
|
<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>
|
<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">
|
<div class="col-sm-9">
|
||||||
@ -385,6 +394,7 @@ else
|
|||||||
private string _redirecturl;
|
private string _redirecturl;
|
||||||
private string _identifierclaimtype;
|
private string _identifierclaimtype;
|
||||||
private string _emailclaimtype;
|
private string _emailclaimtype;
|
||||||
|
private string _roleclaimtype;
|
||||||
private string _domainfilter;
|
private string _domainfilter;
|
||||||
private string _createusers;
|
private string _createusers;
|
||||||
|
|
||||||
@ -396,7 +406,7 @@ else
|
|||||||
private string _lifetime;
|
private string _lifetime;
|
||||||
private string _token;
|
private string _token;
|
||||||
|
|
||||||
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Admin;
|
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.View;
|
||||||
|
|
||||||
protected override async Task OnInitializedAsync()
|
protected override async Task OnInitializedAsync()
|
||||||
{
|
{
|
||||||
@ -436,8 +446,9 @@ else
|
|||||||
_parameters = SettingService.GetSetting(settings, "ExternalLogin:Parameters", "");
|
_parameters = SettingService.GetSetting(settings, "ExternalLogin:Parameters", "");
|
||||||
_pkce = SettingService.GetSetting(settings, "ExternalLogin:PKCE", "false");
|
_pkce = SettingService.GetSetting(settings, "ExternalLogin:PKCE", "false");
|
||||||
_redirecturl = PageState.Uri.Scheme + "://" + PageState.Alias.Name + "/signin-" + _providertype;
|
_redirecturl = PageState.Uri.Scheme + "://" + PageState.Alias.Name + "/signin-" + _providertype;
|
||||||
_identifierclaimtype = SettingService.GetSetting(settings, "ExternalLogin:IdentifierClaimType", "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier");
|
_identifierclaimtype = SettingService.GetSetting(settings, "ExternalLogin:IdentifierClaimType", "sub");
|
||||||
_emailclaimtype = SettingService.GetSetting(settings, "ExternalLogin:EmailClaimType", "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress");
|
_emailclaimtype = SettingService.GetSetting(settings, "ExternalLogin:EmailClaimType", "email");
|
||||||
|
_roleclaimtype = SettingService.GetSetting(settings, "ExternalLogin:RoleClaimType", "");
|
||||||
_domainfilter = SettingService.GetSetting(settings, "ExternalLogin:DomainFilter", "");
|
_domainfilter = SettingService.GetSetting(settings, "ExternalLogin:DomainFilter", "");
|
||||||
_createusers = SettingService.GetSetting(settings, "ExternalLogin:CreateUsers", "true");
|
_createusers = SettingService.GetSetting(settings, "ExternalLogin:CreateUsers", "true");
|
||||||
|
|
||||||
@ -445,7 +456,8 @@ else
|
|||||||
_togglesecret = SharedLocalizer["ShowPassword"];
|
_togglesecret = SharedLocalizer["ShowPassword"];
|
||||||
_issuer = SettingService.GetSetting(settings, "JwtOptions:Issuer", PageState.Uri.Scheme + "://" + PageState.Alias.Name);
|
_issuer = SettingService.GetSetting(settings, "JwtOptions:Issuer", PageState.Uri.Scheme + "://" + PageState.Alias.Name);
|
||||||
_audience = SettingService.GetSetting(settings, "JwtOptions:Audience", "");
|
_audience = SettingService.GetSetting(settings, "JwtOptions:Audience", "");
|
||||||
_lifetime = SettingService.GetSetting(settings, "JwtOptions:Lifetime", "20"); }
|
_lifetime = SettingService.GetSetting(settings, "JwtOptions:Lifetime", "20");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task LoadUsersAsync(bool load)
|
private async Task LoadUsersAsync(bool load)
|
||||||
@ -511,7 +523,7 @@ else
|
|||||||
private async Task UpdateUserSettingsAsync()
|
private async Task UpdateUserSettingsAsync()
|
||||||
{
|
{
|
||||||
Dictionary<string, string> settings = await SettingService.GetUserSettingsAsync(PageState.User.UserId);
|
Dictionary<string, string> settings = await SettingService.GetUserSettingsAsync(PageState.User.UserId);
|
||||||
SettingService.SetSetting(settings, settingSearch, _search);
|
settings = SettingService.SetSetting(settings, settingSearch, _search);
|
||||||
await SettingService.UpdateUserSettingsAsync(settings, PageState.User.UserId);
|
await SettingService.UpdateUserSettingsAsync(settings, PageState.User.UserId);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -555,6 +567,7 @@ else
|
|||||||
settings = SettingService.SetSetting(settings, "ExternalLogin:PKCE", _pkce, true);
|
settings = SettingService.SetSetting(settings, "ExternalLogin:PKCE", _pkce, true);
|
||||||
settings = SettingService.SetSetting(settings, "ExternalLogin:IdentifierClaimType", _identifierclaimtype, true);
|
settings = SettingService.SetSetting(settings, "ExternalLogin:IdentifierClaimType", _identifierclaimtype, true);
|
||||||
settings = SettingService.SetSetting(settings, "ExternalLogin:EmailClaimType", _emailclaimtype, true);
|
settings = SettingService.SetSetting(settings, "ExternalLogin:EmailClaimType", _emailclaimtype, true);
|
||||||
|
settings = SettingService.SetSetting(settings, "ExternalLogin:RoleClaimType", _roleclaimtype, true);
|
||||||
settings = SettingService.SetSetting(settings, "ExternalLogin:DomainFilter", _domainfilter, true);
|
settings = SettingService.SetSetting(settings, "ExternalLogin:DomainFilter", _domainfilter, true);
|
||||||
settings = SettingService.SetSetting(settings, "ExternalLogin:CreateUsers", _createusers, true);
|
settings = SettingService.SetSetting(settings, "ExternalLogin:CreateUsers", _createusers, true);
|
||||||
|
|
||||||
@ -590,14 +603,10 @@ else
|
|||||||
if (_providertype == AuthenticationProviderTypes.OpenIDConnect)
|
if (_providertype == AuthenticationProviderTypes.OpenIDConnect)
|
||||||
{
|
{
|
||||||
_scopes = "openid,profile,email";
|
_scopes = "openid,profile,email";
|
||||||
_identifierclaimtype = "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier";
|
|
||||||
_emailclaimtype = "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress";
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
_scopes = "";
|
_scopes = "";
|
||||||
_identifierclaimtype = "sub";
|
|
||||||
_emailclaimtype = "email";
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_redirecturl = PageState.Uri.Scheme + "://" + PageState.Alias.Name + "/signin-" + _providertype;
|
_redirecturl = PageState.Uri.Scheme + "://" + PageState.Alias.Name + "/signin-" + _providertype;
|
||||||
|
21
Oqtane.Client/Modules/Admin/Users/ModuleInfo.cs
Normal file
21
Oqtane.Client/Modules/Admin/Users/ModuleInfo.cs
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
using Oqtane.Documentation;
|
||||||
|
using Oqtane.Models;
|
||||||
|
using Oqtane.Shared;
|
||||||
|
|
||||||
|
namespace Oqtane.Modules.Admin.Users
|
||||||
|
{
|
||||||
|
[PrivateApi("Mark this as private, since it's not very useful in the public docs")]
|
||||||
|
public class ModuleInfo : IModule
|
||||||
|
{
|
||||||
|
public ModuleDefinition ModuleDefinition => new ModuleDefinition
|
||||||
|
{
|
||||||
|
Name = "Users",
|
||||||
|
Description = "Manage Users",
|
||||||
|
Categories = "Admin",
|
||||||
|
Version = Constants.Version,
|
||||||
|
PermissionNames = $"{PermissionNames.View},{PermissionNames.Edit}," +
|
||||||
|
$"{EntityNames.User}:{PermissionNames.Write}:{RoleNames.Admin}," +
|
||||||
|
$"{EntityNames.UserRole}:{PermissionNames.Write}:{RoleNames.Admin}"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
@ -63,7 +63,7 @@ else
|
|||||||
<td>@context.EffectiveDate</td>
|
<td>@context.EffectiveDate</td>
|
||||||
<td>@context.ExpiryDate</td>
|
<td>@context.ExpiryDate</td>
|
||||||
<td>
|
<td>
|
||||||
<ActionDialog Header="Remove Role" Message="@string.Format(Localizer["Confirm.User.RemoveRole"], context.Role.Name)" Action="Delete" Security="SecurityAccessLevel.Admin" Class="btn btn-danger" OnClick="@(async () => await DeleteUserRole(context.UserRoleId))" Disabled="@(context.Role.IsAutoAssigned || (context.Role.Name == RoleNames.Host && userid == PageState.User.UserId))" ResourceKey="DeleteUserRole" />
|
<ActionDialog Header="Remove Role" Message="@string.Format(Localizer["Confirm.User.RemoveRole"], context.Role.Name)" Action="Delete" Security="SecurityAccessLevel.Edit" Class="btn btn-danger" OnClick="@(async () => await DeleteUserRole(context.UserRoleId))" Disabled="@(context.Role.IsAutoAssigned || (context.Role.Name == RoleNames.Host && userid == PageState.User.UserId))" ResourceKey="DeleteUserRole" />
|
||||||
</td>
|
</td>
|
||||||
</Row>
|
</Row>
|
||||||
</Pager>
|
</Pager>
|
||||||
@ -79,7 +79,7 @@ else
|
|||||||
private string expirydate = string.Empty;
|
private string expirydate = string.Empty;
|
||||||
private List<UserRole> userroles;
|
private List<UserRole> userroles;
|
||||||
|
|
||||||
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Admin;
|
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Edit;
|
||||||
|
|
||||||
protected override async Task OnInitializedAsync()
|
protected override async Task OnInitializedAsync()
|
||||||
{
|
{
|
||||||
|
153
Oqtane.Client/Modules/Controls/AutoComplete.razor
Normal file
153
Oqtane.Client/Modules/Controls/AutoComplete.razor
Normal file
@ -0,0 +1,153 @@
|
|||||||
|
@namespace Oqtane.Modules.Controls
|
||||||
|
@inherits LocalizableComponent
|
||||||
|
|
||||||
|
<div class="app-autocomplete">
|
||||||
|
<input class="form-control" value="@Value" @oninput="OnInput" @onkeyup="OnKeyUp" placeholder="@Placeholder" autocomplete="off" />
|
||||||
|
@if (_results != null)
|
||||||
|
{
|
||||||
|
<select class="form-select" style="position: relative;" value="@Value" size="@Rows" @onkeyup="OnKeyUp" @onchange="(e => OnChange(e))">
|
||||||
|
@if (_results.Any())
|
||||||
|
{
|
||||||
|
@foreach (var result in _results)
|
||||||
|
{
|
||||||
|
if (result.Value == Value)
|
||||||
|
{
|
||||||
|
<option selected>@result.Value</option>
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<option>@result.Value</option>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<option disabled>No Results</option>
|
||||||
|
}
|
||||||
|
</select>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
@code {
|
||||||
|
Dictionary<string, string> _results;
|
||||||
|
|
||||||
|
[Parameter]
|
||||||
|
public Func<string, Task<Dictionary<string, string>>> OnSearch { get; set; } // required - an async delegate method which accepts a filter string parameter and returns a dictionary
|
||||||
|
|
||||||
|
[Parameter]
|
||||||
|
public int Characters { get; set; } = 3; // optional - number of characters before search is initiated
|
||||||
|
|
||||||
|
[Parameter]
|
||||||
|
public int Rows { get; set; } = 3; // optional - number of result rows to display
|
||||||
|
|
||||||
|
[Parameter]
|
||||||
|
public string Placeholder { get; set; } // optional - placeholder input text
|
||||||
|
|
||||||
|
[Parameter]
|
||||||
|
public string Value { get; set; } // value of item selected
|
||||||
|
|
||||||
|
[Parameter]
|
||||||
|
public string Key { get; set; } // key of item selected
|
||||||
|
|
||||||
|
private async Task OnInput(ChangeEventArgs e)
|
||||||
|
{
|
||||||
|
Value = e.Value?.ToString();
|
||||||
|
if (Value?.Length >= Characters)
|
||||||
|
{
|
||||||
|
_results = await OnSearch?.Invoke(Value);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_results = null;
|
||||||
|
}
|
||||||
|
SetKey();
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task OnKeyUp(KeyboardEventArgs e)
|
||||||
|
{
|
||||||
|
var index = -1;
|
||||||
|
switch (e.Key)
|
||||||
|
{
|
||||||
|
case "ArrowDown":
|
||||||
|
if (_results == null)
|
||||||
|
{
|
||||||
|
if (Value?.Length >= Characters)
|
||||||
|
{
|
||||||
|
_results = await OnSearch?.Invoke(Value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
index = GetIndex();
|
||||||
|
if (index < _results.Count - 1)
|
||||||
|
{
|
||||||
|
Value = _results.ElementAt(index + 1).Value;
|
||||||
|
Key = _results.ElementAt(index + 1).Key;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "ArrowUp":
|
||||||
|
index = GetIndex();
|
||||||
|
if (index > 0)
|
||||||
|
{
|
||||||
|
Value = _results.ElementAt(index - 1).Value;
|
||||||
|
Key = _results.ElementAt(index - 1).Key;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "ArrowRight":
|
||||||
|
case "Tab":
|
||||||
|
_results = null;
|
||||||
|
break;
|
||||||
|
case "Enter": // note within a form the enter key submits the entire form
|
||||||
|
case "NumpadEnter":
|
||||||
|
_results = null;
|
||||||
|
break;
|
||||||
|
case "Escape":
|
||||||
|
Value = "";
|
||||||
|
_results = null;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnChange(ChangeEventArgs e)
|
||||||
|
{
|
||||||
|
Value = (string)e.Value;
|
||||||
|
SetKey();
|
||||||
|
_results = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private int GetIndex()
|
||||||
|
{
|
||||||
|
if (_results != null)
|
||||||
|
{
|
||||||
|
for (int index = 0; index < _results.Count; index++)
|
||||||
|
{
|
||||||
|
if (_results.ElementAt(index).Value == Value)
|
||||||
|
{
|
||||||
|
return index;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SetKey()
|
||||||
|
{
|
||||||
|
var index = GetIndex();
|
||||||
|
if (index != -1)
|
||||||
|
{
|
||||||
|
Key = _results.ElementAt(index).Key;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Key = "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Clear()
|
||||||
|
{
|
||||||
|
Value = "";
|
||||||
|
Key = "";
|
||||||
|
_results = null;
|
||||||
|
}
|
||||||
|
}
|
@ -1,4 +1,5 @@
|
|||||||
@namespace Oqtane.Modules.Controls
|
@namespace Oqtane.Modules.Controls
|
||||||
|
@using System.Threading
|
||||||
@inherits ModuleControlBase
|
@inherits ModuleControlBase
|
||||||
@inject IFolderService FolderService
|
@inject IFolderService FolderService
|
||||||
@inject IFileService FileService
|
@inject IFileService FileService
|
||||||
@ -53,7 +54,7 @@
|
|||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
<div class="col mt-2 text-end">
|
<div class="col mt-2 text-end">
|
||||||
<button type="button" class="btn btn-success" @onclick="UploadFile">@SharedLocalizer["Upload"]</button>
|
<button type="button" class="btn btn-success" @onclick="UploadFiles">@SharedLocalizer["Upload"]</button>
|
||||||
@if (ShowFiles && GetFileId() != -1)
|
@if (ShowFiles && GetFileId() != -1)
|
||||||
{
|
{
|
||||||
<button type="button" class="btn btn-danger mx-1" @onclick="DeleteFile">@SharedLocalizer["Delete"]</button>
|
<button type="button" class="btn btn-danger mx-1" @onclick="DeleteFile">@SharedLocalizer["Delete"]</button>
|
||||||
@ -86,7 +87,6 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
@code {
|
@code {
|
||||||
private string _id;
|
|
||||||
private List<Folder> _folders;
|
private List<Folder> _folders;
|
||||||
private List<File> _files = new List<File>();
|
private List<File> _files = new List<File>();
|
||||||
private string _fileinputid = string.Empty;
|
private string _fileinputid = string.Empty;
|
||||||
@ -144,11 +144,6 @@
|
|||||||
|
|
||||||
protected override async Task OnInitializedAsync()
|
protected override async Task OnInitializedAsync()
|
||||||
{
|
{
|
||||||
if (!string.IsNullOrEmpty(Id))
|
|
||||||
{
|
|
||||||
_id = Id;
|
|
||||||
}
|
|
||||||
|
|
||||||
// packages folder is a framework folder for uploading installable nuget packages
|
// packages folder is a framework folder for uploading installable nuget packages
|
||||||
if (Folder == Constants.PackagesFolder)
|
if (Folder == Constants.PackagesFolder)
|
||||||
{
|
{
|
||||||
@ -207,9 +202,9 @@
|
|||||||
|
|
||||||
// create unique id for component
|
// create unique id for component
|
||||||
_guid = Guid.NewGuid().ToString("N");
|
_guid = Guid.NewGuid().ToString("N");
|
||||||
_fileinputid = _guid + "FileInput";
|
_fileinputid = "FileInput_" + _guid;
|
||||||
_progressinfoid = _guid + "ProgressInfo";
|
_progressinfoid = "ProgressInfo_" + _guid;
|
||||||
_progressbarid = _guid + "ProgressBar";
|
_progressbarid = "ProgressBar_" + _guid;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task GetFiles()
|
private async Task GetFiles()
|
||||||
@ -304,17 +299,17 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task UploadFile()
|
private async Task UploadFiles()
|
||||||
{
|
{
|
||||||
_message = string.Empty;
|
_message = string.Empty;
|
||||||
var interop = new Interop(JSRuntime);
|
var interop = new Interop(JSRuntime);
|
||||||
var upload = await interop.GetFiles(_fileinputid);
|
var uploads = await interop.GetFiles(_fileinputid);
|
||||||
if (upload.Length > 0)
|
if (uploads.Length > 0)
|
||||||
{
|
{
|
||||||
string restricted = "";
|
string restricted = "";
|
||||||
foreach (var file in upload)
|
foreach (var upload in uploads)
|
||||||
{
|
{
|
||||||
var extension = (file.LastIndexOf(".") != -1) ? file.Substring(file.LastIndexOf(".") + 1) : "";
|
var extension = (upload.LastIndexOf(".") != -1) ? upload.Substring(upload.LastIndexOf(".") + 1) : "";
|
||||||
if (!Constants.UploadableFiles.Split(',').Contains(extension.ToLower()))
|
if (!Constants.UploadableFiles.Split(',').Contains(extension.ToLower()))
|
||||||
{
|
{
|
||||||
restricted += (restricted == "" ? "" : ",") + extension;
|
restricted += (restricted == "" ? "" : ",") + extension;
|
||||||
@ -324,48 +319,68 @@
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
string result;
|
// upload the files
|
||||||
if (Folder == Constants.PackagesFolder)
|
var posturl = Utilities.TenantUrl(PageState.Alias, "/api/file/upload");
|
||||||
|
var folder = (Folder == Constants.PackagesFolder) ? Folder : FolderId.ToString();
|
||||||
|
await interop.UploadFiles(posturl, folder, _guid, SiteState.AntiForgeryToken);
|
||||||
|
|
||||||
|
// uploading is asynchronous so we need to wait for the uploads to complete
|
||||||
|
// note that this will only wait a maximum of 15 seconds which may not be long enough for very large file uploads
|
||||||
|
bool success = false;
|
||||||
|
int attempts = 0;
|
||||||
|
while (attempts < 5 && !success)
|
||||||
{
|
{
|
||||||
result = await FileService.UploadFilesAsync(Folder, upload, _guid);
|
attempts += 1;
|
||||||
}
|
Thread.Sleep(1000 * attempts); // progressive retry
|
||||||
else
|
|
||||||
{
|
success = true;
|
||||||
result = await FileService.UploadFilesAsync(FolderId, upload, _guid);
|
List<File> files = await FileService.GetFilesAsync(folder);
|
||||||
|
if (files.Count > 0)
|
||||||
|
{
|
||||||
|
foreach (string upload in uploads)
|
||||||
|
{
|
||||||
|
if (!files.Exists(item => item.Name == upload))
|
||||||
|
{
|
||||||
|
success = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (result == string.Empty)
|
// reset progress indicators
|
||||||
|
await interop.SetElementAttribute(_guid + "ProgressInfo", "style", "display: none;");
|
||||||
|
await interop.SetElementAttribute(_guid + "ProgressBar", "style", "display: none;");
|
||||||
|
|
||||||
|
if (success)
|
||||||
{
|
{
|
||||||
await logger.LogInformation("File Upload Succeeded {Files}", upload);
|
await logger.LogInformation("File Upload Succeeded {Files}", uploads);
|
||||||
if (ShowSuccess)
|
if (ShowSuccess)
|
||||||
{
|
{
|
||||||
_message = Localizer["Success.File.Upload"];
|
_message = Localizer["Success.File.Upload"];
|
||||||
_messagetype = MessageType.Success;
|
_messagetype = MessageType.Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
// set FileId to first file in upload collection
|
|
||||||
await GetFiles();
|
|
||||||
var file = _files.Where(item => item.Name == upload[0]).FirstOrDefault();
|
|
||||||
if (file != null)
|
|
||||||
{
|
|
||||||
FileId = file.FileId;
|
|
||||||
await SetImage();
|
|
||||||
await OnUpload.InvokeAsync(FileId);
|
|
||||||
}
|
|
||||||
StateHasChanged();
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
await logger.LogError("File Upload Failed For {Files}", result.Replace(",", ", "));
|
await logger.LogInformation("File Upload Failed Or Is Still In Progress {Files}", uploads);
|
||||||
|
|
||||||
_message = Localizer["Error.File.Upload"];
|
_message = Localizer["Error.File.Upload"];
|
||||||
_messagetype = MessageType.Error;
|
_messagetype = MessageType.Error;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// set FileId to first file in upload collection
|
||||||
|
await GetFiles();
|
||||||
|
var file = _files.Where(item => item.Name == uploads[0]).FirstOrDefault();
|
||||||
|
if (file != null)
|
||||||
|
{
|
||||||
|
FileId = file.FileId;
|
||||||
|
await SetImage();
|
||||||
|
await OnUpload.InvokeAsync(FileId);
|
||||||
|
}
|
||||||
|
StateHasChanged();
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
await logger.LogError(ex, "File Upload Failed {Error}", ex.Message);
|
await logger.LogError(ex, "File Upload Failed {Error}", ex.Message);
|
||||||
|
|
||||||
_message = Localizer["Error.File.Upload"];
|
_message = Localizer["Error.File.Upload"];
|
||||||
_messagetype = MessageType.Error;
|
_messagetype = MessageType.Error;
|
||||||
}
|
}
|
||||||
|
@ -8,16 +8,16 @@
|
|||||||
@if ((Toolbar == "Top" || Toolbar == "Both") && _pages > 0 && Items.Count() > _maxItems)
|
@if ((Toolbar == "Top" || Toolbar == "Both") && _pages > 0 && Items.Count() > _maxItems)
|
||||||
{
|
{
|
||||||
<ul class="pagination justify-content-center my-2">
|
<ul class="pagination justify-content-center my-2">
|
||||||
<li class="page-item@((_page > 1) ? "" : " disabled")">
|
<li class="page-item@((_page > 1) ? " app-pager-pointer" : " disabled")">
|
||||||
<a class="page-link" @onclick=@(async () => UpdateList(1))><span class="oi oi-media-step-backward" title="start" aria-hidden="true"></span></a>
|
<a class="page-link" @onclick=@(async () => UpdateList(1))><span class="oi oi-media-step-backward" title="start" aria-hidden="true"></span></a>
|
||||||
</li>
|
</li>
|
||||||
@if (_pages > _displayPages && _displayPages > 1)
|
@if (_pages > _displayPages && _displayPages > 1)
|
||||||
{
|
{
|
||||||
<li class="page-item@((_page > _displayPages) ? "" : " disabled")">
|
<li class="page-item@((_page > _displayPages) ? " app-pager-pointer" : " disabled")">
|
||||||
<a class="page-link" @onclick=@(async () => SkipPages("back"))><span class="oi oi-media-skip-backward" title="skip back" aria-hidden="true"></span></a>
|
<a class="page-link" @onclick=@(async () => SkipPages("back"))><span class="oi oi-media-skip-backward" title="skip back" aria-hidden="true"></span></a>
|
||||||
</li>
|
</li>
|
||||||
}
|
}
|
||||||
<li class="page-item@((_page > 1) ? "" : " disabled")">
|
<li class="page-item@((_page > 1) ? " app-pager-pointer" : " disabled")">
|
||||||
<a class="page-link" @onclick=@(async () => NavigateToPage("previous"))><span class="oi oi-chevron-left" title="previous" aria-hidden="true"></span></a>
|
<a class="page-link" @onclick=@(async () => NavigateToPage("previous"))><span class="oi oi-chevron-left" title="previous" aria-hidden="true"></span></a>
|
||||||
</li>
|
</li>
|
||||||
@for (int i = _startPage; i <= _endPage; i++)
|
@for (int i = _startPage; i <= _endPage; i++)
|
||||||
@ -25,27 +25,27 @@
|
|||||||
var pager = i;
|
var pager = i;
|
||||||
if (pager == _page)
|
if (pager == _page)
|
||||||
{
|
{
|
||||||
<li class="page-item active">
|
<li class="page-item app-pager-pointer active">
|
||||||
<a class="page-link" @onclick=@(async () => UpdateList(pager))>@pager</a>
|
<a class="page-link" @onclick=@(async () => UpdateList(pager))>@pager</a>
|
||||||
</li>
|
</li>
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
<li class="page-item">
|
<li class="page-item app-pager-pointer">
|
||||||
<a class="page-link" @onclick=@(async () => UpdateList(pager))>@pager</a>
|
<a class="page-link" @onclick=@(async () => UpdateList(pager))>@pager</a>
|
||||||
</li>
|
</li>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
<li class="page-item@((_page < _pages) ? "" : " disabled")">
|
<li class="page-item@((_page < _pages) ? " app-pager-pointer" : " disabled")">
|
||||||
<a class="page-link" @onclick=@(async () => NavigateToPage("next"))><span class="oi oi-chevron-right" title="next" aria-hidden="true"></span></a>
|
<a class="page-link" @onclick=@(async () => NavigateToPage("next"))><span class="oi oi-chevron-right" title="next" aria-hidden="true"></span></a>
|
||||||
</li>
|
</li>
|
||||||
@if (_pages > _displayPages && _displayPages > 1)
|
@if (_pages > _displayPages && _displayPages > 1)
|
||||||
{
|
{
|
||||||
<li class="page-item@((_endPage < _pages) ? "" : " disabled")">
|
<li class="page-item@((_endPage < _pages) ? " app-pager-pointer" : " disabled")">
|
||||||
<a class="page-link" @onclick=@(async () => SkipPages("forward"))><span class="oi oi-media-skip-forward" title="skip forward" aria-hidden="true"></span></a>
|
<a class="page-link" @onclick=@(async () => SkipPages("forward"))><span class="oi oi-media-skip-forward" title="skip forward" aria-hidden="true"></span></a>
|
||||||
</li>
|
</li>
|
||||||
}
|
}
|
||||||
<li class="page-item@((_page < _pages) ? "" : " disabled")">
|
<li class="page-item@((_page < _pages) ? " app-pager-pointer" : " disabled")">
|
||||||
<a class="page-link" @onclick=@(async () => UpdateList(_pages))><span class="oi oi-media-step-forward" title="end" aria-hidden="true"></span></a>
|
<a class="page-link" @onclick=@(async () => UpdateList(_pages))><span class="oi oi-media-step-forward" title="end" aria-hidden="true"></span></a>
|
||||||
</li>
|
</li>
|
||||||
<li class="page-item disabled">
|
<li class="page-item disabled">
|
||||||
@ -55,43 +55,46 @@
|
|||||||
}
|
}
|
||||||
@if (Format == "Table" && Row != null)
|
@if (Format == "Table" && Row != null)
|
||||||
{
|
{
|
||||||
<div class="table-responsive">
|
<div class="table-responsive">
|
||||||
<table class="@Class">
|
<table class="@Class">
|
||||||
<thead>
|
<thead>
|
||||||
<tr class="@RowClass">@Header</tr>
|
<tr class="@RowClass">@Header</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
@foreach (var item in ItemList)
|
@foreach (var item in ItemList)
|
||||||
{
|
{
|
||||||
<tr class="@RowClass">@Row(item)</tr>
|
<tr class="@RowClass">@Row(item)</tr>
|
||||||
@if (Detail != null)
|
@if (Detail != null)
|
||||||
{
|
{
|
||||||
<tr>@Detail(item)</tr>
|
<tr>@Detail(item)</tr>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
<tfoot>
|
||||||
</div>
|
<tr class="@RowClass">@Footer</tr>
|
||||||
|
</tfoot>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
}
|
}
|
||||||
@if (Format == "Grid" && Row != null)
|
@if (Format == "Grid" && Row != null)
|
||||||
{
|
{
|
||||||
int count = 0;
|
int count = 0;
|
||||||
int rows = 0;
|
int rows = 0;
|
||||||
int cols = 0;
|
int cols = 0;
|
||||||
if (ItemList != null)
|
if (ItemList != null)
|
||||||
{
|
{
|
||||||
if (_columns == 0)
|
if (_columns == 0)
|
||||||
{
|
{
|
||||||
count = ItemList.Count();
|
count = ItemList.Count();
|
||||||
rows = 1;
|
rows = 1;
|
||||||
cols = count;
|
cols = count;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
count = (int)Math.Ceiling(ItemList.Count() / (decimal)_columns) * _columns;
|
count = (int)Math.Ceiling(ItemList.Count() / (decimal)_columns) * _columns;
|
||||||
rows = count / _columns;
|
rows = count / _columns;
|
||||||
cols = _columns;
|
cols = _columns;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
<div class="@Class">
|
<div class="@Class">
|
||||||
@for (int row = 0; row < rows; row++)
|
@for (int row = 0; row < rows; row++)
|
||||||
@ -113,19 +116,19 @@
|
|||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
@if ((Toolbar == "Bottom" || Toolbar == "Both") && _pages > 0 && Items.Count() > _maxItems)
|
@if ((Toolbar == "Bottom" || Toolbar == "Both") && _pages > 0 && Items.Count() > _maxItems)
|
||||||
{
|
{
|
||||||
<ul class="pagination justify-content-center my-2">
|
<ul class="pagination justify-content-center my-2">
|
||||||
<li class="page-item@((_page > 1) ? "" : " disabled")">
|
<li class="page-item@((_page > 1) ? " app-pager-pointer" : " disabled")">
|
||||||
<a class="page-link" @onclick=@(async () => UpdateList(1))><span class="oi oi-media-step-backward" title="start" aria-hidden="true"></span></a>
|
<a class="page-link" @onclick=@(async () => UpdateList(1))><span class="oi oi-media-step-backward" title="start" aria-hidden="true"></span></a>
|
||||||
</li>
|
</li>
|
||||||
@if (_pages > _displayPages && _displayPages > 1)
|
@if (_pages > _displayPages && _displayPages > 1)
|
||||||
{
|
{
|
||||||
<li class="page-item@((_page > _displayPages) ? "" : " disabled")">
|
<li class="page-item@((_page > _displayPages) ? " app-pager-pointer" : " disabled")">
|
||||||
<a class="page-link" @onclick=@(async () => SkipPages("back"))><span class="oi oi-media-skip-backward" title="skip back" aria-hidden="true"></span></a>
|
<a class="page-link" @onclick=@(async () => SkipPages("back"))><span class="oi oi-media-skip-backward" title="skip back" aria-hidden="true"></span></a>
|
||||||
</li>
|
</li>
|
||||||
}
|
}
|
||||||
<li class="page-item@((_page > 1) ? "" : " disabled")">
|
<li class="page-item@((_page > 1) ? " app-pager-pointer" : " disabled")">
|
||||||
<a class="page-link" @onclick=@(async () => NavigateToPage("previous"))><span class="oi oi-chevron-left" title="previous" aria-hidden="true"></span></a>
|
<a class="page-link" @onclick=@(async () => NavigateToPage("previous"))><span class="oi oi-chevron-left" title="previous" aria-hidden="true"></span></a>
|
||||||
</li>
|
</li>
|
||||||
@for (int i = _startPage; i <= _endPage; i++)
|
@for (int i = _startPage; i <= _endPage; i++)
|
||||||
@ -133,97 +136,100 @@
|
|||||||
var pager = i;
|
var pager = i;
|
||||||
if (pager == _page)
|
if (pager == _page)
|
||||||
{
|
{
|
||||||
<li class="page-item active">
|
<li class="page-item app-pager-pointer active">
|
||||||
<a class="page-link" @onclick=@(async () => UpdateList(pager))>@pager</a>
|
<a class="page-link" @onclick=@(async () => UpdateList(pager))>@pager</a>
|
||||||
</li>
|
</li>
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
<li class="page-item">
|
<li class="page-item app-pager-pointer">
|
||||||
<a class="page-link" @onclick=@(async () => UpdateList(pager))>@pager</a>
|
<a class="page-link" @onclick=@(async () => UpdateList(pager))>@pager</a>
|
||||||
</li>
|
</li>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
<li class="page-item@((_page < _pages) ? "" : " disabled")">
|
<li class="page-item@((_page < _pages) ? " app-pager-pointer" : " disabled")">
|
||||||
<a class="page-link" @onclick=@(async () => NavigateToPage("next"))><span class="oi oi-chevron-right" title="next" aria-hidden="true"></span></a>
|
<a class="page-link" @onclick=@(async () => NavigateToPage("next"))><span class="oi oi-chevron-right" title="next" aria-hidden="true"></span></a>
|
||||||
</li>
|
</li>
|
||||||
@if (_pages > _displayPages && _displayPages > 1)
|
@if (_pages > _displayPages && _displayPages > 1)
|
||||||
{
|
{
|
||||||
<li class="page-item@((_endPage < _pages) ? "" : " disabled")">
|
<li class="page-item@((_endPage < _pages) ? " app-pager-pointer" : " disabled")">
|
||||||
<a class="page-link" @onclick=@(async () => SkipPages("forward"))><span class="oi oi-media-skip-forward" title="skip forward" aria-hidden="true"></span></a>
|
<a class="page-link" @onclick=@(async () => SkipPages("forward"))><span class="oi oi-media-skip-forward" title="skip forward" aria-hidden="true"></span></a>
|
||||||
</li>
|
</li>
|
||||||
}
|
}
|
||||||
<li class="page-item@((_page < _pages) ? "" : " disabled")">
|
<li class="page-item@((_page < _pages) ? " app-pager-pointer" : " disabled")">
|
||||||
<a class="page-link" @onclick=@(async () => UpdateList(_pages))><span class="oi oi-media-step-forward" title="end" aria-hidden="true"></span></a>
|
<a class="page-link" @onclick=@(async () => UpdateList(_pages))><span class="oi oi-media-step-forward" title="end" aria-hidden="true"></span></a>
|
||||||
</li>
|
</li>
|
||||||
<li class="page-item disabled">
|
<li class="page-item disabled">
|
||||||
<a class="page-link" style="white-space: nowrap;">@Localizer["PageOfPages", _page, _pages]</a>
|
<a class="page-link" style="white-space: nowrap;">@Localizer["PageOfPages", _page, _pages]</a>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@code {
|
@code {
|
||||||
private IStringLocalizer Localizer;
|
private IStringLocalizer Localizer;
|
||||||
private int _pages = 0;
|
private int _pages = 0;
|
||||||
private int _page = 1;
|
private int _page = 1;
|
||||||
private int _maxItems = 10;
|
private int _maxItems = 10;
|
||||||
private int _displayPages = 5;
|
private int _displayPages = 5;
|
||||||
private int _startPage = 0;
|
private int _startPage = 0;
|
||||||
private int _endPage = 0;
|
private int _endPage = 0;
|
||||||
private int _columns = 0;
|
private int _columns = 0;
|
||||||
|
|
||||||
[Parameter]
|
[Parameter]
|
||||||
public string Format { get; set; } // Table or Grid
|
public string Format { get; set; } // Table or Grid
|
||||||
|
|
||||||
[Parameter]
|
[Parameter]
|
||||||
public string Toolbar { get; set; } // Top, Bottom or Both
|
public string Toolbar { get; set; } // Top, Bottom or Both
|
||||||
|
|
||||||
[Parameter]
|
[Parameter]
|
||||||
public RenderFragment Header { get; set; } = null; // only applicable to Table layouts
|
public RenderFragment Header { get; set; } = null; // only applicable to Table layouts
|
||||||
|
|
||||||
[Parameter]
|
[Parameter]
|
||||||
public RenderFragment<TableItem> Row { get; set; } = null; // required
|
public RenderFragment<TableItem> Row { get; set; } = null; // required
|
||||||
|
|
||||||
[Parameter]
|
[Parameter]
|
||||||
public RenderFragment<TableItem> Detail { get; set; } = null; // only applicable to Table layouts
|
public RenderFragment Footer { get; set; } = null; // only applicable to Table layouts
|
||||||
|
|
||||||
[Parameter]
|
[Parameter]
|
||||||
public IEnumerable<TableItem> Items { get; set; } // the IEnumerable data source
|
public RenderFragment<TableItem> Detail { get; set; } = null; // only applicable to Table layouts
|
||||||
|
|
||||||
[Parameter]
|
[Parameter]
|
||||||
public string PageSize { get; set; } // number of items to display on a page
|
public IEnumerable<TableItem> Items { get; set; } // the IEnumerable data source
|
||||||
|
|
||||||
[Parameter]
|
[Parameter]
|
||||||
public string Columns { get; set; } // only applicable to Grid layouts - default is zero indicating use responsive behavior
|
public string PageSize { get; set; } // number of items to display on a page
|
||||||
|
|
||||||
[Parameter]
|
[Parameter]
|
||||||
public string CurrentPage { get; set; } // sets the initial page to display
|
public string Columns { get; set; } // only applicable to Grid layouts - default is zero indicating use responsive behavior
|
||||||
|
|
||||||
[Parameter]
|
[Parameter]
|
||||||
public string DisplayPages { get; set; } // maximum number of page numbers to display for user selection
|
public string CurrentPage { get; set; } // sets the initial page to display
|
||||||
|
|
||||||
[Parameter]
|
[Parameter]
|
||||||
public string Class { get; set; } // class for the containing element - ie. <table> for Table or <div> for Grid
|
public string DisplayPages { get; set; } // maximum number of page numbers to display for user selection
|
||||||
|
|
||||||
[Parameter]
|
[Parameter]
|
||||||
public string RowClass { get; set; } // class for row element - ie. <tr> for Table or <div> for Grid
|
public string Class { get; set; } // class for the containing element - ie. <table> for Table or <div> for Grid
|
||||||
|
|
||||||
[Parameter]
|
[Parameter]
|
||||||
public string ColumnClass { get; set; } // class for column element - only applicable to Grid format
|
public string RowClass { get; set; } // class for row element - ie. <tr> for Table or <div> for Grid
|
||||||
|
|
||||||
[Parameter]
|
[Parameter]
|
||||||
public Action<int> OnPageChange { get; set; } // a method to be executed in the calling component when the page changes
|
public string ColumnClass { get; set; } // class for column element - only applicable to Grid format
|
||||||
|
|
||||||
private IEnumerable<TableItem> ItemList { get; set; }
|
[Parameter]
|
||||||
|
public Action<int> OnPageChange { get; set; } // a method to be executed in the calling component when the page changes
|
||||||
|
|
||||||
protected override void OnInitialized()
|
private IEnumerable<TableItem> ItemList { get; set; }
|
||||||
{
|
|
||||||
Localizer = LocalizerFactory.Create(GetType().FullName);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void OnParametersSet()
|
protected override void OnInitialized()
|
||||||
{
|
{
|
||||||
|
Localizer = LocalizerFactory.Create(GetType().FullName);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnParametersSet()
|
||||||
|
{
|
||||||
if (string.IsNullOrEmpty(Format))
|
if (string.IsNullOrEmpty(Format))
|
||||||
{
|
{
|
||||||
Format = "Table";
|
Format = "Table";
|
||||||
@ -256,8 +262,8 @@
|
|||||||
{
|
{
|
||||||
RowClass = "row";
|
RowClass = "row";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (string.IsNullOrEmpty(ColumnClass))
|
if (string.IsNullOrEmpty(ColumnClass))
|
||||||
{
|
{
|
||||||
if (Format == "Table")
|
if (Format == "Table")
|
||||||
@ -268,9 +274,9 @@
|
|||||||
{
|
{
|
||||||
ColumnClass = "col";
|
ColumnClass = "col";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(PageSize))
|
if (!string.IsNullOrEmpty(PageSize))
|
||||||
{
|
{
|
||||||
_maxItems = int.Parse(PageSize);
|
_maxItems = int.Parse(PageSize);
|
||||||
}
|
}
|
||||||
@ -293,6 +299,7 @@
|
|||||||
{
|
{
|
||||||
_page = 1;
|
_page = 1;
|
||||||
}
|
}
|
||||||
|
if (_page < 1) _page = 1;
|
||||||
|
|
||||||
_startPage = 0;
|
_startPage = 0;
|
||||||
_endPage = 0;
|
_endPage = 0;
|
||||||
@ -304,7 +311,6 @@
|
|||||||
{
|
{
|
||||||
_page = _pages;
|
_page = _pages;
|
||||||
}
|
}
|
||||||
ItemList = Items.Skip((_page - 1) * _maxItems).Take(_maxItems);
|
|
||||||
SetPagerSize();
|
SetPagerSize();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -317,13 +323,13 @@
|
|||||||
{
|
{
|
||||||
_endPage = _pages;
|
_endPage = _pages;
|
||||||
}
|
}
|
||||||
OnPageChange?.Invoke(_page);
|
ItemList = Items.Skip((_page - 1) * _maxItems).Take(_maxItems);
|
||||||
StateHasChanged();
|
StateHasChanged();
|
||||||
}
|
OnPageChange?.Invoke(_page);
|
||||||
|
}
|
||||||
|
|
||||||
public void UpdateList(int page)
|
public void UpdateList(int page)
|
||||||
{
|
{
|
||||||
ItemList = Items.Skip((page - 1) * _maxItems).Take(_maxItems);
|
|
||||||
_page = page;
|
_page = page;
|
||||||
SetPagerSize();
|
SetPagerSize();
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
@inherits ModuleControlBase
|
@inherits ModuleControlBase
|
||||||
@inject IRoleService RoleService
|
@inject IRoleService RoleService
|
||||||
@inject IUserService UserService
|
@inject IUserService UserService
|
||||||
|
@inject IUserRoleService UserRoleService
|
||||||
@inject IStringLocalizer<PermissionGrid> Localizer
|
@inject IStringLocalizer<PermissionGrid> Localizer
|
||||||
@inject IStringLocalizer<SharedResources> SharedLocalizer
|
@inject IStringLocalizer<SharedResources> SharedLocalizer
|
||||||
|
|
||||||
@ -16,8 +17,8 @@
|
|||||||
<th scope="col">@Localizer["Role"]</th>
|
<th scope="col">@Localizer["Role"]</th>
|
||||||
@foreach (PermissionString permission in _permissions)
|
@foreach (PermissionString permission in _permissions)
|
||||||
{
|
{
|
||||||
<th style="text-align: center; width: 1px;">@Localizer[permission.PermissionName]</th>
|
<th style="text-align: center; width: 1px;">@((MarkupString)GetPermissionName(permission).Replace(" ", "<br />"))</th>
|
||||||
}
|
}
|
||||||
</tr>
|
</tr>
|
||||||
@foreach (Role role in _roles)
|
@foreach (Role role in _roles)
|
||||||
{
|
{
|
||||||
@ -27,7 +28,7 @@
|
|||||||
{
|
{
|
||||||
var p = permission;
|
var p = permission;
|
||||||
<td style="text-align: center;">
|
<td style="text-align: center;">
|
||||||
<TriStateCheckBox Value=@GetPermissionValue(p.Permissions, role.Name) Disabled=@GetPermissionDisabled(role.Name) OnChange="@(e => PermissionChanged(e, p.PermissionName, role.Name))" />
|
<TriStateCheckBox Value=@GetPermissionValue(p.Permissions, role.Name) Disabled="@GetPermissionDisabled(p.EntityName, p.PermissionName, role.Name)" OnChange="@(e => PermissionChanged(e, p.EntityName, p.PermissionName, role.Name))" />
|
||||||
</td>
|
</td>
|
||||||
}
|
}
|
||||||
</tr>
|
</tr>
|
||||||
@ -65,7 +66,7 @@
|
|||||||
{
|
{
|
||||||
var p = permission;
|
var p = permission;
|
||||||
<td style="text-align: center; width: 1px;">
|
<td style="text-align: center; width: 1px;">
|
||||||
<TriStateCheckBox Value=@GetPermissionValue(p.Permissions, userid) Disabled=false OnChange="@(e => PermissionChanged(e, p.PermissionName, userid))" />
|
<TriStateCheckBox Value=@GetPermissionValue(p.Permissions, userid) Disabled="@GetPermissionDisabled(p.EntityName, p.PermissionName, "")" OnChange="@(e => PermissionChanged(e, p.EntityName, p.PermissionName, userid))" />
|
||||||
</td>
|
</td>
|
||||||
}
|
}
|
||||||
</tr>
|
</tr>
|
||||||
@ -77,23 +78,16 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col">
|
<div class="col-11">
|
||||||
<table class="table table-borderless">
|
<AutoComplete OnSearch="GetUsers" Placeholder="@Localizer["Username.Enter"]" @ref="_user" />
|
||||||
<tbody>
|
</div>
|
||||||
<tr>
|
<div class="col-1">
|
||||||
<td class="input-group">
|
<button type="button" class="btn btn-primary" @onclick="AddUser">@SharedLocalizer["Add"]</button>
|
||||||
<input type="text" name="Username" class="form-control" placeholder="@Localizer["Username.Enter"]" @bind="@_username" />
|
|
||||||
<button type="button" class="btn btn-primary" @onclick="AddUser">@SharedLocalizer["Add"]</button>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
<br />
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col">
|
<div class="col">
|
||||||
<ModuleMessage Type="MessageType.Error" Message="@_message" />
|
<ModuleMessage Type="MessageType.Warning" Message="@_message" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -104,7 +98,7 @@
|
|||||||
private List<Role> _roles;
|
private List<Role> _roles;
|
||||||
private List<PermissionString> _permissions;
|
private List<PermissionString> _permissions;
|
||||||
private List<User> _users = new List<User>();
|
private List<User> _users = new List<User>();
|
||||||
private string _username = string.Empty;
|
private AutoComplete _user;
|
||||||
private string _message = string.Empty;
|
private string _message = string.Empty;
|
||||||
|
|
||||||
[Parameter]
|
[Parameter]
|
||||||
@ -135,10 +129,25 @@
|
|||||||
|
|
||||||
_permissions = new List<PermissionString>();
|
_permissions = new List<PermissionString>();
|
||||||
|
|
||||||
foreach (string permissionname in _permissionnames.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries))
|
foreach (string permissionname in _permissionnames.Split(',', StringSplitOptions.RemoveEmptyEntries))
|
||||||
{
|
{
|
||||||
// initialize with admin role
|
// permission names can be in the form of "EntityName:PermissionName:Roles"
|
||||||
_permissions.Add(new PermissionString { PermissionName = permissionname, Permissions = RoleNames.Admin });
|
if (permissionname.Contains(":"))
|
||||||
|
{
|
||||||
|
var segments = permissionname.Split(':');
|
||||||
|
if (segments.Length == 3)
|
||||||
|
{
|
||||||
|
if (!segments[2].Contains(RoleNames.Admin))
|
||||||
|
{
|
||||||
|
segments[2] = RoleNames.Admin + ";" + segments[2]; // ensure admin access
|
||||||
|
}
|
||||||
|
_permissions.Add(new PermissionString { EntityName = segments[0], PermissionName = segments[1], Permissions = segments[2] });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_permissions.Add(new PermissionString { EntityName = EntityName, PermissionName = permissionname, Permissions = RoleNames.Admin });
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(Permissions))
|
if (!string.IsNullOrEmpty(Permissions))
|
||||||
@ -146,14 +155,15 @@
|
|||||||
// populate permissions
|
// populate permissions
|
||||||
foreach (PermissionString permissionstring in UserSecurity.GetPermissionStrings(Permissions))
|
foreach (PermissionString permissionstring in UserSecurity.GetPermissionStrings(Permissions))
|
||||||
{
|
{
|
||||||
if (_permissions.Find(item => item.PermissionName == permissionstring.PermissionName) != null)
|
int index = _permissions.FindIndex(item => item.EntityName == permissionstring.EntityName && item.PermissionName == permissionstring.PermissionName);
|
||||||
|
if (index != -1)
|
||||||
{
|
{
|
||||||
_permissions[_permissions.FindIndex(item => item.PermissionName == permissionstring.PermissionName)].Permissions = permissionstring.Permissions;
|
_permissions[index].Permissions = permissionstring.Permissions;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (permissionstring.Permissions.Contains("["))
|
if (permissionstring.Permissions.Contains("["))
|
||||||
{
|
{
|
||||||
foreach (string user in permissionstring.Permissions.Split(new char[] { '[' }, StringSplitOptions.RemoveEmptyEntries))
|
foreach (string user in permissionstring.Permissions.Split('[', StringSplitOptions.RemoveEmptyEntries))
|
||||||
{
|
{
|
||||||
if (user.Contains("]"))
|
if (user.Contains("]"))
|
||||||
{
|
{
|
||||||
@ -169,6 +179,16 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private string GetPermissionName(PermissionString permission)
|
||||||
|
{
|
||||||
|
var permissionname = Localizer[permission.PermissionName].ToString();
|
||||||
|
if (!string.IsNullOrEmpty(EntityName))
|
||||||
|
{
|
||||||
|
permissionname += " " + Localizer[permission.EntityName].ToString();
|
||||||
|
}
|
||||||
|
return permissionname;
|
||||||
|
}
|
||||||
|
|
||||||
private bool? GetPermissionValue(string permissions, string securityKey)
|
private bool? GetPermissionValue(string permissions, string securityKey)
|
||||||
{
|
{
|
||||||
if ((";" + permissions + ";").Contains(";" + "!" + securityKey + ";"))
|
if ((";" + permissions + ";").Contains(";" + "!" + securityKey + ";"))
|
||||||
@ -188,38 +208,58 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool GetPermissionDisabled(string roleName)
|
private bool GetPermissionDisabled(string entityName, string permissionName, string roleName)
|
||||||
=> (roleName == RoleNames.Admin && !UserSecurity.IsAuthorized(PageState.User, RoleNames.Host)) ? true : false;
|
{
|
||||||
|
if (roleName == RoleNames.Admin && !UserSecurity.IsAuthorized(PageState.User, RoleNames.Host))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (entityName != EntityName && !UserSecurity.IsAuthorized(PageState.User, RoleNames.Admin))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<Dictionary<string, string>> GetUsers(string filter)
|
||||||
|
{
|
||||||
|
var users = await UserRoleService.GetUserRolesAsync(PageState.Site.SiteId, RoleNames.Registered);
|
||||||
|
return users.Where(item => item.User.DisplayName.Contains(filter, StringComparison.OrdinalIgnoreCase))
|
||||||
|
.ToDictionary(item => item.UserId.ToString(), item => item.User.DisplayName);
|
||||||
|
}
|
||||||
|
|
||||||
private async Task AddUser()
|
private async Task AddUser()
|
||||||
{
|
{
|
||||||
if (_users.Where(item => item.Username == _username).FirstOrDefault() == null)
|
if (!string.IsNullOrEmpty(_user.Key))
|
||||||
{
|
{
|
||||||
try
|
var user = await UserService.GetUserAsync(int.Parse(_user.Key), ModuleState.SiteId);
|
||||||
|
if (user != null && !_users.Any(item => item.UserId == user.UserId))
|
||||||
{
|
{
|
||||||
var user = await UserService.GetUserAsync(_username, ModuleState.SiteId);
|
_users.Add(user);
|
||||||
if (user != null)
|
|
||||||
{
|
|
||||||
_users.Add(user);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
_message = Localizer["Message.Username.DontExist"];
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else
|
||||||
_username = string.Empty;
|
{
|
||||||
|
_message = Localizer["Message.Username.DontExist"];
|
||||||
|
}
|
||||||
|
_user.Clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void PermissionChanged(bool? value, string permissionName, string securityId)
|
private void PermissionChanged(bool? value, string entityName, string permissionName, string securityId)
|
||||||
{
|
{
|
||||||
var selected = value;
|
var selected = value;
|
||||||
var permission = _permissions.Find(item => item.PermissionName == permissionName);
|
int index = _permissions.FindIndex(item => item.EntityName == entityName && item.PermissionName == permissionName);
|
||||||
if (permission != null)
|
if (index != -1)
|
||||||
{
|
{
|
||||||
var ids = permission.Permissions.Split(';').ToList();
|
var permission = _permissions[index];
|
||||||
|
|
||||||
|
var ids = permission.Permissions.Split(';').ToList();
|
||||||
ids.Remove(securityId); // remove grant permission
|
ids.Remove(securityId); // remove grant permission
|
||||||
ids.Remove("!" + securityId); // remove deny permission
|
ids.Remove("!" + securityId); // remove deny permission
|
||||||
|
|
||||||
@ -235,7 +275,7 @@
|
|||||||
break; // permission not specified
|
break; // permission not specified
|
||||||
}
|
}
|
||||||
|
|
||||||
_permissions[_permissions.FindIndex(item => item.PermissionName == permissionName)].Permissions = string.Join(";", ids.ToArray());
|
_permissions[index].Permissions = string.Join(";", ids.ToArray());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -248,9 +288,9 @@
|
|||||||
private void ValidatePermissions()
|
private void ValidatePermissions()
|
||||||
{
|
{
|
||||||
PermissionString permission;
|
PermissionString permission;
|
||||||
for (int i = 0; i < _permissions.Count; i++)
|
for (int index = 0; index < _permissions.Count; index++)
|
||||||
{
|
{
|
||||||
permission = _permissions[i];
|
permission = _permissions[index];
|
||||||
List<string> ids = permission.Permissions.Split(';', StringSplitOptions.RemoveEmptyEntries).ToList();
|
List<string> ids = permission.Permissions.Split(';', StringSplitOptions.RemoveEmptyEntries).ToList();
|
||||||
ids.Remove("!" + RoleNames.Everyone); // remove deny all users
|
ids.Remove("!" + RoleNames.Everyone); // remove deny all users
|
||||||
ids.Remove("!" + RoleNames.Unauthenticated); // remove deny unauthenticated
|
ids.Remove("!" + RoleNames.Unauthenticated); // remove deny unauthenticated
|
||||||
@ -266,7 +306,7 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
permission.Permissions = string.Join(";", ids.ToArray());
|
permission.Permissions = string.Join(";", ids.ToArray());
|
||||||
_permissions[i] = permission;
|
_permissions[index] = permission;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -262,7 +262,7 @@
|
|||||||
{
|
{
|
||||||
var interop = new Interop(JSRuntime);
|
var interop = new Interop(JSRuntime);
|
||||||
int pos = await interop.GetCaretPosition("rawhtmleditor");
|
int pos = await interop.GetCaretPosition("rawhtmleditor");
|
||||||
var image = "<img src=\"" + file.Url + "\" alt=\"" + ((!string.IsNullOrEmpty(file.Description)) ? file.Description : file.Name) + "\">";
|
var image = "<img src=\"" + file.Url + "\" alt=\"" + ((!string.IsNullOrEmpty(file.Description)) ? file.Description : file.Name) + "\" class=\"img-fluid\">";
|
||||||
_rawhtml = _rawhtml.Substring(0, pos) + image + _rawhtml.Substring(pos);
|
_rawhtml = _rawhtml.Substring(0, pos) + image + _rawhtml.Substring(pos);
|
||||||
_rawfilemanager = false;
|
_rawfilemanager = false;
|
||||||
}
|
}
|
||||||
|
@ -3,13 +3,13 @@
|
|||||||
|
|
||||||
@if (Name == Parent.ActiveTab)
|
@if (Name == Parent.ActiveTab)
|
||||||
{
|
{
|
||||||
<div id="@Name" class="tab-pane fade show active" role="tabpanel">
|
<div id="@(Parent.Id + Name)" class="tab-pane fade show active" role="tabpanel">
|
||||||
@ChildContent
|
@ChildContent
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
<div id="@Name" class="tab-pane fade" role="tabpanel">
|
<div id="@(Parent.Id + Name)" class="tab-pane fade" role="tabpanel">
|
||||||
@ChildContent
|
@ChildContent
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
@ -10,13 +10,13 @@
|
|||||||
<li class="nav-item" @key="tabPanel.Name">
|
<li class="nav-item" @key="tabPanel.Name">
|
||||||
@if (tabPanel.Name == ActiveTab)
|
@if (tabPanel.Name == ActiveTab)
|
||||||
{
|
{
|
||||||
<a class="nav-link active" data-bs-toggle="tab" href="#@tabPanel.Name" role="tab" @onclick:preventDefault="true">
|
<a class="nav-link active" data-bs-toggle="tab" href="#@(Id + tabPanel.Name)" role="tab" @onclick:preventDefault="true">
|
||||||
@tabPanel.DisplayHeading()
|
@tabPanel.DisplayHeading()
|
||||||
</a>
|
</a>
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
<a class="nav-link" data-bs-toggle="tab" href="#@tabPanel.Name" role="tab" @onclick:preventDefault="true">
|
<a class="nav-link" data-bs-toggle="tab" href="#@(Id + tabPanel.Name)" role="tab" @onclick:preventDefault="true">
|
||||||
@tabPanel.DisplayHeading()
|
@tabPanel.DisplayHeading()
|
||||||
</a>
|
</a>
|
||||||
}
|
}
|
||||||
@ -32,18 +32,31 @@
|
|||||||
</CascadingValue>
|
</CascadingValue>
|
||||||
|
|
||||||
@code {
|
@code {
|
||||||
private List<TabPanel> _tabPanels;
|
private List<TabPanel> _tabPanels;
|
||||||
|
private string _tabpanelid = string.Empty;
|
||||||
|
|
||||||
[Parameter]
|
[Parameter]
|
||||||
public RenderFragment ChildContent { get; set; } // contains the TabPanels
|
public RenderFragment ChildContent { get; set; } // contains the TabPanels
|
||||||
|
|
||||||
[Parameter]
|
[Parameter]
|
||||||
public string ActiveTab { get; set; } // optional - defaults to first TabPanel if not specified. Can also be set using a "tab=" querystring parameter.
|
public string ActiveTab { get; set; } // optional - defaults to first TabPanel if not specified. Can also be set using a "tab=" querystring parameter.
|
||||||
|
|
||||||
[Parameter]
|
[Parameter]
|
||||||
public bool Refresh { get; set; } // optional - used in scenarios where TabPanels are added/removed dynamically within a parent form. ActiveTab may need to be reset as well when this property is used.
|
public bool Refresh { get; set; } // optional - used in scenarios where TabPanels are added/removed dynamically within a parent form. ActiveTab may need to be reset as well when this property is used.
|
||||||
|
|
||||||
protected override void OnParametersSet()
|
[Parameter]
|
||||||
|
public string Id { get; set; } // optional - used to uniquely identify an instance of a tab strip component (will be set automatically if no value provided)
|
||||||
|
|
||||||
|
protected override void OnInitialized()
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(Id))
|
||||||
|
{
|
||||||
|
// create unique id for component
|
||||||
|
Id = "TabStrip_" + Guid.NewGuid().ToString("N") + "_" ;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnParametersSet()
|
||||||
{
|
{
|
||||||
if (PageState.QueryString.ContainsKey("tab"))
|
if (PageState.QueryString.ContainsKey("tab"))
|
||||||
{
|
{
|
||||||
|
@ -72,15 +72,24 @@ namespace Oqtane.Modules
|
|||||||
{
|
{
|
||||||
if (Resources != null && Resources.Exists(item => item.ResourceType == ResourceType.Script))
|
if (Resources != null && Resources.Exists(item => item.ResourceType == ResourceType.Script))
|
||||||
{
|
{
|
||||||
|
var interop = new Interop(JSRuntime);
|
||||||
var scripts = new List<object>();
|
var scripts = new List<object>();
|
||||||
|
var inline = 0;
|
||||||
foreach (Resource resource in Resources.Where(item => item.ResourceType == ResourceType.Script))
|
foreach (Resource resource in Resources.Where(item => item.ResourceType == ResourceType.Script))
|
||||||
{
|
{
|
||||||
var url = (resource.Url.Contains("://")) ? resource.Url : PageState.Alias.BaseUrl + "/" + resource.Url;
|
if (!string.IsNullOrEmpty(resource.Url))
|
||||||
scripts.Add(new { href = url, bundle = resource.Bundle ?? "", integrity = resource.Integrity ?? "", crossorigin = resource.CrossOrigin ?? "", es6module = resource.ES6Module });
|
{
|
||||||
|
var url = (resource.Url.Contains("://")) ? resource.Url : PageState.Alias.BaseUrl + resource.Url;
|
||||||
|
scripts.Add(new { href = url, bundle = resource.Bundle ?? "", integrity = resource.Integrity ?? "", crossorigin = resource.CrossOrigin ?? "", es6module = resource.ES6Module });
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
inline += 1;
|
||||||
|
await interop.IncludeScript(GetType().Namespace.ToLower() + inline.ToString(), "", "", "", resource.Content, resource.Location.ToString().ToLower());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (scripts.Any())
|
if (scripts.Any())
|
||||||
{
|
{
|
||||||
var interop = new Interop(JSRuntime);
|
|
||||||
await interop.IncludeScripts(scripts.ToArray());
|
await interop.IncludeScripts(scripts.ToArray());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -91,7 +100,7 @@ namespace Oqtane.Modules
|
|||||||
|
|
||||||
public string ModulePath()
|
public string ModulePath()
|
||||||
{
|
{
|
||||||
return "Modules/" + GetType().Namespace + "/";
|
return PageState?.Alias.BaseUrl + "/Modules/" + GetType().Namespace + "/";
|
||||||
}
|
}
|
||||||
|
|
||||||
// url methods
|
// url methods
|
||||||
@ -145,14 +154,23 @@ namespace Oqtane.Modules
|
|||||||
return Utilities.EditUrl(PageState.Alias.Path, path, moduleid, action, parameters);
|
return Utilities.EditUrl(PageState.Alias.Path, path, moduleid, action, parameters);
|
||||||
}
|
}
|
||||||
|
|
||||||
public string ContentUrl(int fileid)
|
public string FileUrl(string folderpath, string filename)
|
||||||
{
|
{
|
||||||
return ContentUrl(fileid, false);
|
return FileUrl(folderpath, filename, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public string ContentUrl(int fileid, bool asAttachment)
|
public string FileUrl(string folderpath, string filename, bool download)
|
||||||
{
|
{
|
||||||
return Utilities.ContentUrl(PageState.Alias, fileid, asAttachment);
|
return Utilities.FileUrl(PageState.Alias, folderpath, filename, download);
|
||||||
|
}
|
||||||
|
public string FileUrl(int fileid)
|
||||||
|
{
|
||||||
|
return FileUrl(fileid, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public string FileUrl(int fileid, bool download)
|
||||||
|
{
|
||||||
|
return Utilities.FileUrl(PageState.Alias, fileid, download);
|
||||||
}
|
}
|
||||||
|
|
||||||
public string ImageUrl(int fileid, int width, int height)
|
public string ImageUrl(int fileid, int width, int height)
|
||||||
@ -297,15 +315,10 @@ namespace Oqtane.Modules
|
|||||||
{
|
{
|
||||||
int pageId = ModuleState.PageId;
|
int pageId = ModuleState.PageId;
|
||||||
int moduleId = ModuleState.ModuleId;
|
int moduleId = ModuleState.ModuleId;
|
||||||
int? userId = null;
|
|
||||||
if (PageState.User != null)
|
|
||||||
{
|
|
||||||
userId = PageState.User.UserId;
|
|
||||||
}
|
|
||||||
string category = GetType().AssemblyQualifiedName;
|
string category = GetType().AssemblyQualifiedName;
|
||||||
string feature = Utilities.GetTypeNameLastSegment(category, 1);
|
string feature = Utilities.GetTypeNameLastSegment(category, 1);
|
||||||
|
|
||||||
await LoggingService.Log(alias, pageId, moduleId, userId, category, feature, function, level, exception, message, args);
|
await LoggingService.Log(alias, pageId, moduleId, PageState.User?.UserId, category, feature, function, level, exception, message, args);
|
||||||
}
|
}
|
||||||
|
|
||||||
public class Logger
|
public class Logger
|
||||||
@ -407,5 +420,17 @@ namespace Oqtane.Modules
|
|||||||
await _moduleBase.Log(null, LogLevel.Critical, "", exception, message, args);
|
await _moduleBase.Log(null, LogLevel.Critical, "", exception, message, args);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Obsolete("ContentUrl(int fileId) is deprecated. Use FileUrl(int fileId) instead.", false)]
|
||||||
|
public string ContentUrl(int fileid)
|
||||||
|
{
|
||||||
|
return ContentUrl(fileid, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Obsolete("ContentUrl(int fileId, bool asAttachment) is deprecated. Use FileUrl(int fileId, bool download) instead.", false)]
|
||||||
|
public string ContentUrl(int fileid, bool asAttachment)
|
||||||
|
{
|
||||||
|
return Utilities.FileUrl(PageState.Alias, fileid, asAttachment);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
<OutputType>Exe</OutputType>
|
<OutputType>Exe</OutputType>
|
||||||
<RazorLangVersion>3.0</RazorLangVersion>
|
<RazorLangVersion>3.0</RazorLangVersion>
|
||||||
<Configurations>Debug;Release</Configurations>
|
<Configurations>Debug;Release</Configurations>
|
||||||
<Version>3.2.0</Version>
|
<Version>3.3.1</Version>
|
||||||
<Product>Oqtane</Product>
|
<Product>Oqtane</Product>
|
||||||
<Authors>Shaun Walker</Authors>
|
<Authors>Shaun Walker</Authors>
|
||||||
<Company>.NET Foundation</Company>
|
<Company>.NET Foundation</Company>
|
||||||
@ -13,7 +13,7 @@
|
|||||||
<Copyright>.NET Foundation</Copyright>
|
<Copyright>.NET Foundation</Copyright>
|
||||||
<PackageProjectUrl>https://www.oqtane.org</PackageProjectUrl>
|
<PackageProjectUrl>https://www.oqtane.org</PackageProjectUrl>
|
||||||
<PackageLicenseUrl>https://github.com/oqtane/oqtane.framework/blob/dev/LICENSE</PackageLicenseUrl>
|
<PackageLicenseUrl>https://github.com/oqtane/oqtane.framework/blob/dev/LICENSE</PackageLicenseUrl>
|
||||||
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.2.0</PackageReleaseNotes>
|
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.3.1</PackageReleaseNotes>
|
||||||
<RepositoryUrl>https://github.com/oqtane/oqtane.framework</RepositoryUrl>
|
<RepositoryUrl>https://github.com/oqtane/oqtane.framework</RepositoryUrl>
|
||||||
<RepositoryType>Git</RepositoryType>
|
<RepositoryType>Git</RepositoryType>
|
||||||
<RootNamespace>Oqtane</RootNamespace>
|
<RootNamespace>Oqtane</RootNamespace>
|
||||||
|
@ -16,6 +16,7 @@ using Microsoft.JSInterop;
|
|||||||
using Oqtane.Documentation;
|
using Oqtane.Documentation;
|
||||||
using Oqtane.Modules;
|
using Oqtane.Modules;
|
||||||
using Oqtane.Services;
|
using Oqtane.Services;
|
||||||
|
using Oqtane.Shared;
|
||||||
using Oqtane.UI;
|
using Oqtane.UI;
|
||||||
|
|
||||||
namespace Oqtane.Client
|
namespace Oqtane.Client
|
||||||
@ -56,14 +57,18 @@ namespace Oqtane.Client
|
|||||||
RegisterClientStartups(assembly, builder.Services);
|
RegisterClientStartups(assembly, builder.Services);
|
||||||
}
|
}
|
||||||
|
|
||||||
await builder.Build().RunAsync();
|
var host = builder.Build();
|
||||||
|
|
||||||
|
await SetCultureFromLocalizationCookie(host.Services);
|
||||||
|
|
||||||
|
await host.RunAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static async Task LoadClientAssemblies(HttpClient http, IServiceProvider serviceProvider)
|
private static async Task LoadClientAssemblies(HttpClient http, IServiceProvider serviceProvider)
|
||||||
{
|
{
|
||||||
var dlls = new Dictionary<string, byte[]>();
|
var dlls = new Dictionary<string, byte[]>();
|
||||||
var pdbs = new Dictionary<string, byte[]>();
|
var pdbs = new Dictionary<string, byte[]>();
|
||||||
var filter = new List<string>();
|
var list = new List<string>();
|
||||||
|
|
||||||
var jsRuntime = serviceProvider.GetRequiredService<IJSRuntime>();
|
var jsRuntime = serviceProvider.GetRequiredService<IJSRuntime>();
|
||||||
var interop = new Interop(jsRuntime);
|
var interop = new Interop(jsRuntime);
|
||||||
@ -81,14 +86,14 @@ namespace Oqtane.Client
|
|||||||
var file = files.FirstOrDefault(item => item.Contains(assembly));
|
var file = files.FirstOrDefault(item => item.Contains(assembly));
|
||||||
if (file == null)
|
if (file == null)
|
||||||
{
|
{
|
||||||
filter.Add(assembly);
|
list.Add(assembly);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// check if newer version available
|
// check if newer version available
|
||||||
if (GetFileDate(assembly) > GetFileDate(file))
|
if (GetFileDate(assembly) > GetFileDate(file))
|
||||||
{
|
{
|
||||||
filter.Add(assembly);
|
list.Add(assembly);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -96,7 +101,7 @@ namespace Oqtane.Client
|
|||||||
// get assemblies already downloaded
|
// get assemblies already downloaded
|
||||||
foreach (var file in files)
|
foreach (var file in files)
|
||||||
{
|
{
|
||||||
if (assemblies.Contains(file) && !filter.Contains(file))
|
if (assemblies.Contains(file) && !list.Contains(file))
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@ -117,6 +122,7 @@ namespace Oqtane.Client
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
await interop.RemoveIndexedDBItem(file);
|
await interop.RemoveIndexedDBItem(file);
|
||||||
|
await interop.RemoveIndexedDBItem(file.Replace(".dll", ".pdb"));
|
||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
{
|
{
|
||||||
@ -127,13 +133,13 @@ namespace Oqtane.Client
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
filter.Add("*");
|
list.Add("*");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (filter.Count != 0)
|
if (list.Count != 0)
|
||||||
{
|
{
|
||||||
// get assemblies from server and load into client app domain
|
// get assemblies from server and load into client app domain
|
||||||
var zip = await http.GetByteArrayAsync($"/api/Installation/load?list=" + string.Join(",", filter));
|
var zip = await http.GetByteArrayAsync($"/api/Installation/load?list=" + string.Join(",", list));
|
||||||
|
|
||||||
// asemblies and debug symbols are packaged in a zip file
|
// asemblies and debug symbols are packaged in a zip file
|
||||||
using (ZipArchive archive = new ZipArchive(new MemoryStream(zip)))
|
using (ZipArchive archive = new ZipArchive(new MemoryStream(zip)))
|
||||||
@ -219,7 +225,7 @@ namespace Oqtane.Client
|
|||||||
var localizationCookie = await interop.GetCookie(CookieRequestCultureProvider.DefaultCookieName);
|
var localizationCookie = await interop.GetCookie(CookieRequestCultureProvider.DefaultCookieName);
|
||||||
var culture = CookieRequestCultureProvider.ParseCookieValue(localizationCookie)?.UICultures?[0].Value;
|
var culture = CookieRequestCultureProvider.ParseCookieValue(localizationCookie)?.UICultures?[0].Value;
|
||||||
var localizationService = serviceProvider.GetRequiredService<ILocalizationService>();
|
var localizationService = serviceProvider.GetRequiredService<ILocalizationService>();
|
||||||
var cultures = await localizationService.GetCulturesAsync();
|
var cultures = await localizationService.GetCulturesAsync(false);
|
||||||
|
|
||||||
if (culture == null || !cultures.Any(c => c.Name.Equals(culture, StringComparison.OrdinalIgnoreCase)))
|
if (culture == null || !cultures.Any(c => c.Name.Equals(culture, StringComparison.OrdinalIgnoreCase)))
|
||||||
{
|
{
|
||||||
|
@ -136,7 +136,7 @@
|
|||||||
<value>Please Enter All Required Fields. Ensure Passwords Match And Email Address Provided Is Valid.</value>
|
<value>Please Enter All Required Fields. Ensure Passwords Match And Email Address Provided Is Valid.</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="Message.Password.Invalid" xml:space="preserve">
|
<data name="Message.Password.Invalid" xml:space="preserve">
|
||||||
<value>The Password Provided Does Not Meet The Password Policy. Please Verify The Minimum Password Length And Complexity Requirements.</value>
|
<value>The Password Provided Does Not Meet The Complexity Policy. Passwords Must Be At Least 6 Characters In Length And Contain Uppercase, Lowercase, Numeric, And Punctuation Characters.</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="Register" xml:space="preserve">
|
<data name="Register" xml:space="preserve">
|
||||||
<value>Please Register Me For Major Product Updates And Security Bulletins</value>
|
<value>Please Register Me For Major Product Updates And Security Bulletins</value>
|
||||||
|
126
Oqtane.Client/Resources/Modules/Admin/Api/Edit.resx
Normal file
126
Oqtane.Client/Resources/Modules/Admin/Api/Edit.resx
Normal file
@ -0,0 +1,126 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<root>
|
||||||
|
<!--
|
||||||
|
Microsoft ResX Schema
|
||||||
|
|
||||||
|
Version 2.0
|
||||||
|
|
||||||
|
The primary goals of this format is to allow a simple XML format
|
||||||
|
that is mostly human readable. The generation and parsing of the
|
||||||
|
various data types are done through the TypeConverter classes
|
||||||
|
associated with the data types.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
... ado.net/XML headers & schema ...
|
||||||
|
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||||
|
<resheader name="version">2.0</resheader>
|
||||||
|
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
|
||||||
|
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
|
||||||
|
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
|
||||||
|
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
|
||||||
|
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
|
||||||
|
<value>[base64 mime encoded serialized .NET Framework object]</value>
|
||||||
|
</data>
|
||||||
|
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||||
|
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||||
|
<comment>This is a comment</comment>
|
||||||
|
</data>
|
||||||
|
|
||||||
|
There are any number of "resheader" rows that contain simple
|
||||||
|
name/value pairs.
|
||||||
|
|
||||||
|
Each data row contains a name, and value. The row also contains a
|
||||||
|
type or mimetype. Type corresponds to a .NET class that support
|
||||||
|
text/value conversion through the TypeConverter architecture.
|
||||||
|
Classes that don't support this are serialized and stored with the
|
||||||
|
mimetype set.
|
||||||
|
|
||||||
|
The mimetype is used for serialized objects, and tells the
|
||||||
|
ResXResourceReader how to depersist the object. This is currently not
|
||||||
|
extensible. For a given mimetype the value must be set accordingly:
|
||||||
|
|
||||||
|
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||||
|
that the ResXResourceWriter will generate, however the reader can
|
||||||
|
read any of the formats listed below.
|
||||||
|
|
||||||
|
mimetype: application/x-microsoft.net.object.binary.base64
|
||||||
|
value : The object must be serialized with
|
||||||
|
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
||||||
|
: and then encoded with base64 encoding.
|
||||||
|
|
||||||
|
mimetype: application/x-microsoft.net.object.soap.base64
|
||||||
|
value : The object must be serialized with
|
||||||
|
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||||
|
: and then encoded with base64 encoding.
|
||||||
|
|
||||||
|
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||||
|
value : The object must be serialized into a byte array
|
||||||
|
: using a System.ComponentModel.TypeConverter
|
||||||
|
: and then encoded with base64 encoding.
|
||||||
|
-->
|
||||||
|
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||||
|
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||||
|
<xsd:element name="root" msdata:IsDataSet="true">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:choice maxOccurs="unbounded">
|
||||||
|
<xsd:element name="metadata">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||||
|
</xsd:sequence>
|
||||||
|
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||||
|
<xsd:attribute name="type" type="xsd:string" />
|
||||||
|
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||||
|
<xsd:attribute ref="xml:space" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
<xsd:element name="assembly">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:attribute name="alias" type="xsd:string" />
|
||||||
|
<xsd:attribute name="name" type="xsd:string" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
<xsd:element name="data">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||||
|
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||||
|
</xsd:sequence>
|
||||||
|
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
||||||
|
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||||
|
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||||
|
<xsd:attribute ref="xml:space" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
<xsd:element name="resheader">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||||
|
</xsd:sequence>
|
||||||
|
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
</xsd:choice>
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
</xsd:schema>
|
||||||
|
<resheader name="resmimetype">
|
||||||
|
<value>text/microsoft-resx</value>
|
||||||
|
</resheader>
|
||||||
|
<resheader name="version">
|
||||||
|
<value>2.0</value>
|
||||||
|
</resheader>
|
||||||
|
<resheader name="reader">
|
||||||
|
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||||
|
</resheader>
|
||||||
|
<resheader name="writer">
|
||||||
|
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||||
|
</resheader>
|
||||||
|
<data name="EntityName.HelpText" xml:space="preserve">
|
||||||
|
<value>The Name Of The Entity</value>
|
||||||
|
</data>
|
||||||
|
<data name="EntityName.Text" xml:space="preserve">
|
||||||
|
<value>Entity:</value>
|
||||||
|
</data>
|
||||||
|
</root>
|
126
Oqtane.Client/Resources/Modules/Admin/Api/Index.resx
Normal file
126
Oqtane.Client/Resources/Modules/Admin/Api/Index.resx
Normal file
@ -0,0 +1,126 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<root>
|
||||||
|
<!--
|
||||||
|
Microsoft ResX Schema
|
||||||
|
|
||||||
|
Version 2.0
|
||||||
|
|
||||||
|
The primary goals of this format is to allow a simple XML format
|
||||||
|
that is mostly human readable. The generation and parsing of the
|
||||||
|
various data types are done through the TypeConverter classes
|
||||||
|
associated with the data types.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
... ado.net/XML headers & schema ...
|
||||||
|
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||||
|
<resheader name="version">2.0</resheader>
|
||||||
|
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
|
||||||
|
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
|
||||||
|
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
|
||||||
|
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
|
||||||
|
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
|
||||||
|
<value>[base64 mime encoded serialized .NET Framework object]</value>
|
||||||
|
</data>
|
||||||
|
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||||
|
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||||
|
<comment>This is a comment</comment>
|
||||||
|
</data>
|
||||||
|
|
||||||
|
There are any number of "resheader" rows that contain simple
|
||||||
|
name/value pairs.
|
||||||
|
|
||||||
|
Each data row contains a name, and value. The row also contains a
|
||||||
|
type or mimetype. Type corresponds to a .NET class that support
|
||||||
|
text/value conversion through the TypeConverter architecture.
|
||||||
|
Classes that don't support this are serialized and stored with the
|
||||||
|
mimetype set.
|
||||||
|
|
||||||
|
The mimetype is used for serialized objects, and tells the
|
||||||
|
ResXResourceReader how to depersist the object. This is currently not
|
||||||
|
extensible. For a given mimetype the value must be set accordingly:
|
||||||
|
|
||||||
|
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||||
|
that the ResXResourceWriter will generate, however the reader can
|
||||||
|
read any of the formats listed below.
|
||||||
|
|
||||||
|
mimetype: application/x-microsoft.net.object.binary.base64
|
||||||
|
value : The object must be serialized with
|
||||||
|
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
||||||
|
: and then encoded with base64 encoding.
|
||||||
|
|
||||||
|
mimetype: application/x-microsoft.net.object.soap.base64
|
||||||
|
value : The object must be serialized with
|
||||||
|
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||||
|
: and then encoded with base64 encoding.
|
||||||
|
|
||||||
|
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||||
|
value : The object must be serialized into a byte array
|
||||||
|
: using a System.ComponentModel.TypeConverter
|
||||||
|
: and then encoded with base64 encoding.
|
||||||
|
-->
|
||||||
|
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||||
|
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||||
|
<xsd:element name="root" msdata:IsDataSet="true">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:choice maxOccurs="unbounded">
|
||||||
|
<xsd:element name="metadata">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||||
|
</xsd:sequence>
|
||||||
|
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||||
|
<xsd:attribute name="type" type="xsd:string" />
|
||||||
|
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||||
|
<xsd:attribute ref="xml:space" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
<xsd:element name="assembly">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:attribute name="alias" type="xsd:string" />
|
||||||
|
<xsd:attribute name="name" type="xsd:string" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
<xsd:element name="data">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||||
|
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||||
|
</xsd:sequence>
|
||||||
|
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
||||||
|
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||||
|
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||||
|
<xsd:attribute ref="xml:space" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
<xsd:element name="resheader">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||||
|
</xsd:sequence>
|
||||||
|
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
</xsd:choice>
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
</xsd:schema>
|
||||||
|
<resheader name="resmimetype">
|
||||||
|
<value>text/microsoft-resx</value>
|
||||||
|
</resheader>
|
||||||
|
<resheader name="version">
|
||||||
|
<value>2.0</value>
|
||||||
|
</resheader>
|
||||||
|
<resheader name="reader">
|
||||||
|
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||||
|
</resheader>
|
||||||
|
<resheader name="writer">
|
||||||
|
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||||
|
</resheader>
|
||||||
|
<data name="Entity" xml:space="preserve">
|
||||||
|
<value>Entity</value>
|
||||||
|
</data>
|
||||||
|
<data name="Permissions" xml:space="preserve">
|
||||||
|
<value>Permissions</value>
|
||||||
|
</data>
|
||||||
|
</root>
|
@ -117,45 +117,30 @@
|
|||||||
<resheader name="writer">
|
<resheader name="writer">
|
||||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||||
</resheader>
|
</resheader>
|
||||||
<data name="The Only Supported Culture That Has Been Defined Is English" xml:space="preserve">
|
|
||||||
<value>The Only Supported Culture That Has Been Defined Is English</value>
|
|
||||||
</data>
|
|
||||||
<data name="Error.Language.Add" xml:space="preserve">
|
<data name="Error.Language.Add" xml:space="preserve">
|
||||||
<value>Error Adding Language</value>
|
<value>Error Adding Language</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="Translated.HelpText" xml:space="preserve">
|
||||||
|
<value>Specify If You Wish To Select Languages That Have Translations Installed</value>
|
||||||
|
</data>
|
||||||
<data name="Name.HelpText" xml:space="preserve">
|
<data name="Name.HelpText" xml:space="preserve">
|
||||||
<value>Name Of The Langauage</value>
|
<value>Name Of The Langauage</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="IsDefault.HelpText" xml:space="preserve">
|
<data name="IsDefault.HelpText" xml:space="preserve">
|
||||||
<value>Indicates Whether Or Not This Language Is The Default For The Site</value>
|
<value>Indicates Whether Or Not This Language Is The Default For The Site</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="Translated.Text" xml:space="preserve">
|
||||||
|
<value>Translated?</value>
|
||||||
|
</data>
|
||||||
<data name="Name.Text" xml:space="preserve">
|
<data name="Name.Text" xml:space="preserve">
|
||||||
<value>Name:</value>
|
<value>Name:</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="IsDefault.Text" xml:space="preserve">
|
<data name="IsDefault.Text" xml:space="preserve">
|
||||||
<value>Default?</value>
|
<value>Default?</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="AllLanguages" xml:space="preserve">
|
|
||||||
<value>All The Installed Languages Have Been Added.</value>
|
|
||||||
</data>
|
|
||||||
<data name="Error.Language.Download" xml:space="preserve">
|
|
||||||
<value>Error Downloading Translation</value>
|
|
||||||
</data>
|
|
||||||
<data name="OnlyEnglish" xml:space="preserve">
|
|
||||||
<value>The Only Installed Language Is English</value>
|
|
||||||
</data>
|
|
||||||
<data name="Success.Language.Download" xml:space="preserve">
|
|
||||||
<value>Translation Downloaded Successfully. Click Install To Complete Installation.</value>
|
|
||||||
</data>
|
|
||||||
<data name="Success.Language.Install" xml:space="preserve">
|
<data name="Success.Language.Install" xml:space="preserve">
|
||||||
<value>Translations Installed Successfully. You Must <a href={0}>Restart</a> Your Application To Apply These Changes.</value>
|
<value>Translations Installed Successfully. You Must <a href={0}>Restart</a> Your Application To Apply These Changes.</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="Search.NoResults" xml:space="preserve">
|
|
||||||
<value>No Translations Match The Criteria Provided Or Package Service Is Disabled</value>
|
|
||||||
</data>
|
|
||||||
<data name="Download.Heading" xml:space="preserve">
|
|
||||||
<value>Translations</value>
|
|
||||||
</data>
|
|
||||||
<data name="LanguageUpload.HelpText" xml:space="preserve">
|
<data name="LanguageUpload.HelpText" xml:space="preserve">
|
||||||
<value>Upload one or more translation packages. Once they are uploaded click Install to complete the installation.</value>
|
<value>Upload one or more translation packages. Once they are uploaded click Install to complete the installation.</value>
|
||||||
</data>
|
</data>
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<root>
|
<root>
|
||||||
<!--
|
<!--
|
||||||
Microsoft ResX Schema
|
Microsoft ResX Schema
|
||||||
@ -204,6 +204,9 @@
|
|||||||
<data name="Error.Translation.Download" xml:space="preserve">
|
<data name="Error.Translation.Download" xml:space="preserve">
|
||||||
<value>Error Downloading Translation</value>
|
<value>Error Downloading Translation</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="Search.PackageNameMissing" xml:space="preserve">
|
||||||
|
<value>A Package Name Was Not Provided For The Module</value>
|
||||||
|
</data>
|
||||||
<data name="Search.NoResults" xml:space="preserve">
|
<data name="Search.NoResults" xml:space="preserve">
|
||||||
<value>No Translations Exist For This Module Or Package Service Is Disabled</value>
|
<value>No Translations Exist For This Module Or Package Service Is Disabled</value>
|
||||||
</data>
|
</data>
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<root>
|
<root>
|
||||||
<!--
|
<!--
|
||||||
Microsoft ResX Schema
|
Microsoft ResX Schema
|
||||||
@ -166,16 +166,22 @@
|
|||||||
<value>Error Permanently Deleting Modules</value>
|
<value>Error Permanently Deleting Modules</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="DeleteAllPages.Header" xml:space="preserve">
|
<data name="DeleteAllPages.Header" xml:space="preserve">
|
||||||
<value>Delete All Pages</value>
|
<value>Remove All Deleted Pages</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="DeleteAllPages.Message" xml:space="preserve">
|
<data name="DeleteAllPages.Message" xml:space="preserve">
|
||||||
<value>Are You Sure You Wish To Permanently Delete All Pages?</value>
|
<value>Are You Sure You Wish To Permanently Remove All Deleted Pages?</value>
|
||||||
|
</data>
|
||||||
|
<data name="DeleteAllPages.Text" xml:space="preserve">
|
||||||
|
<value>Remove All Deleted Pages</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="DeleteAllModules.Header" xml:space="preserve">
|
<data name="DeleteAllModules.Header" xml:space="preserve">
|
||||||
<value>Delete All Modules</value>
|
<value>Remove All Deleted Modules</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="DeleteAllModules.Message" xml:space="preserve">
|
<data name="DeleteAllModules.Message" xml:space="preserve">
|
||||||
<value>Are You Sure You Wish To Permanently Delete All Modules?</value>
|
<value>Are You Sure You Wish To Permanently Remove All Deleted Modules?</value>
|
||||||
|
</data>
|
||||||
|
<data name="DeleteAllModules.Text" xml:space="preserve">
|
||||||
|
<value>Remove All Deleted Modules</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="Pages.Heading" xml:space="preserve">
|
<data name="Pages.Heading" xml:space="preserve">
|
||||||
<value>Pages</value>
|
<value>Pages</value>
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<root>
|
<root>
|
||||||
<!--
|
<!--
|
||||||
Microsoft ResX Schema
|
Microsoft ResX Schema
|
||||||
@ -121,7 +121,7 @@
|
|||||||
<value>User: </value>
|
<value>User: </value>
|
||||||
</data>
|
</data>
|
||||||
<data name="User.Select" xml:space="preserve">
|
<data name="User.Select" xml:space="preserve">
|
||||||
<value>Select User</value>
|
<value>Enter User's Name</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="Users" xml:space="preserve">
|
<data name="Users" xml:space="preserve">
|
||||||
<value>Users</value>
|
<value>Users</value>
|
||||||
@ -129,9 +129,6 @@
|
|||||||
<data name="Error.User.Load" xml:space="preserve">
|
<data name="Error.User.Load" xml:space="preserve">
|
||||||
<value>Error Loading Users</value>
|
<value>Error Loading Users</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="Error.User.LoadRole" xml:space="preserve">
|
|
||||||
<value>Error Loading User Roles</value>
|
|
||||||
</data>
|
|
||||||
<data name="Success.User.AssignedRole" xml:space="preserve">
|
<data name="Success.User.AssignedRole" xml:space="preserve">
|
||||||
<value>User Assigned To Role</value>
|
<value>User Assigned To Role</value>
|
||||||
</data>
|
</data>
|
||||||
@ -151,7 +148,7 @@
|
|||||||
<value>The role you are assigning users to</value>
|
<value>The role you are assigning users to</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="User.HelpText" xml:space="preserve">
|
<data name="User.HelpText" xml:space="preserve">
|
||||||
<value>Select a user</value>
|
<value>Enter the name of a user</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="EffectiveDate.HelpText" xml:space="preserve">
|
<data name="EffectiveDate.HelpText" xml:space="preserve">
|
||||||
<value>The date that this role assignment is active</value>
|
<value>The date that this role assignment is active</value>
|
||||||
|
@ -195,13 +195,13 @@
|
|||||||
<data name="UseSsl.HelpText" xml:space="preserve">
|
<data name="UseSsl.HelpText" xml:space="preserve">
|
||||||
<value>Specify if SSL is required for your SMTP server</value>
|
<value>Specify if SSL is required for your SMTP server</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="SmptUsername.HelpText" xml:space="preserve">
|
<data name="SmtpUsername.HelpText" xml:space="preserve">
|
||||||
<value>Enter the username for your SMTP account</value>
|
<value>Enter the username for your SMTP account</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="SmtpPassword.HelpText" xml:space="preserve">
|
<data name="SmtpPassword.HelpText" xml:space="preserve">
|
||||||
<value>Enter the password for your SMTP account</value>
|
<value>Enter the password for your SMTP account</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="SmptSender.HelpText" xml:space="preserve">
|
<data name="SmtpSender.HelpText" xml:space="preserve">
|
||||||
<value>Enter the email which emails will be sent from. Please note that this email address may need to be authorized with the SMTP server.</value>
|
<value>Enter the email which emails will be sent from. Please note that this email address may need to be authorized with the SMTP server.</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="EnablePWA.HelpText" xml:space="preserve">
|
<data name="EnablePWA.HelpText" xml:space="preserve">
|
||||||
@ -243,13 +243,13 @@
|
|||||||
<data name="UseSsl.Text" xml:space="preserve">
|
<data name="UseSsl.Text" xml:space="preserve">
|
||||||
<value>SSL Enabled: </value>
|
<value>SSL Enabled: </value>
|
||||||
</data>
|
</data>
|
||||||
<data name="SmptUsername.Text" xml:space="preserve">
|
<data name="SmtpUsername.Text" xml:space="preserve">
|
||||||
<value>Username: </value>
|
<value>Username: </value>
|
||||||
</data>
|
</data>
|
||||||
<data name="SmtpPassword.Text" xml:space="preserve">
|
<data name="SmtpPassword.Text" xml:space="preserve">
|
||||||
<value>Password: </value>
|
<value>Password: </value>
|
||||||
</data>
|
</data>
|
||||||
<data name="SmptSender.Text" xml:space="preserve">
|
<data name="SmtpSender.Text" xml:space="preserve">
|
||||||
<value>Email Sender: </value>
|
<value>Email Sender: </value>
|
||||||
</data>
|
</data>
|
||||||
<data name="EnablePWA.Text" xml:space="preserve">
|
<data name="EnablePWA.Text" xml:space="preserve">
|
||||||
@ -339,4 +339,10 @@
|
|||||||
<data name="HomePage.Text" xml:space="preserve">
|
<data name="HomePage.Text" xml:space="preserve">
|
||||||
<value>Home Page:</value>
|
<value>Home Page:</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="SmtpRelay.HelpText" xml:space="preserve">
|
||||||
|
<value>Only specify this option if you have properly configured an SMTP Relay Service to route your outgoing mail. This option will send notifications from the user's email rather than from the Email Sender specified above.</value>
|
||||||
|
</data>
|
||||||
|
<data name="SmtpRelay.Text" xml:space="preserve">
|
||||||
|
<value>Relay Configured?</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
@ -1,4 +1,4 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<root>
|
<root>
|
||||||
<!--
|
<!--
|
||||||
Microsoft ResX Schema
|
Microsoft ResX Schema
|
||||||
@ -133,10 +133,10 @@
|
|||||||
<value>No Results Returned</value>
|
<value>No Results Returned</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="Tenant.HelpText" xml:space="preserve">
|
<data name="Tenant.HelpText" xml:space="preserve">
|
||||||
<value>Select the tenant for the SQL server</value>
|
<value>Select the tenant associated with the database server</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="SqlQuery.HelpText" xml:space="preserve">
|
<data name="SqlQuery.HelpText" xml:space="preserve">
|
||||||
<value>Enter the query for the SQL server</value>
|
<value>Enter the SQL query for the database server</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="SqlQuery.Text" xml:space="preserve">
|
<data name="SqlQuery.Text" xml:space="preserve">
|
||||||
<value>SQL Query: </value>
|
<value>SQL Query: </value>
|
||||||
|
@ -210,7 +210,10 @@
|
|||||||
<data name="Options.Heading" xml:space="preserve">
|
<data name="Options.Heading" xml:space="preserve">
|
||||||
<value>Options</value>
|
<value>Options</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="Register" xml:space="preserve">
|
<data name="Log.Heading" xml:space="preserve">
|
||||||
|
<value>Log</value>
|
||||||
|
</data>
|
||||||
|
<data name="Register" xml:space="preserve">
|
||||||
<value>Please Register Me For Major Product Updates And Security Bulletins</value>
|
<value>Please Register Me For Major Product Updates And Security Bulletins</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="Success.Register" xml:space="preserve">
|
<data name="Success.Register" xml:space="preserve">
|
||||||
@ -276,4 +279,10 @@
|
|||||||
<data name="Environment.Text" xml:space="preserve">
|
<data name="Environment.Text" xml:space="preserve">
|
||||||
<value>Environment:</value>
|
<value>Environment:</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="Log.Text" xml:space="preserve">
|
||||||
|
<value>Log:</value>
|
||||||
|
</data>
|
||||||
|
<data name="Log.HelpText" xml:space="preserve">
|
||||||
|
<value>System log information for current day</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
@ -120,6 +120,9 @@
|
|||||||
<data name="Message.Password.Invalid" xml:space="preserve">
|
<data name="Message.Password.Invalid" xml:space="preserve">
|
||||||
<value>Passwords Entered Do Not Match</value>
|
<value>Passwords Entered Do Not Match</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="Message.Password.Complexity" xml:space="preserve">
|
||||||
|
<value>Password Provided Does Not Meet The Complexity Policy</value>
|
||||||
|
</data>
|
||||||
<data name="From" xml:space="preserve">
|
<data name="From" xml:space="preserve">
|
||||||
<value>From</value>
|
<value>From</value>
|
||||||
</data>
|
</data>
|
||||||
|
@ -120,6 +120,9 @@
|
|||||||
<data name="Message.Password.NoMatch" xml:space="preserve">
|
<data name="Message.Password.NoMatch" xml:space="preserve">
|
||||||
<value>Passwords Entered Do Not Match</value>
|
<value>Passwords Entered Do Not Match</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="Message.Password.Complexity" xml:space="preserve">
|
||||||
|
<value>Password Provided Does Not Meet The Complexity Policy</value>
|
||||||
|
</data>
|
||||||
<data name="Identity.Name" xml:space="preserve">
|
<data name="Identity.Name" xml:space="preserve">
|
||||||
<value>Identity</value>
|
<value>Identity</value>
|
||||||
</data>
|
</data>
|
||||||
|
@ -211,25 +211,25 @@
|
|||||||
<value>Allow Login?</value>
|
<value>Allow Login?</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="Authority.HelpText" xml:space="preserve">
|
<data name="Authority.HelpText" xml:space="preserve">
|
||||||
<value>The Authority Url or Issuer Url associated with the OpenID Connect provider</value>
|
<value>The authority url or issuer url associated with the identity provider</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="Authority.Text" xml:space="preserve">
|
<data name="Authority.Text" xml:space="preserve">
|
||||||
<value>Authority:</value>
|
<value>Authority:</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="AuthorizationUrl.HelpText" xml:space="preserve">
|
<data name="AuthorizationUrl.HelpText" xml:space="preserve">
|
||||||
<value>The endpoint for obtaining an Authorization Code</value>
|
<value>The endpoint for obtaining an authorization code</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="AuthorizationUrl.Text" xml:space="preserve">
|
<data name="AuthorizationUrl.Text" xml:space="preserve">
|
||||||
<value>Authorization Url:</value>
|
<value>Authorization Url:</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="ClientID.HelpText" xml:space="preserve">
|
<data name="ClientID.HelpText" xml:space="preserve">
|
||||||
<value>The Client ID from the provider</value>
|
<value>The client id for the identity provider</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="ClientID.Text" xml:space="preserve">
|
<data name="ClientID.Text" xml:space="preserve">
|
||||||
<value>Client ID:</value>
|
<value>Client ID:</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="ClientSecret.HelpText" xml:space="preserve">
|
<data name="ClientSecret.HelpText" xml:space="preserve">
|
||||||
<value>The Client Secret from the provider</value>
|
<value>The client secret for the identity provider</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="ClientSecret.Text" xml:space="preserve">
|
<data name="ClientSecret.Text" xml:space="preserve">
|
||||||
<value>Client Secret:</value>
|
<value>Client Secret:</value>
|
||||||
@ -247,7 +247,7 @@
|
|||||||
<value>Domain Filter:</value>
|
<value>Domain Filter:</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="EmailClaimType.HelpText" xml:space="preserve">
|
<data name="EmailClaimType.HelpText" xml:space="preserve">
|
||||||
<value>The name of the email address claim provided by the provider</value>
|
<value>The name of the email address claim provided by the identity provider</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="EmailClaimType.Text" xml:space="preserve">
|
<data name="EmailClaimType.Text" xml:space="preserve">
|
||||||
<value>Email Claim:</value>
|
<value>Email Claim:</value>
|
||||||
@ -259,7 +259,7 @@
|
|||||||
<value>Lockout Settings</value>
|
<value>Lockout Settings</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="MetadataUrl.HelpText" xml:space="preserve">
|
<data name="MetadataUrl.HelpText" xml:space="preserve">
|
||||||
<value>The discovery endpoint for obtaining metadata for this provider. Only specify if the OpenID Connect provider does not use the standard approach (ie. /.well-known/openid-configuration)</value>
|
<value>The discovery endpoint for obtaining metadata for this identity provider. Only specify if the identity provider does not use the standard approach (ie. /.well-known/openid-configuration)</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="MetadataUrl.Text" xml:space="preserve">
|
<data name="MetadataUrl.Text" xml:space="preserve">
|
||||||
<value>Metadata Url:</value>
|
<value>Metadata Url:</value>
|
||||||
@ -268,7 +268,7 @@
|
|||||||
<value>Password Settings</value>
|
<value>Password Settings</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="PKCE.HelpText" xml:space="preserve">
|
<data name="PKCE.HelpText" xml:space="preserve">
|
||||||
<value>Indicate if the provider supports Proof Key for Code Exchange (PKCE)</value>
|
<value>Indicate if the identity provider supports proof key for code exchange (PKCE)</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="PKCE.Text" xml:space="preserve">
|
<data name="PKCE.Text" xml:space="preserve">
|
||||||
<value>Use PKCE?</value>
|
<value>Use PKCE?</value>
|
||||||
@ -286,25 +286,25 @@
|
|||||||
<value>Provider Type:</value>
|
<value>Provider Type:</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="RedirectUrl.HelpText" xml:space="preserve">
|
<data name="RedirectUrl.HelpText" xml:space="preserve">
|
||||||
<value>The Redirect Url (or Callback Url) which usually needs to be registered with the provider</value>
|
<value>The redirect url (or callback url) which usually needs to be registered with the identity provider</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="RedirectUrl.Text" xml:space="preserve">
|
<data name="RedirectUrl.Text" xml:space="preserve">
|
||||||
<value>Redirect Url:</value>
|
<value>Redirect Url:</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="Scopes.HelpText" xml:space="preserve">
|
<data name="Scopes.HelpText" xml:space="preserve">
|
||||||
<value>A list of Scopes to request from the provider (separated by commas). If none are specified, standard Scopes will be used by default.</value>
|
<value>A list of scopes to request from the identity provider (separated by commas). If none are specified, standard Scopes will be used by default.</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="Scopes.Text" xml:space="preserve">
|
<data name="Scopes.Text" xml:space="preserve">
|
||||||
<value>Scopes:</value>
|
<value>Scopes:</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TokenUrl.HelpText" xml:space="preserve">
|
<data name="TokenUrl.HelpText" xml:space="preserve">
|
||||||
<value>The endpoint for obtaining an Auth Token</value>
|
<value>The endpoint for obtaining an auth token</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TokenUrl.Text" xml:space="preserve">
|
<data name="TokenUrl.Text" xml:space="preserve">
|
||||||
<value>Token Url:</value>
|
<value>Token Url:</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="UserInfoUrl.HelpText" xml:space="preserve">
|
<data name="UserInfoUrl.HelpText" xml:space="preserve">
|
||||||
<value>The endpoint for obtaining user information. This should be an API or Page Url which contains the users email address.</value>
|
<value>The endpoint for obtaining user information. This should be an API endpoint or page url which contains the users email address.</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="UserInfoUrl.Text" xml:space="preserve">
|
<data name="UserInfoUrl.Text" xml:space="preserve">
|
||||||
<value>User Info Url:</value>
|
<value>User Info Url:</value>
|
||||||
@ -373,15 +373,21 @@
|
|||||||
<value>Last Login</value>
|
<value>Last Login</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="IdentifierClaimType.HelpText" xml:space="preserve">
|
<data name="IdentifierClaimType.HelpText" xml:space="preserve">
|
||||||
<value>The name of the unique user identifier claim provided by the provider</value>
|
<value>The name of the unique user identifier claim provided by the identity provider</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="IdentifierClaimType.Text" xml:space="preserve">
|
<data name="IdentifierClaimType.Text" xml:space="preserve">
|
||||||
<value>Identifier Claim:</value>
|
<value>Identifier Claim:</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="Parameters.HelpText" xml:space="preserve">
|
<data name="Parameters.HelpText" xml:space="preserve">
|
||||||
<value>Optionally specify any additional parameters as name/value pairs to send to the provider (separated by commas if there are multiple).</value>
|
<value>Optionally specify any additional parameters as name/value pairs to send to the identity provider (separated by commas if there are multiple).</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="Parameters.Text" xml:space="preserve">
|
<data name="Parameters.Text" xml:space="preserve">
|
||||||
<value>Parameters:</value>
|
<value>Parameters:</value>
|
||||||
</data>
|
</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>
|
||||||
|
</data>
|
||||||
|
<data name="RoleClaimType.Text" xml:space="preserve">
|
||||||
|
<value>Role Claim Type:</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
@ -127,7 +127,7 @@
|
|||||||
<value>Error Loading Files</value>
|
<value>Error Loading Files</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="Error.File.Upload" xml:space="preserve">
|
<data name="Error.File.Upload" xml:space="preserve">
|
||||||
<value>File Upload Failed</value>
|
<value>File Upload Failed Or Is Still In Progress</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="Message.File.NotSelected" xml:space="preserve">
|
<data name="Message.File.NotSelected" xml:space="preserve">
|
||||||
<value>You Have Not Selected A File To Upload</value>
|
<value>You Have Not Selected A File To Upload</value>
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<root>
|
<root>
|
||||||
<!--
|
<!--
|
||||||
Microsoft ResX Schema
|
Microsoft ResX Schema
|
||||||
@ -124,9 +124,9 @@
|
|||||||
<value>User</value>
|
<value>User</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="Username.Enter" xml:space="preserve">
|
<data name="Username.Enter" xml:space="preserve">
|
||||||
<value>Enter Username</value>
|
<value>Enter User's Name</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="Message.Username.DontExist" xml:space="preserve">
|
<data name="Message.Username.DontExist" xml:space="preserve">
|
||||||
<value>Username Does Not Exist</value>
|
<value>User Does Not Exist With Name Specified</value>
|
||||||
</data>
|
</data>
|
||||||
</root>
|
</root>
|
@ -2,27 +2,17 @@ using System.Collections.Generic;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
using System.Threading;
|
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.JSInterop;
|
|
||||||
using Oqtane.Documentation;
|
using Oqtane.Documentation;
|
||||||
using Oqtane.Models;
|
using Oqtane.Models;
|
||||||
using Oqtane.Shared;
|
using Oqtane.Shared;
|
||||||
using Oqtane.UI;
|
|
||||||
|
|
||||||
namespace Oqtane.Services
|
namespace Oqtane.Services
|
||||||
{
|
{
|
||||||
[PrivateApi("Don't show in the documentation, as everything should use the Interface")]
|
[PrivateApi("Don't show in the documentation, as everything should use the Interface")]
|
||||||
public class FileService : ServiceBase, IFileService
|
public class FileService : ServiceBase, IFileService
|
||||||
{
|
{
|
||||||
private readonly SiteState _siteState;
|
public FileService(HttpClient http, SiteState siteState) : base(http, siteState) { }
|
||||||
private readonly IJSRuntime _jsRuntime;
|
|
||||||
|
|
||||||
public FileService(HttpClient http, SiteState siteState, IJSRuntime jsRuntime) : base(http, siteState)
|
|
||||||
{
|
|
||||||
_siteState = siteState;
|
|
||||||
_jsRuntime = jsRuntime;
|
|
||||||
}
|
|
||||||
|
|
||||||
private string Apiurl => CreateApiUrl("File");
|
private string Apiurl => CreateApiUrl("File");
|
||||||
|
|
||||||
@ -75,54 +65,6 @@ namespace Oqtane.Services
|
|||||||
return await GetJsonAsync<File>($"{Apiurl}/upload?url={WebUtility.UrlEncode(url)}&folderid={folderId}&name={name}");
|
return await GetJsonAsync<File>($"{Apiurl}/upload?url={WebUtility.UrlEncode(url)}&folderid={folderId}&name={name}");
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<string> UploadFilesAsync(int folderId, string[] files, string id)
|
|
||||||
{
|
|
||||||
return await UploadFilesAsync(folderId.ToString(), files, id);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<string> UploadFilesAsync(string folder, string[] files, string id)
|
|
||||||
{
|
|
||||||
string result = "";
|
|
||||||
|
|
||||||
var interop = new Interop(_jsRuntime);
|
|
||||||
await interop.UploadFiles($"{Apiurl}/upload", folder, id, _siteState.AntiForgeryToken);
|
|
||||||
|
|
||||||
// uploading files is asynchronous so we need to wait for the upload to complete
|
|
||||||
bool success = false;
|
|
||||||
int attempts = 0;
|
|
||||||
while (attempts < 5 && success == false)
|
|
||||||
{
|
|
||||||
Thread.Sleep(2000); // wait 2 seconds
|
|
||||||
result = "";
|
|
||||||
|
|
||||||
List<File> fileList = await GetFilesAsync(folder);
|
|
||||||
if (fileList.Count > 0)
|
|
||||||
{
|
|
||||||
success = true;
|
|
||||||
foreach (string file in files)
|
|
||||||
{
|
|
||||||
if (!fileList.Exists(item => item.Name == file))
|
|
||||||
{
|
|
||||||
success = false;
|
|
||||||
result += file + ",";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
attempts += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
await interop.SetElementAttribute(id + "ProgressInfo", "style", "display: none;");
|
|
||||||
await interop.SetElementAttribute(id + "ProgressBar", "style", "display: none;");
|
|
||||||
|
|
||||||
if (!success)
|
|
||||||
{
|
|
||||||
result = result.Substring(0, result.Length - 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<byte[]> DownloadFileAsync(int fileId)
|
public async Task<byte[]> DownloadFileAsync(int fileId)
|
||||||
{
|
{
|
||||||
return await GetByteArrayAsync($"{Apiurl}/download/{fileId}");
|
return await GetByteArrayAsync($"{Apiurl}/download/{fileId}");
|
||||||
|
@ -66,27 +66,6 @@ namespace Oqtane.Services
|
|||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
Task<File> UploadFileAsync(string url, int folderId, string name);
|
Task<File> UploadFileAsync(string url, int folderId, string name);
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Upload one or more files.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="folderId">Target <see cref="Folder"/></param>
|
|
||||||
/// <param name="files">The files to upload, serialized as a string.</param>
|
|
||||||
/// <param name="fileUploadName">A task-identifier, to ensure communication about this upload.</param>
|
|
||||||
/// <returns></returns>
|
|
||||||
Task<string> UploadFilesAsync(int folderId, string[] files, string fileUploadName);
|
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Upload one or more files.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="folder">Target <see cref="Folder"/>
|
|
||||||
/// TODO: todoc verify exactly from where the folder path must start
|
|
||||||
/// </param>
|
|
||||||
/// <param name="files">The files to upload, serialized as a string.</param>
|
|
||||||
/// <param name="fileUploadName">A task-identifier, to ensure communication about this upload.</param>
|
|
||||||
/// <returns></returns>
|
|
||||||
Task<string> UploadFilesAsync(string folder, string[] files, string fileUploadName);
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Get / download a file (the body).
|
/// Get / download a file (the body).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -13,6 +13,6 @@ namespace Oqtane.Services
|
|||||||
/// Returns a collection of supported cultures
|
/// Returns a collection of supported cultures
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
Task<IEnumerable<Culture>> GetCulturesAsync();
|
Task<IEnumerable<Culture>> GetCulturesAsync(bool installed);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -164,6 +164,24 @@ namespace Oqtane.Services
|
|||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
Task UpdateSettingsAsync(Dictionary<string, string> settings, string entityName, int entityId);
|
Task UpdateSettingsAsync(Dictionary<string, string> settings, string entityName, int entityId);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns a specific setting
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="entityName"></param>
|
||||||
|
/// <param name="entityId"></param>
|
||||||
|
/// <param name="settingName"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
Task DeleteSettingAsync(string entityName, int entityId, string settingName);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns a specific setting
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="entityName"></param>
|
||||||
|
/// <param name="entityId"></param>
|
||||||
|
/// <param name="settingName"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
Task<List<Setting>> GetSettingsAsync(string entityName, int entityId, string settingName);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Returns a specific setting
|
/// Returns a specific setting
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -14,6 +14,6 @@ namespace Oqtane.Services
|
|||||||
|
|
||||||
private string Apiurl => CreateApiUrl("Localization");
|
private string Apiurl => CreateApiUrl("Localization");
|
||||||
|
|
||||||
public async Task<IEnumerable<Culture>> GetCulturesAsync() => await GetJsonAsync<IEnumerable<Culture>>(Apiurl);
|
public async Task<IEnumerable<Culture>> GetCulturesAsync(bool installed) => await GetJsonAsync<IEnumerable<Culture>>($"{Apiurl}?installed={installed}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -170,6 +170,26 @@ namespace Oqtane.Services
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task DeleteSettingAsync(string entityName, int entityId, string settingName)
|
||||||
|
{
|
||||||
|
var settings = await GetJsonAsync<List<Setting>>($"{Apiurl}?entityname={entityName}&entityid={entityId}");
|
||||||
|
var setting = settings.FirstOrDefault(item => item.SettingName == settingName);
|
||||||
|
if (setting != null)
|
||||||
|
{
|
||||||
|
await DeleteAsync($"{Apiurl}/{setting.SettingId}/{entityName}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<List<Setting>> GetSettingsAsync(string entityName, int entityId, string settingName)
|
||||||
|
{
|
||||||
|
var settings = await GetJsonAsync<List<Setting>>($"{Apiurl}?entityname={entityName}&entityid={entityId}");
|
||||||
|
if (!string.IsNullOrEmpty(settingName))
|
||||||
|
{
|
||||||
|
settings = settings.Where(item => item.SettingName == settingName).ToList();
|
||||||
|
}
|
||||||
|
return settings;
|
||||||
|
}
|
||||||
|
|
||||||
public async Task<Setting> GetSettingAsync(string entityName, int settingId)
|
public async Task<Setting> GetSettingAsync(string entityName, int settingId)
|
||||||
{
|
{
|
||||||
return await GetJsonAsync<Setting>($"{Apiurl}/{settingId}/{entityName}");
|
return await GetJsonAsync<Setting>($"{Apiurl}/{settingId}/{entityName}");
|
||||||
|
@ -33,7 +33,7 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.Edit, PageState.Page.Permissions))
|
@if (_canViewAdminDashboard || UserSecurity.IsAuthorized(PageState.User, PermissionNames.Edit, PageState.Page.Permissions))
|
||||||
{
|
{
|
||||||
<button type="button" class="btn @ButtonClass" data-bs-toggle="offcanvas" data-bs-target="#offcanvasControlPanel" aria-controls="offcanvasControlPanel">
|
<button type="button" class="btn @ButtonClass" data-bs-toggle="offcanvas" data-bs-target="#offcanvasControlPanel" aria-controls="offcanvasControlPanel">
|
||||||
<span class="oi oi-cog"></span>
|
<span class="oi oi-cog"></span>
|
||||||
@ -46,16 +46,17 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="@BodyClass">
|
<div class="@BodyClass">
|
||||||
<div class="container-fluid">
|
<div class="container-fluid">
|
||||||
@if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Admin))
|
@if (_canViewAdminDashboard)
|
||||||
{
|
{
|
||||||
<div class="row d-flex">
|
<div class="row d-flex">
|
||||||
<div class="col">
|
<div class="col">
|
||||||
<button type="button" data-bs-dismiss="offcanvas" class="btn btn-primary col-12" @onclick=@(async () => Navigate("Admin"))>@Localizer["AdminDash"]</button>
|
<button type="button" data-bs-dismiss="offcanvas" class="btn btn-primary col-12" @onclick=@(async () => Navigate("Admin"))>@Localizer["AdminDash"]</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<hr class="app-rule" />
|
<hr class="app-rule" />
|
||||||
|
}
|
||||||
|
@if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Admin))
|
||||||
|
{
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col text-center">
|
<div class="col text-center">
|
||||||
<label class="control-label">@Localizer["Page.Manage"] </label>
|
<label class="control-label">@Localizer["Page.Manage"] </label>
|
||||||
@ -80,144 +81,149 @@
|
|||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
}
|
<hr class="app-rule" />
|
||||||
|
|
||||||
@if (_deleteConfirmation)
|
@if (_deleteConfirmation)
|
||||||
{
|
{
|
||||||
<div class="app-admin-modal">
|
<div class="app-admin-modal">
|
||||||
<div class="modal" tabindex="-1" role="dialog">
|
<div class="modal" tabindex="-1" role="dialog">
|
||||||
<div class="modal-dialog">
|
<div class="modal-dialog">
|
||||||
<div class="modal-content">
|
<div class="modal-content">
|
||||||
<div class="modal-header">
|
<div class="modal-header">
|
||||||
<h5 class="modal-title">@Localizer["Page.Delete"]</h5>
|
<h5 class="modal-title">@Localizer["Page.Delete"]</h5>
|
||||||
<button type="button" class="btn-close" aria-label="Close" @onclick="ConfirmDelete"></button>
|
<button type="button" class="btn-close" aria-label="Close" @onclick="ConfirmDelete"></button>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
<p>Are You Sure You Want To Delete This Page?</p>
|
<p>Are You Sure You Want To Delete This Page?</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-footer">
|
<div class="modal-footer">
|
||||||
<button type="button" class="btn btn-danger" @onclick="DeletePage">@SharedLocalizer["Delete"]</button>
|
<button type="button" class="btn btn-danger" @onclick="DeletePage">@SharedLocalizer["Delete"]</button>
|
||||||
<button type="button" class="btn btn-secondary" @onclick="ConfirmDelete">@SharedLocalizer["Cancel"]</button>
|
<button type="button" class="btn btn-secondary" @onclick="ConfirmDelete">@SharedLocalizer["Cancel"]</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
}
|
||||||
}
|
}
|
||||||
<hr class="app-rule" />
|
|
||||||
<div class="row">
|
@if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.Edit, PageState.Page.Permissions))
|
||||||
<div class="col text-center">
|
|
||||||
<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>
|
|
||||||
</select>
|
|
||||||
@if (ModuleType == "new")
|
|
||||||
{
|
|
||||||
@if (_moduleDefinitions != null)
|
|
||||||
{
|
|
||||||
<select class="form-select" @onchange="(e => CategoryChanged(e))">
|
|
||||||
@foreach (var category in _categories)
|
|
||||||
{
|
|
||||||
if (category == Category)
|
|
||||||
{
|
|
||||||
<option value="@category" selected>@category @Localizer["Modules"]</option>
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
<option value="@category">@category @Localizer["Modules"]</option>
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</select>
|
|
||||||
<select class="form-select" @onchange="(e => ModuleChanged(e))">
|
|
||||||
@if (ModuleDefinitionName == "-")
|
|
||||||
{
|
|
||||||
<option value="-" selected><@Localizer["Module.Select"]></option>
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
<option value="-"><@Localizer["Module.Select"]></option>
|
|
||||||
}
|
|
||||||
@foreach (var moduledefinition in _moduleDefinitions)
|
|
||||||
{
|
|
||||||
if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.Utilize, moduledefinition.Permissions))
|
|
||||||
{
|
|
||||||
if (moduledefinition.Runtimes == "" || moduledefinition.Runtimes.Contains(PageState.Runtime.ToString()))
|
|
||||||
{
|
|
||||||
<option value="@moduledefinition.ModuleDefinitionName">@moduledefinition.Name</option>
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</select>
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
<select class="form-select" @onchange="(e => PageChanged(e))">
|
|
||||||
<option value="-"><@Localizer["Page.Select"]></option>
|
|
||||||
@foreach (Page p in _pages)
|
|
||||||
{
|
|
||||||
<option value="@p.PageId">@p.Name</option>
|
|
||||||
}
|
|
||||||
</select>
|
|
||||||
<select class="form-select" @bind="@ModuleId">
|
|
||||||
<option value="-"><@Localizer["Module.Select"]></option>
|
|
||||||
@foreach (Module module in _modules)
|
|
||||||
{
|
|
||||||
<option value="@module.ModuleId">@module.Title</option>
|
|
||||||
}
|
|
||||||
</select>
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="row">
|
|
||||||
<div class="col text-center">
|
|
||||||
<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="row">
|
||||||
<div class="col text-center">
|
<div class="col text-center">
|
||||||
<label for="Pane" class="control-label">@Localizer["Pane"] </label>
|
<label for="Module" class="control-label">@Localizer["Module.Manage"] </label>
|
||||||
<select class="form-select" @bind="@Pane">
|
<select class="form-select" @bind="@ModuleType">
|
||||||
@foreach (string pane in PageState.Page.Panes)
|
<option value="new">@Localizer["Module.AddNew"]</option>
|
||||||
|
<option value="existing">@Localizer["Module.AddExisting"]</option>
|
||||||
|
</select>
|
||||||
|
@if (ModuleType == "new")
|
||||||
|
{
|
||||||
|
@if (_moduleDefinitions != null)
|
||||||
{
|
{
|
||||||
<option value="@pane">@pane Pane</option>
|
<select class="form-select" @onchange="(e => CategoryChanged(e))">
|
||||||
|
@foreach (var category in _categories)
|
||||||
|
{
|
||||||
|
if (category == Category)
|
||||||
|
{
|
||||||
|
<option value="@category" selected>@category @Localizer["Modules"]</option>
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<option value="@category">@category @Localizer["Modules"]</option>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</select>
|
||||||
|
<select class="form-select" @onchange="(e => ModuleChanged(e))">
|
||||||
|
@if (ModuleDefinitionName == "-")
|
||||||
|
{
|
||||||
|
<option value="-" selected><@Localizer["Module.Select"]></option>
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<option value="-"><@Localizer["Module.Select"]></option>
|
||||||
|
}
|
||||||
|
@foreach (var moduledefinition in _moduleDefinitions)
|
||||||
|
{
|
||||||
|
if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.Utilize, moduledefinition.Permissions))
|
||||||
|
{
|
||||||
|
if (moduledefinition.Runtimes == "" || moduledefinition.Runtimes.Contains(PageState.Runtime.ToString()))
|
||||||
|
{
|
||||||
|
<option value="@moduledefinition.ModuleDefinitionName">@moduledefinition.Name</option>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</select>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<select class="form-select" @onchange="(e => PageChanged(e))">
|
||||||
|
<option value="-"><@Localizer["Page.Select"]></option>
|
||||||
|
@foreach (Page p in _pages)
|
||||||
|
{
|
||||||
|
<option value="@p.PageId">@p.Name</option>
|
||||||
|
}
|
||||||
|
</select>
|
||||||
|
<select class="form-select" @bind="@ModuleId">
|
||||||
|
<option value="-"><@Localizer["Module.Select"]></option>
|
||||||
|
@foreach (Module module in _modules)
|
||||||
|
{
|
||||||
|
<option value="@module.ModuleId">@module.Title</option>
|
||||||
|
}
|
||||||
|
</select>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col text-center">
|
||||||
|
<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>
|
||||||
|
<select class="form-select" @bind="@ContainerType">
|
||||||
|
@foreach (var container in _containers)
|
||||||
|
{
|
||||||
|
<option value="@container.TypeName">@container.Name</option>
|
||||||
}
|
}
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col text-center">
|
||||||
|
<label for="visibility" class="control-label">@Localizer["Visibility"]</label>
|
||||||
|
<select class="form-select" @bind="@Visibility">
|
||||||
|
<option value="view">@Localizer["VisibilityView"]</option>
|
||||||
|
<option value="edit">@Localizer["VisibilityEdit"]</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<button type="button" class="btn btn-primary col-12 mt-4" @onclick="@AddModule">@Localizer["Page.Module.Add"]</button>
|
||||||
|
@((MarkupString)Message)
|
||||||
}
|
}
|
||||||
<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)
|
|
||||||
{
|
|
||||||
<option value="@container.TypeName">@container.Name</option>
|
|
||||||
}
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="row">
|
|
||||||
<div class="col text-center">
|
|
||||||
<label for="visibility" class="control-label">@Localizer["Visibility"]</label>
|
|
||||||
<select class="form-select" @bind="@Visibility">
|
|
||||||
<option value="edit" selected>@Localizer["VisibilityEdit"]</option>
|
|
||||||
<option value="view">@Localizer["VisibilityView"]</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<button type="button" class="btn btn-primary col-12 mt-4" @onclick="@AddModule">@Localizer["Page.Module.Add"]</button>
|
|
||||||
@((MarkupString) Message)
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|
||||||
@code{
|
@code{
|
||||||
|
private bool _canViewAdminDashboard = false;
|
||||||
private bool _showEditMode = false;
|
private bool _showEditMode = false;
|
||||||
private bool _deleteConfirmation = false;
|
private bool _deleteConfirmation = false;
|
||||||
private List<string> _categories = new List<string>();
|
private List<string> _categories = new List<string>();
|
||||||
@ -265,7 +271,7 @@
|
|||||||
|
|
||||||
protected string Title { get; private set; } = "";
|
protected string Title { get; private set; } = "";
|
||||||
protected string ContainerType { get; private set; } = "";
|
protected string ContainerType { get; private set; } = "";
|
||||||
protected string Visibility { get; private set; } = "edit";
|
protected string Visibility { get; private set; } = "view";
|
||||||
protected string Message { get; private set; } = "";
|
protected string Message { get; private set; } = "";
|
||||||
|
|
||||||
[Parameter]
|
[Parameter]
|
||||||
@ -286,6 +292,7 @@
|
|||||||
|
|
||||||
protected override async Task OnParametersSetAsync()
|
protected override async Task OnParametersSetAsync()
|
||||||
{
|
{
|
||||||
|
_canViewAdminDashboard = CanViewAdminDashboard();
|
||||||
_showEditMode = false;
|
_showEditMode = false;
|
||||||
if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.Edit, PageState.Page.Permissions))
|
if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.Edit, PageState.Page.Permissions))
|
||||||
{
|
{
|
||||||
@ -321,6 +328,22 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private bool CanViewAdminDashboard()
|
||||||
|
{
|
||||||
|
var admin = PageState.Pages.FirstOrDefault(item => item.Path == "admin");
|
||||||
|
if (admin != null)
|
||||||
|
{
|
||||||
|
foreach (var page in PageState.Pages.Where(item => item.ParentId == admin?.PageId))
|
||||||
|
{
|
||||||
|
if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.View, page.Permissions))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
private void CategoryChanged(ChangeEventArgs e)
|
private void CategoryChanged(ChangeEventArgs e)
|
||||||
{
|
{
|
||||||
Category = (string)e.Value;
|
Category = (string)e.Value;
|
||||||
@ -380,6 +403,8 @@
|
|||||||
// set module view permissions to page edit permissions
|
// set module view permissions to page edit permissions
|
||||||
permissions.Find(p => p.PermissionName == PermissionNames.View).Permissions = permissions.Find(p => p.PermissionName == PermissionNames.Edit).Permissions;
|
permissions.Find(p => p.PermissionName == PermissionNames.View).Permissions = permissions.Find(p => p.PermissionName == PermissionNames.Edit).Permissions;
|
||||||
}
|
}
|
||||||
|
// set entityname
|
||||||
|
permissions.ForEach(item => item.EntityName = EntityNames.Module);
|
||||||
module.Permissions = UserSecurity.SetPermissionStrings(permissions);
|
module.Permissions = UserSecurity.SetPermissionStrings(permissions);
|
||||||
|
|
||||||
module = await ModuleService.AddModuleAsync(module);
|
module = await ModuleService.AddModuleAsync(module);
|
||||||
@ -465,12 +490,10 @@
|
|||||||
case "Admin":
|
case "Admin":
|
||||||
// get admin dashboard moduleid
|
// get admin dashboard moduleid
|
||||||
module = PageState.Modules.FirstOrDefault(item => item.ModuleDefinitionName == Constants.AdminDashboardModule);
|
module = PageState.Modules.FirstOrDefault(item => item.ModuleDefinitionName == Constants.AdminDashboardModule);
|
||||||
|
|
||||||
if (module != null)
|
if (module != null)
|
||||||
{
|
{
|
||||||
NavigationManager.NavigateTo(EditUrl(PageState.Page.Path, module.ModuleId, "Index", ""));
|
NavigationManager.NavigateTo(EditUrl(PageState.Page.Path, module.ModuleId, "Index", ""));
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
case "Add":
|
case "Add":
|
||||||
case "Edit":
|
case "Edit":
|
||||||
@ -551,19 +574,19 @@
|
|||||||
{
|
{
|
||||||
page.IsDeleted = true;
|
page.IsDeleted = true;
|
||||||
await PageService.UpdatePageAsync(page);
|
await PageService.UpdatePageAsync(page);
|
||||||
await logger.Log(page.PageId, null, PageState.User.UserId, GetType().AssemblyQualifiedName, "ControlPanel", LogFunction.Delete, LogLevel.Information, null, "Page Deleted {Page}", page);
|
await logger.Log(page.PageId, null, PageState.User?.UserId, GetType().AssemblyQualifiedName, "ControlPanel", LogFunction.Delete, LogLevel.Information, null, "Page Deleted {Page}", page);
|
||||||
NavigationManager.NavigateTo(NavigateUrl(""));
|
NavigationManager.NavigateTo(NavigateUrl(""));
|
||||||
}
|
}
|
||||||
else // personalized page
|
else // personalized page
|
||||||
{
|
{
|
||||||
await PageService.DeletePageAsync(page.PageId);
|
await PageService.DeletePageAsync(page.PageId);
|
||||||
await logger.Log(page.PageId, null, PageState.User.UserId, GetType().AssemblyQualifiedName, "ControlPanel", LogFunction.Delete, LogLevel.Information, null, "Page Deleted {Page}", page);
|
await logger.Log(page.PageId, null, PageState.User?.UserId, GetType().AssemblyQualifiedName, "ControlPanel", LogFunction.Delete, LogLevel.Information, null, "Page Deleted {Page}", page);
|
||||||
NavigationManager.NavigateTo(NavigateUrl());
|
NavigationManager.NavigateTo(NavigateUrl());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
await logger.Log(page.PageId, null, PageState.User.UserId, GetType().AssemblyQualifiedName, "ControlPanel", LogFunction.Delete, LogLevel.Information, ex, "Page Deleted {Page} {Error}", page, ex.Message);
|
await logger.Log(page.PageId, null, PageState.User?.UserId, GetType().AssemblyQualifiedName, "ControlPanel", LogFunction.Delete, LogLevel.Information, ex, "Page Deleted {Page} {Error}", page, ex.Message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -596,8 +619,8 @@
|
|||||||
private async Task UpdateSettingsAsync()
|
private async Task UpdateSettingsAsync()
|
||||||
{
|
{
|
||||||
Dictionary<string, string> settings = await SettingService.GetUserSettingsAsync(PageState.User.UserId);
|
Dictionary<string, string> settings = await SettingService.GetUserSettingsAsync(PageState.User.UserId);
|
||||||
SettingService.SetSetting(settings, settingCategory, _category);
|
settings = SettingService.SetSetting(settings, settingCategory, _category);
|
||||||
SettingService.SetSetting(settings, settingPane, _pane);
|
settings = SettingService.SetSetting(settings, settingPane, _pane);
|
||||||
await SettingService.UpdateUserSettingsAsync(settings, PageState.User.UserId);
|
await SettingService.UpdateUserSettingsAsync(settings, PageState.User.UserId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -32,7 +32,7 @@ namespace Oqtane.Themes.Controls
|
|||||||
|
|
||||||
protected async Task LogoutUser()
|
protected async Task LogoutUser()
|
||||||
{
|
{
|
||||||
await LoggingService.Log(PageState.Alias, PageState.Page.PageId, null, PageState.User.UserId, GetType().AssemblyQualifiedName, "Logout", LogFunction.Security, LogLevel.Information, null, "User Logout For Username {Username}", PageState.User.Username);
|
await LoggingService.Log(PageState.Alias, PageState.Page.PageId, null, PageState.User?.UserId, GetType().AssemblyQualifiedName, "Logout", LogFunction.Security, LogLevel.Information, null, "User Logout For Username {Username}", PageState.User?.Username);
|
||||||
|
|
||||||
// check if anonymous user can access page
|
// check if anonymous user can access page
|
||||||
var url = PageState.Alias.Path + "/" + PageState.Page.Path;
|
var url = PageState.Alias.Path + "/" + PageState.Page.Path;
|
||||||
|
@ -3,6 +3,7 @@ using Microsoft.JSInterop;
|
|||||||
using Oqtane.Models;
|
using Oqtane.Models;
|
||||||
using Oqtane.Shared;
|
using Oqtane.Shared;
|
||||||
using Oqtane.UI;
|
using Oqtane.UI;
|
||||||
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
@ -31,14 +32,24 @@ namespace Oqtane.Themes
|
|||||||
{
|
{
|
||||||
if (Resources != null && Resources.Exists(item => item.ResourceType == ResourceType.Script))
|
if (Resources != null && Resources.Exists(item => item.ResourceType == ResourceType.Script))
|
||||||
{
|
{
|
||||||
|
var interop = new Interop(JSRuntime);
|
||||||
var scripts = new List<object>();
|
var scripts = new List<object>();
|
||||||
|
var inline = 0;
|
||||||
foreach (Resource resource in Resources.Where(item => item.ResourceType == ResourceType.Script))
|
foreach (Resource resource in Resources.Where(item => item.ResourceType == ResourceType.Script))
|
||||||
{
|
{
|
||||||
scripts.Add(new { href = resource.Url, bundle = resource.Bundle ?? "", integrity = resource.Integrity ?? "", crossorigin = resource.CrossOrigin ?? "", es6module = resource.ES6Module });
|
if (!string.IsNullOrEmpty(resource.Url))
|
||||||
|
{
|
||||||
|
var url = (resource.Url.Contains("://")) ? resource.Url : PageState.Alias.BaseUrl + resource.Url;
|
||||||
|
scripts.Add(new { href = url, bundle = resource.Bundle ?? "", integrity = resource.Integrity ?? "", crossorigin = resource.CrossOrigin ?? "", es6module = resource.ES6Module });
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
inline += 1;
|
||||||
|
await interop.IncludeScript(GetType().Namespace.ToLower() + inline.ToString(), "", "", "", resource.Content, resource.Location.ToString().ToLower());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (scripts.Any())
|
if (scripts.Any())
|
||||||
{
|
{
|
||||||
var interop = new Interop(JSRuntime);
|
|
||||||
await interop.IncludeScripts(scripts.ToArray());
|
await interop.IncludeScripts(scripts.ToArray());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -49,7 +60,7 @@ namespace Oqtane.Themes
|
|||||||
|
|
||||||
public string ThemePath()
|
public string ThemePath()
|
||||||
{
|
{
|
||||||
return "Themes/" + GetType().Namespace + "/";
|
return PageState?.Alias.BaseUrl + "/Themes/" + GetType().Namespace + "/";
|
||||||
}
|
}
|
||||||
|
|
||||||
// url methods
|
// url methods
|
||||||
@ -94,14 +105,23 @@ namespace Oqtane.Themes
|
|||||||
return Utilities.EditUrl(PageState.Alias.Path, path, moduleid, action, parameters);
|
return Utilities.EditUrl(PageState.Alias.Path, path, moduleid, action, parameters);
|
||||||
}
|
}
|
||||||
|
|
||||||
public string ContentUrl(int fileid)
|
public string FileUrl(string folderpath, string filename)
|
||||||
{
|
{
|
||||||
return Utilities.ContentUrl(PageState.Alias, fileid);
|
return FileUrl(folderpath, filename, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public string ContentUrl(int fileid, bool asAttachment)
|
public string FileUrl(string folderpath, string filename, bool download)
|
||||||
{
|
{
|
||||||
return Utilities.ContentUrl(PageState.Alias, fileid, asAttachment);
|
return Utilities.FileUrl(PageState.Alias, folderpath, filename, download);
|
||||||
|
}
|
||||||
|
public string FileUrl(int fileid)
|
||||||
|
{
|
||||||
|
return FileUrl(fileid, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public string FileUrl(int fileid, bool download)
|
||||||
|
{
|
||||||
|
return Utilities.FileUrl(PageState.Alias, fileid, download);
|
||||||
}
|
}
|
||||||
|
|
||||||
public string ImageUrl(int fileid, int width, int height)
|
public string ImageUrl(int fileid, int width, int height)
|
||||||
@ -118,5 +138,17 @@ namespace Oqtane.Themes
|
|||||||
{
|
{
|
||||||
return Utilities.ImageUrl(PageState.Alias, fileid, width, height, mode, position, background, rotate, recreate);
|
return Utilities.ImageUrl(PageState.Alias, fileid, width, height, mode, position, background, rotate, recreate);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Obsolete("ContentUrl(int fileId) is deprecated. Use FileUrl(int fileId) instead.", false)]
|
||||||
|
public string ContentUrl(int fileid)
|
||||||
|
{
|
||||||
|
return ContentUrl(fileid, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Obsolete("ContentUrl(int fileId, bool asAttachment) is deprecated. Use FileUrl(int fileId, bool download) instead.", false)]
|
||||||
|
public string ContentUrl(int fileid, bool asAttachment)
|
||||||
|
{
|
||||||
|
return Utilities.FileUrl(PageState.Alias, fileid, asAttachment);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -60,13 +60,13 @@ namespace Oqtane.UI
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task IncludeMeta(string id, string attribute, string name, string content, string key)
|
public Task IncludeMeta(string id, string attribute, string name, string content)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
_jsRuntime.InvokeVoidAsync(
|
_jsRuntime.InvokeVoidAsync(
|
||||||
"Oqtane.Interop.includeMeta",
|
"Oqtane.Interop.includeMeta",
|
||||||
id, attribute, name, content, key);
|
id, attribute, name, content);
|
||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
@ -105,6 +105,7 @@ namespace Oqtane.UI
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// external scripts need to specify src, inline scripts need to specify id and content
|
||||||
public Task IncludeScript(string id, string src, string integrity, string crossorigin, string content, string location)
|
public Task IncludeScript(string id, string src, string integrity, string crossorigin, string content, string location)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
|
@ -87,10 +87,9 @@ else
|
|||||||
// retrieve friendly localized error
|
// retrieve friendly localized error
|
||||||
_error = Localizer["Error.Module.Exception"];
|
_error = Localizer["Error.Module.Exception"];
|
||||||
// log error
|
// log error
|
||||||
int? userId = (PageState.User != null) ? PageState.User.UserId : null;
|
|
||||||
string category = GetType().AssemblyQualifiedName;
|
string category = GetType().AssemblyQualifiedName;
|
||||||
string feature = Utilities.GetTypeNameLastSegment(category, 1);
|
string feature = Utilities.GetTypeNameLastSegment(category, 1);
|
||||||
await LoggingService.Log(null, ModuleState.PageId, ModuleState.ModuleId, userId, category, feature, LogFunction.Other, LogLevel.Error, exception, "An Unexpected Error Has Occurred In {ModuleDefinitionName}: {Error}", ModuleState.ModuleDefinitionName, exception.Message);
|
await LoggingService.Log(null, ModuleState.PageId, ModuleState.ModuleId, PageState.User?.UserId, category, feature, LogFunction.Other, LogLevel.Error, exception, "An Unexpected Error Has Occurred In {ModuleDefinitionName}: {Error}", ModuleState.ModuleDefinitionName, exception.Message);
|
||||||
await base.OnErrorAsync(exception);
|
await base.OnErrorAsync(exception);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -1,9 +0,0 @@
|
|||||||
namespace Oqtane.UI
|
|
||||||
{
|
|
||||||
public enum Refresh
|
|
||||||
{
|
|
||||||
None,
|
|
||||||
Site,
|
|
||||||
Application
|
|
||||||
}
|
|
||||||
}
|
|
@ -79,7 +79,7 @@
|
|||||||
Page page;
|
Page page;
|
||||||
User user = null;
|
User user = null;
|
||||||
var editmode = false;
|
var editmode = false;
|
||||||
var refresh = UI.Refresh.None;
|
var refresh = false;
|
||||||
var lastsyncdate = DateTime.UtcNow.AddHours(-1);
|
var lastsyncdate = DateTime.UtcNow.AddHours(-1);
|
||||||
var runtime = (Shared.Runtime)Enum.Parse(typeof(Shared.Runtime), Runtime);
|
var runtime = (Shared.Runtime)Enum.Parse(typeof(Shared.Runtime), Runtime);
|
||||||
_error = "";
|
_error = "";
|
||||||
@ -97,7 +97,7 @@
|
|||||||
// reload the client application from the server if there is a forced reload or the user navigated to a site with a different alias
|
// reload the client application from the server if there is a forced reload or the user navigated to a site with a different alias
|
||||||
if (querystring.ContainsKey("reload") || (!NavigationManager.ToBaseRelativePath(_absoluteUri).ToLower().StartsWith(SiteState.Alias.Path.ToLower()) && !string.IsNullOrEmpty(SiteState.Alias.Path)))
|
if (querystring.ContainsKey("reload") || (!NavigationManager.ToBaseRelativePath(_absoluteUri).ToLower().StartsWith(SiteState.Alias.Path.ToLower()) && !string.IsNullOrEmpty(SiteState.Alias.Path)))
|
||||||
{
|
{
|
||||||
if (querystring["reload"] == "post")
|
if (querystring.ContainsKey("reload") && querystring["reload"] == "post")
|
||||||
{
|
{
|
||||||
// post back so that the cookies are set correctly - required on any change to the principal
|
// post back so that the cookies are set correctly - required on any change to the principal
|
||||||
var interop = new Interop(JSRuntime);
|
var interop = new Interop(JSRuntime);
|
||||||
@ -116,7 +116,7 @@
|
|||||||
// the refresh parameter is used to refresh the client-side PageState
|
// the refresh parameter is used to refresh the client-side PageState
|
||||||
if (querystring.ContainsKey("refresh"))
|
if (querystring.ContainsKey("refresh"))
|
||||||
{
|
{
|
||||||
refresh = UI.Refresh.Site;
|
refresh = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (PageState != null)
|
if (PageState != null)
|
||||||
@ -126,7 +126,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
// get user
|
// get user
|
||||||
if (PageState == null || refresh == UI.Refresh.Site || PageState.Alias.SiteId != SiteState.Alias.SiteId)
|
if (PageState == null || refresh || PageState.Alias.SiteId != SiteState.Alias.SiteId)
|
||||||
{
|
{
|
||||||
var authState = await AuthenticationStateProvider.GetAuthenticationStateAsync();
|
var authState = await AuthenticationStateProvider.GetAuthenticationStateAsync();
|
||||||
if (authState.User.Identity.IsAuthenticated)
|
if (authState.User.Identity.IsAuthenticated)
|
||||||
@ -149,27 +149,27 @@
|
|||||||
if (sync.SyncEvents.Any())
|
if (sync.SyncEvents.Any())
|
||||||
{
|
{
|
||||||
// reload client application if server was restarted or site runtime/rendermode was modified
|
// reload client application if server was restarted or site runtime/rendermode was modified
|
||||||
if (PageState != null && sync.SyncEvents.Exists(item => (item.TenantId == -1 || item.EntityName == EntityNames.Site && item.EntityId == SiteState.Alias.SiteId) && item.Reload))
|
if (PageState != null && sync.SyncEvents.Exists(item => (item.Action == SyncEventActions.Reload)))
|
||||||
{
|
{
|
||||||
NavigationManager.NavigateTo(_absoluteUri, true);
|
NavigationManager.NavigateTo(_absoluteUri, true);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// when a site has changed the state needs to be refreshed
|
// when site information has changed the PageState needs to be refreshed
|
||||||
if (sync.SyncEvents.Exists(item => item.EntityName == EntityNames.Site && item.EntityId == SiteState.Alias.SiteId))
|
if (sync.SyncEvents.Exists(item => item.EntityName == EntityNames.Site && item.EntityId == SiteState.Alias.SiteId))
|
||||||
{
|
{
|
||||||
refresh = UI.Refresh.Site;
|
refresh = true;
|
||||||
}
|
}
|
||||||
// when a user changed the site needs to be refreshed as the list of pages/modules may have changed
|
// when user information has changed the PageState needs to be refreshed as the list of pages/modules may have changed
|
||||||
if (user != null && sync.SyncEvents.Exists(item => item.EntityName == EntityNames.User && item.EntityId == user.UserId))
|
if (user != null && sync.SyncEvents.Exists(item => item.EntityName == EntityNames.User && item.EntityId == user.UserId))
|
||||||
{
|
{
|
||||||
refresh = UI.Refresh.Site;
|
refresh = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (PageState == null || refresh == UI.Refresh.Site || PageState.Alias.SiteId != SiteState.Alias.SiteId)
|
if (PageState == null || refresh || PageState.Alias.SiteId != SiteState.Alias.SiteId)
|
||||||
{
|
{
|
||||||
site = await SiteService.GetSiteAsync(SiteState.Alias.SiteId);
|
site = await SiteService.GetSiteAsync(SiteState.Alias.SiteId);
|
||||||
refresh = UI.Refresh.Site;
|
refresh = true;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -178,7 +178,7 @@
|
|||||||
|
|
||||||
if (site != null)
|
if (site != null)
|
||||||
{
|
{
|
||||||
if (PageState == null || refresh == UI.Refresh.Site || PageState.Page.Path != route.PagePath)
|
if (PageState == null || refresh || PageState.Page.Path != route.PagePath)
|
||||||
{
|
{
|
||||||
page = site.Pages.FirstOrDefault(item => item.Path.Equals(route.PagePath, StringComparison.OrdinalIgnoreCase));
|
page = site.Pages.FirstOrDefault(item => item.Path.Equals(route.PagePath, StringComparison.OrdinalIgnoreCase));
|
||||||
editmode = false;
|
editmode = false;
|
||||||
|
@ -36,7 +36,7 @@
|
|||||||
foreach (Resource resource in PageState.Page.Resources.Where(item => item.ResourceType == ResourceType.Stylesheet))
|
foreach (Resource resource in PageState.Page.Resources.Where(item => item.ResourceType == ResourceType.Stylesheet))
|
||||||
{
|
{
|
||||||
var prefix = "app-stylesheet-" + resource.Level.ToString().ToLower();
|
var prefix = "app-stylesheet-" + resource.Level.ToString().ToLower();
|
||||||
var url = (resource.Url.Contains("://")) ? resource.Url : PageState.Alias.BaseUrl + "/" + resource.Url;
|
var url = (resource.Url.Contains("://")) ? resource.Url : PageState.Alias.BaseUrl + resource.Url;
|
||||||
links.Add(new { id = prefix + "-" + batch + "-" + (links.Count + 1).ToString("00"), rel = "stylesheet", href = url, type = "text/css", integrity = resource.Integrity ?? "", crossorigin = resource.CrossOrigin ?? "", insertbefore = prefix });
|
links.Add(new { id = prefix + "-" + batch + "-" + (links.Count + 1).ToString("00"), rel = "stylesheet", href = url, type = "text/css", integrity = resource.Integrity ?? "", crossorigin = resource.CrossOrigin ?? "", insertbefore = prefix });
|
||||||
}
|
}
|
||||||
if (links.Any())
|
if (links.Any())
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net6.0</TargetFramework>
|
<TargetFramework>net6.0</TargetFramework>
|
||||||
<Version>3.2.0</Version>
|
<Version>3.3.1</Version>
|
||||||
<Product>Oqtane</Product>
|
<Product>Oqtane</Product>
|
||||||
<Authors>Shaun Walker</Authors>
|
<Authors>Shaun Walker</Authors>
|
||||||
<Company>.NET Foundation</Company>
|
<Company>.NET Foundation</Company>
|
||||||
@ -10,7 +10,7 @@
|
|||||||
<Copyright>.NET Foundation</Copyright>
|
<Copyright>.NET Foundation</Copyright>
|
||||||
<PackageProjectUrl>https://www.oqtane.org</PackageProjectUrl>
|
<PackageProjectUrl>https://www.oqtane.org</PackageProjectUrl>
|
||||||
<PackageLicenseUrl>https://github.com/oqtane/oqtane.framework/blob/dev/LICENSE</PackageLicenseUrl>
|
<PackageLicenseUrl>https://github.com/oqtane/oqtane.framework/blob/dev/LICENSE</PackageLicenseUrl>
|
||||||
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.2.0</PackageReleaseNotes>
|
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.3.1</PackageReleaseNotes>
|
||||||
<RepositoryUrl>https://github.com/oqtane/oqtane.framework</RepositoryUrl>
|
<RepositoryUrl>https://github.com/oqtane/oqtane.framework</RepositoryUrl>
|
||||||
<RepositoryType>Git</RepositoryType>
|
<RepositoryType>Git</RepositoryType>
|
||||||
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
|
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
|
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
|
||||||
<metadata>
|
<metadata>
|
||||||
<id>Oqtane.Database.MySQL</id>
|
<id>Oqtane.Database.MySQL</id>
|
||||||
<version>3.2.0</version>
|
<version>3.3.1</version>
|
||||||
<authors>Shaun Walker</authors>
|
<authors>Shaun Walker</authors>
|
||||||
<owners>.NET Foundation</owners>
|
<owners>.NET Foundation</owners>
|
||||||
<title>Oqtane MySQL Provider</title>
|
<title>Oqtane MySQL Provider</title>
|
||||||
@ -12,7 +12,7 @@
|
|||||||
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
||||||
<license type="expression">MIT</license>
|
<license type="expression">MIT</license>
|
||||||
<projectUrl>https://github.com/oqtane/oqtane.framework</projectUrl>
|
<projectUrl>https://github.com/oqtane/oqtane.framework</projectUrl>
|
||||||
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.2.0</releaseNotes>
|
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.3.1</releaseNotes>
|
||||||
<icon>icon.png</icon>
|
<icon>icon.png</icon>
|
||||||
<tags>oqtane</tags>
|
<tags>oqtane</tags>
|
||||||
</metadata>
|
</metadata>
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net6.0</TargetFramework>
|
<TargetFramework>net6.0</TargetFramework>
|
||||||
<Version>3.2.0</Version>
|
<Version>3.3.1</Version>
|
||||||
<Product>Oqtane</Product>
|
<Product>Oqtane</Product>
|
||||||
<Authors>Shaun Walker</Authors>
|
<Authors>Shaun Walker</Authors>
|
||||||
<Company>.NET Foundation</Company>
|
<Company>.NET Foundation</Company>
|
||||||
@ -10,7 +10,7 @@
|
|||||||
<Copyright>.NET Foundation</Copyright>
|
<Copyright>.NET Foundation</Copyright>
|
||||||
<PackageProjectUrl>https://www.oqtane.org</PackageProjectUrl>
|
<PackageProjectUrl>https://www.oqtane.org</PackageProjectUrl>
|
||||||
<PackageLicenseUrl>https://github.com/oqtane/oqtane.framework/blob/dev/LICENSE</PackageLicenseUrl>
|
<PackageLicenseUrl>https://github.com/oqtane/oqtane.framework/blob/dev/LICENSE</PackageLicenseUrl>
|
||||||
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.2.0</PackageReleaseNotes>
|
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.3.1</PackageReleaseNotes>
|
||||||
<RepositoryUrl>https://github.com/oqtane/oqtane.framework</RepositoryUrl>
|
<RepositoryUrl>https://github.com/oqtane/oqtane.framework</RepositoryUrl>
|
||||||
<RepositoryType>Git</RepositoryType>
|
<RepositoryType>Git</RepositoryType>
|
||||||
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
|
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
|
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
|
||||||
<metadata>
|
<metadata>
|
||||||
<id>Oqtane.Database.PostgreSQL</id>
|
<id>Oqtane.Database.PostgreSQL</id>
|
||||||
<version>3.2.0</version>
|
<version>3.3.1</version>
|
||||||
<authors>Shaun Walker</authors>
|
<authors>Shaun Walker</authors>
|
||||||
<owners>.NET Foundation</owners>
|
<owners>.NET Foundation</owners>
|
||||||
<title>Oqtane PostgreSQL Provider</title>
|
<title>Oqtane PostgreSQL Provider</title>
|
||||||
@ -12,7 +12,7 @@
|
|||||||
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
||||||
<license type="expression">MIT</license>
|
<license type="expression">MIT</license>
|
||||||
<projectUrl>https://github.com/oqtane/oqtane.framework</projectUrl>
|
<projectUrl>https://github.com/oqtane/oqtane.framework</projectUrl>
|
||||||
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.2.0</releaseNotes>
|
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.3.1</releaseNotes>
|
||||||
<icon>icon.png</icon>
|
<icon>icon.png</icon>
|
||||||
<tags>oqtane</tags>
|
<tags>oqtane</tags>
|
||||||
</metadata>
|
</metadata>
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net6.0</TargetFramework>
|
<TargetFramework>net6.0</TargetFramework>
|
||||||
<Version>3.2.0</Version>
|
<Version>3.3.1</Version>
|
||||||
<Product>Oqtane</Product>
|
<Product>Oqtane</Product>
|
||||||
<Authors>Shaun Walker</Authors>
|
<Authors>Shaun Walker</Authors>
|
||||||
<Company>.NET Foundation</Company>
|
<Company>.NET Foundation</Company>
|
||||||
@ -10,7 +10,7 @@
|
|||||||
<Copyright>.NET Foundation</Copyright>
|
<Copyright>.NET Foundation</Copyright>
|
||||||
<PackageProjectUrl>https://www.oqtane.org</PackageProjectUrl>
|
<PackageProjectUrl>https://www.oqtane.org</PackageProjectUrl>
|
||||||
<PackageLicenseUrl>https://github.com/oqtane/oqtane.framework/blob/dev/LICENSE</PackageLicenseUrl>
|
<PackageLicenseUrl>https://github.com/oqtane/oqtane.framework/blob/dev/LICENSE</PackageLicenseUrl>
|
||||||
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.2.0</PackageReleaseNotes>
|
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.3.1</PackageReleaseNotes>
|
||||||
<RepositoryUrl>https://github.com/oqtane/oqtane.framework</RepositoryUrl>
|
<RepositoryUrl>https://github.com/oqtane/oqtane.framework</RepositoryUrl>
|
||||||
<RepositoryType>Git</RepositoryType>
|
<RepositoryType>Git</RepositoryType>
|
||||||
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
|
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
|
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
|
||||||
<metadata>
|
<metadata>
|
||||||
<id>Oqtane.Database.SqlServer</id>
|
<id>Oqtane.Database.SqlServer</id>
|
||||||
<version>3.2.0</version>
|
<version>3.3.1</version>
|
||||||
<authors>Shaun Walker</authors>
|
<authors>Shaun Walker</authors>
|
||||||
<owners>.NET Foundation</owners>
|
<owners>.NET Foundation</owners>
|
||||||
<title>Oqtane SQL Server Provider</title>
|
<title>Oqtane SQL Server Provider</title>
|
||||||
@ -12,7 +12,7 @@
|
|||||||
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
||||||
<license type="expression">MIT</license>
|
<license type="expression">MIT</license>
|
||||||
<projectUrl>https://github.com/oqtane/oqtane.framework</projectUrl>
|
<projectUrl>https://github.com/oqtane/oqtane.framework</projectUrl>
|
||||||
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.2.0</releaseNotes>
|
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.3.1</releaseNotes>
|
||||||
<icon>icon.png</icon>
|
<icon>icon.png</icon>
|
||||||
<tags>oqtane</tags>
|
<tags>oqtane</tags>
|
||||||
</metadata>
|
</metadata>
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net6.0</TargetFramework>
|
<TargetFramework>net6.0</TargetFramework>
|
||||||
<Version>3.2.0</Version>
|
<Version>3.3.1</Version>
|
||||||
<Product>Oqtane</Product>
|
<Product>Oqtane</Product>
|
||||||
<Authors>Shaun Walker</Authors>
|
<Authors>Shaun Walker</Authors>
|
||||||
<Company>.NET Foundation</Company>
|
<Company>.NET Foundation</Company>
|
||||||
@ -10,7 +10,7 @@
|
|||||||
<Copyright>.NET Foundation</Copyright>
|
<Copyright>.NET Foundation</Copyright>
|
||||||
<PackageProjectUrl>https://www.oqtane.org</PackageProjectUrl>
|
<PackageProjectUrl>https://www.oqtane.org</PackageProjectUrl>
|
||||||
<PackageLicenseUrl>https://github.com/oqtane/oqtane.framework/blob/dev/LICENSE</PackageLicenseUrl>
|
<PackageLicenseUrl>https://github.com/oqtane/oqtane.framework/blob/dev/LICENSE</PackageLicenseUrl>
|
||||||
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.2.0</PackageReleaseNotes>
|
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.3.1</PackageReleaseNotes>
|
||||||
<RepositoryUrl>https://github.com/oqtane/oqtane.framework</RepositoryUrl>
|
<RepositoryUrl>https://github.com/oqtane/oqtane.framework</RepositoryUrl>
|
||||||
<RepositoryType>Git</RepositoryType>
|
<RepositoryType>Git</RepositoryType>
|
||||||
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
|
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
|
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
|
||||||
<metadata>
|
<metadata>
|
||||||
<id>Oqtane.Database.Sqlite</id>
|
<id>Oqtane.Database.Sqlite</id>
|
||||||
<version>3.2.0</version>
|
<version>3.3.1</version>
|
||||||
<authors>Shaun Walker</authors>
|
<authors>Shaun Walker</authors>
|
||||||
<owners>.NET Foundation</owners>
|
<owners>.NET Foundation</owners>
|
||||||
<title>Oqtane SQLite Provider</title>
|
<title>Oqtane SQLite Provider</title>
|
||||||
@ -12,7 +12,7 @@
|
|||||||
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
||||||
<license type="expression">MIT</license>
|
<license type="expression">MIT</license>
|
||||||
<projectUrl>https://github.com/oqtane/oqtane.framework</projectUrl>
|
<projectUrl>https://github.com/oqtane/oqtane.framework</projectUrl>
|
||||||
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.2.0</releaseNotes>
|
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.3.1</releaseNotes>
|
||||||
<icon>icon.png</icon>
|
<icon>icon.png</icon>
|
||||||
<tags>oqtane</tags>
|
<tags>oqtane</tags>
|
||||||
</metadata>
|
</metadata>
|
||||||
|
@ -12,7 +12,8 @@ namespace Oqtane.Maui;
|
|||||||
public static class MauiProgram
|
public static class MauiProgram
|
||||||
{
|
{
|
||||||
// the API service url
|
// the API service url
|
||||||
static string apiurl = "http://localhost:44357";
|
static string apiurl = "https://www.dnfprojects.com"; // for testing
|
||||||
|
//static string apiurl = "http://localhost:44357"; // for local development (Oqtane.Server must be already running for MAUI client to connect)
|
||||||
|
|
||||||
public static MauiApp CreateMauiApp()
|
public static MauiApp CreateMauiApp()
|
||||||
{
|
{
|
||||||
@ -72,7 +73,7 @@ public static class MauiProgram
|
|||||||
|
|
||||||
var dlls = new Dictionary<string, byte[]>();
|
var dlls = new Dictionary<string, byte[]>();
|
||||||
var pdbs = new Dictionary<string, byte[]>();
|
var pdbs = new Dictionary<string, byte[]>();
|
||||||
var filter = new List<string>();
|
var list = new List<string>();
|
||||||
|
|
||||||
var files = new List<string>();
|
var files = new List<string>();
|
||||||
foreach (var file in Directory.EnumerateFiles(folder, "*.dll", SearchOption.AllDirectories))
|
foreach (var file in Directory.EnumerateFiles(folder, "*.dll", SearchOption.AllDirectories))
|
||||||
@ -92,14 +93,14 @@ public static class MauiProgram
|
|||||||
var file = files.FirstOrDefault(item => item.Contains(assembly));
|
var file = files.FirstOrDefault(item => item.Contains(assembly));
|
||||||
if (file == null)
|
if (file == null)
|
||||||
{
|
{
|
||||||
filter.Add(assembly);
|
list.Add(assembly);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// check if newer version available
|
// check if newer version available
|
||||||
if (GetFileDate(assembly) > GetFileDate(file))
|
if (GetFileDate(assembly) > GetFileDate(file))
|
||||||
{
|
{
|
||||||
filter.Add(assembly);
|
list.Add(assembly);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -107,7 +108,7 @@ public static class MauiProgram
|
|||||||
// get assemblies already downloaded
|
// get assemblies already downloaded
|
||||||
foreach (var file in files)
|
foreach (var file in files)
|
||||||
{
|
{
|
||||||
if (assemblies.Contains(file) && !filter.Contains(file))
|
if (assemblies.Contains(file) && !list.Contains(file))
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@ -127,7 +128,10 @@ public static class MauiProgram
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
File.Delete(Path.Combine(folder, file));
|
foreach (var path in Directory.EnumerateFiles(folder, Path.GetFileNameWithoutExtension(file) + ".*"))
|
||||||
|
{
|
||||||
|
File.Delete(path);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
{
|
{
|
||||||
@ -138,13 +142,13 @@ public static class MauiProgram
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
filter.Add("*");
|
list.Add("*");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (filter.Count != 0)
|
if (list.Count != 0)
|
||||||
{
|
{
|
||||||
// get assemblies from server
|
// get assemblies from server
|
||||||
var zip = Task.Run(() => http.GetByteArrayAsync("/api/Installation/load?list=" + string.Join(",", filter))).GetAwaiter().GetResult();
|
var zip = Task.Run(() => http.GetByteArrayAsync("/api/Installation/load?list=" + string.Join(",", list))).GetAwaiter().GetResult();
|
||||||
|
|
||||||
// asemblies and debug symbols are packaged in a zip file
|
// asemblies and debug symbols are packaged in a zip file
|
||||||
using (ZipArchive archive = new ZipArchive(new MemoryStream(zip)))
|
using (ZipArchive archive = new ZipArchive(new MemoryStream(zip)))
|
||||||
@ -159,11 +163,6 @@ public static class MauiProgram
|
|||||||
// save assembly to local folder
|
// save assembly to local folder
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
int subfolder = entry.FullName.IndexOf('/');
|
|
||||||
if (subfolder != -1 && !Directory.Exists(Path.Combine(folder, entry.FullName.Substring(0, subfolder))))
|
|
||||||
{
|
|
||||||
Directory.CreateDirectory(Path.Combine(folder, entry.FullName.Substring(0, subfolder)));
|
|
||||||
}
|
|
||||||
using var stream = File.Create(Path.Combine(folder, entry.FullName));
|
using var stream = File.Create(Path.Combine(folder, entry.FullName));
|
||||||
stream.Write(file, 0, file.Length);
|
stream.Write(file, 0, file.Length);
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
<!-- Uncomment to also build the tizen app. You will need to install tizen by following this: https://github.com/Samsung/Tizen.NET -->
|
<!-- Uncomment to also build the tizen app. You will need to install tizen by following this: https://github.com/Samsung/Tizen.NET -->
|
||||||
<!-- <TargetFrameworks>$(TargetFrameworks);net6.0-tizen</TargetFrameworks> -->
|
<!-- <TargetFrameworks>$(TargetFrameworks);net6.0-tizen</TargetFrameworks> -->
|
||||||
<OutputType>Exe</OutputType>
|
<OutputType>Exe</OutputType>
|
||||||
<Version>3.2.0</Version>
|
<Version>3.3.1</Version>
|
||||||
<Product>Oqtane</Product>
|
<Product>Oqtane</Product>
|
||||||
<Authors>Shaun Walker</Authors>
|
<Authors>Shaun Walker</Authors>
|
||||||
<Company>.NET Foundation</Company>
|
<Company>.NET Foundation</Company>
|
||||||
@ -14,7 +14,7 @@
|
|||||||
<Copyright>.NET Foundation</Copyright>
|
<Copyright>.NET Foundation</Copyright>
|
||||||
<PackageProjectUrl>https://www.oqtane.org</PackageProjectUrl>
|
<PackageProjectUrl>https://www.oqtane.org</PackageProjectUrl>
|
||||||
<PackageLicenseUrl>https://github.com/oqtane/oqtane.framework/blob/dev/LICENSE</PackageLicenseUrl>
|
<PackageLicenseUrl>https://github.com/oqtane/oqtane.framework/blob/dev/LICENSE</PackageLicenseUrl>
|
||||||
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.2.0</PackageReleaseNotes>
|
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.3.1</PackageReleaseNotes>
|
||||||
<RepositoryUrl>https://github.com/oqtane/oqtane.framework</RepositoryUrl>
|
<RepositoryUrl>https://github.com/oqtane/oqtane.framework</RepositoryUrl>
|
||||||
<RepositoryType>Git</RepositoryType>
|
<RepositoryType>Git</RepositoryType>
|
||||||
<RootNamespace>Oqtane.Maui</RootNamespace>
|
<RootNamespace>Oqtane.Maui</RootNamespace>
|
||||||
@ -31,7 +31,7 @@
|
|||||||
<ApplicationIdGuid>0E29FC31-1B83-48ED-B6E0-9F3C67B775D4</ApplicationIdGuid>
|
<ApplicationIdGuid>0E29FC31-1B83-48ED-B6E0-9F3C67B775D4</ApplicationIdGuid>
|
||||||
|
|
||||||
<!-- Versions -->
|
<!-- Versions -->
|
||||||
<ApplicationDisplayVersion>3.2.0</ApplicationDisplayVersion>
|
<ApplicationDisplayVersion>3.3.1</ApplicationDisplayVersion>
|
||||||
<ApplicationVersion>1</ApplicationVersion>
|
<ApplicationVersion>1</ApplicationVersion>
|
||||||
|
|
||||||
<SupportedOSPlatformVersion Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'ios'">14.2</SupportedOSPlatformVersion>
|
<SupportedOSPlatformVersion Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'ios'">14.2</SupportedOSPlatformVersion>
|
||||||
@ -71,16 +71,8 @@
|
|||||||
<PackageReference Include="Microsoft.Extensions.Http" Version="6.0.0" />
|
<PackageReference Include="Microsoft.Extensions.Http" Version="6.0.0" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Localization" Version="6.0.3" />
|
<PackageReference Include="Microsoft.Extensions.Localization" Version="6.0.3" />
|
||||||
<PackageReference Include="System.Net.Http.Json" Version="6.0.0" />
|
<PackageReference Include="System.Net.Http.Json" Version="6.0.0" />
|
||||||
|
<PackageReference Include="Oqtane.Client" Version="3.3.1" />
|
||||||
|
<PackageReference Include="Oqtane.Shared" Version="3.3.1" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<Reference Include="Oqtane.Client">
|
|
||||||
<HintPath>..\Oqtane.Server\bin\Debug\net6.0\Oqtane.Client.dll</HintPath>
|
|
||||||
</Reference>
|
|
||||||
<Reference Include="Oqtane.Shared">
|
|
||||||
<HintPath>..\Oqtane.Server\bin\Debug\net6.0\Oqtane.Shared.dll</HintPath>
|
|
||||||
</Reference>
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
BIN
Oqtane.Maui/wwwroot/loading.gif
Normal file
BIN
Oqtane.Maui/wwwroot/loading.gif
Normal file
Binary file not shown.
After Width: | Height: | Size: 8.0 KiB |
@ -2,7 +2,7 @@
|
|||||||
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
|
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
|
||||||
<metadata>
|
<metadata>
|
||||||
<id>Oqtane.Client</id>
|
<id>Oqtane.Client</id>
|
||||||
<version>3.2.0</version>
|
<version>3.3.1</version>
|
||||||
<authors>Shaun Walker</authors>
|
<authors>Shaun Walker</authors>
|
||||||
<owners>.NET Foundation</owners>
|
<owners>.NET Foundation</owners>
|
||||||
<title>Oqtane Framework</title>
|
<title>Oqtane Framework</title>
|
||||||
@ -12,7 +12,7 @@
|
|||||||
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
||||||
<license type="expression">MIT</license>
|
<license type="expression">MIT</license>
|
||||||
<projectUrl>https://github.com/oqtane/oqtane.framework</projectUrl>
|
<projectUrl>https://github.com/oqtane/oqtane.framework</projectUrl>
|
||||||
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.2.0</releaseNotes>
|
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.3.1</releaseNotes>
|
||||||
<icon>icon.png</icon>
|
<icon>icon.png</icon>
|
||||||
<tags>oqtane</tags>
|
<tags>oqtane</tags>
|
||||||
</metadata>
|
</metadata>
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
|
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
|
||||||
<metadata>
|
<metadata>
|
||||||
<id>Oqtane.Framework</id>
|
<id>Oqtane.Framework</id>
|
||||||
<version>3.2.0</version>
|
<version>3.3.1</version>
|
||||||
<authors>Shaun Walker</authors>
|
<authors>Shaun Walker</authors>
|
||||||
<owners>.NET Foundation</owners>
|
<owners>.NET Foundation</owners>
|
||||||
<title>Oqtane Framework</title>
|
<title>Oqtane Framework</title>
|
||||||
@ -11,8 +11,8 @@
|
|||||||
<copyright>.NET Foundation</copyright>
|
<copyright>.NET Foundation</copyright>
|
||||||
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
||||||
<license type="expression">MIT</license>
|
<license type="expression">MIT</license>
|
||||||
<projectUrl>https://github.com/oqtane/oqtane.framework/releases/download/v3.2.0/Oqtane.Framework.3.2.0.Upgrade.zip</projectUrl>
|
<projectUrl>https://github.com/oqtane/oqtane.framework/releases/download/v3.3.1/Oqtane.Framework.3.3.1.Upgrade.zip</projectUrl>
|
||||||
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.2.0</releaseNotes>
|
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.3.1</releaseNotes>
|
||||||
<icon>icon.png</icon>
|
<icon>icon.png</icon>
|
||||||
<tags>oqtane framework</tags>
|
<tags>oqtane framework</tags>
|
||||||
</metadata>
|
</metadata>
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
|
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
|
||||||
<metadata>
|
<metadata>
|
||||||
<id>Oqtane.Server</id>
|
<id>Oqtane.Server</id>
|
||||||
<version>3.2.0</version>
|
<version>3.3.1</version>
|
||||||
<authors>Shaun Walker</authors>
|
<authors>Shaun Walker</authors>
|
||||||
<owners>.NET Foundation</owners>
|
<owners>.NET Foundation</owners>
|
||||||
<title>Oqtane Framework</title>
|
<title>Oqtane Framework</title>
|
||||||
@ -12,7 +12,7 @@
|
|||||||
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
||||||
<license type="expression">MIT</license>
|
<license type="expression">MIT</license>
|
||||||
<projectUrl>https://github.com/oqtane/oqtane.framework</projectUrl>
|
<projectUrl>https://github.com/oqtane/oqtane.framework</projectUrl>
|
||||||
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.2.0</releaseNotes>
|
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.3.1</releaseNotes>
|
||||||
<icon>icon.png</icon>
|
<icon>icon.png</icon>
|
||||||
<tags>oqtane</tags>
|
<tags>oqtane</tags>
|
||||||
</metadata>
|
</metadata>
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
|
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
|
||||||
<metadata>
|
<metadata>
|
||||||
<id>Oqtane.Shared</id>
|
<id>Oqtane.Shared</id>
|
||||||
<version>3.2.0</version>
|
<version>3.3.1</version>
|
||||||
<authors>Shaun Walker</authors>
|
<authors>Shaun Walker</authors>
|
||||||
<owners>.NET Foundation</owners>
|
<owners>.NET Foundation</owners>
|
||||||
<title>Oqtane Framework</title>
|
<title>Oqtane Framework</title>
|
||||||
@ -12,7 +12,7 @@
|
|||||||
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
||||||
<license type="expression">MIT</license>
|
<license type="expression">MIT</license>
|
||||||
<projectUrl>https://github.com/oqtane/oqtane.framework</projectUrl>
|
<projectUrl>https://github.com/oqtane/oqtane.framework</projectUrl>
|
||||||
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.2.0</releaseNotes>
|
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.3.1</releaseNotes>
|
||||||
<icon>icon.png</icon>
|
<icon>icon.png</icon>
|
||||||
<tags>oqtane</tags>
|
<tags>oqtane</tags>
|
||||||
</metadata>
|
</metadata>
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
|
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
|
||||||
<metadata>
|
<metadata>
|
||||||
<id>Oqtane.Updater</id>
|
<id>Oqtane.Updater</id>
|
||||||
<version>3.2.0</version>
|
<version>3.3.1</version>
|
||||||
<authors>Shaun Walker</authors>
|
<authors>Shaun Walker</authors>
|
||||||
<owners>.NET Foundation</owners>
|
<owners>.NET Foundation</owners>
|
||||||
<title>Oqtane Framework</title>
|
<title>Oqtane Framework</title>
|
||||||
@ -12,7 +12,7 @@
|
|||||||
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
||||||
<license type="expression">MIT</license>
|
<license type="expression">MIT</license>
|
||||||
<projectUrl>https://github.com/oqtane/oqtane.framework</projectUrl>
|
<projectUrl>https://github.com/oqtane/oqtane.framework</projectUrl>
|
||||||
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.2.0</releaseNotes>
|
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.3.1</releaseNotes>
|
||||||
<icon>icon.png</icon>
|
<icon>icon.png</icon>
|
||||||
<tags>oqtane</tags>
|
<tags>oqtane</tags>
|
||||||
</metadata>
|
</metadata>
|
||||||
|
@ -1 +1 @@
|
|||||||
Compress-Archive -Path "..\Oqtane.Server\bin\Release\net6.0\publish\*" -DestinationPath "Oqtane.Framework.3.2.0.Install.zip" -Force
|
Compress-Archive -Path "..\Oqtane.Server\bin\Release\net6.0\publish\*" -DestinationPath "Oqtane.Framework.3.3.1.Install.zip" -Force
|
@ -13,6 +13,23 @@ rmdir /Q/S "..\Oqtane.Server\bin\Release\net6.0\publish"
|
|||||||
dotnet publish ..\Oqtane.Server\Oqtane.Server.csproj /p:Configuration=Release
|
dotnet publish ..\Oqtane.Server\Oqtane.Server.csproj /p:Configuration=Release
|
||||||
del /F/Q/S "..\Oqtane.Server\bin\Release\net6.0\publish\wwwroot\Content" > NUL
|
del /F/Q/S "..\Oqtane.Server\bin\Release\net6.0\publish\wwwroot\Content" > NUL
|
||||||
rmdir /Q/S "..\Oqtane.Server\bin\Release\net6.0\publish\wwwroot\Content"
|
rmdir /Q/S "..\Oqtane.Server\bin\Release\net6.0\publish\wwwroot\Content"
|
||||||
|
setlocal ENABLEDELAYEDEXPANSION
|
||||||
|
set retain=Oqtane.Modules.Admin.Login,Oqtane.Modules.HtmlText,Templates
|
||||||
|
for /D %%i in ("..\Oqtane.Server\bin\Release\net6.0\publish\wwwroot\Modules\*") 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"
|
||||||
|
)
|
||||||
|
set retain=Oqtane.Themes.BlazorTheme,Oqtane.Themes.OqtaneTheme,Templates
|
||||||
|
for /D %%i in ("..\Oqtane.Server\bin\Release\net6.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\net6.0\publish\appsettings.json"
|
del "..\Oqtane.Server\bin\Release\net6.0\publish\appsettings.json"
|
||||||
ren "..\Oqtane.Server\bin\Release\net6.0\publish\appsettings.release.json" "appsettings.json"
|
ren "..\Oqtane.Server\bin\Release\net6.0\publish\appsettings.release.json" "appsettings.json"
|
||||||
C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe ".\install.ps1"
|
C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe ".\install.ps1"
|
||||||
|
@ -1 +1 @@
|
|||||||
Compress-Archive -Path "..\Oqtane.Server\bin\Release\net6.0\publish\*" -DestinationPath "Oqtane.Framework.3.2.0.Upgrade.zip" -Force
|
Compress-Archive -Path "..\Oqtane.Server\bin\Release\net6.0\publish\*" -DestinationPath "Oqtane.Framework.3.3.1.Upgrade.zip" -Force
|
@ -17,13 +17,15 @@ namespace Oqtane.Controllers
|
|||||||
{
|
{
|
||||||
private readonly IAliasRepository _aliases;
|
private readonly IAliasRepository _aliases;
|
||||||
private readonly ITenantRepository _tenants;
|
private readonly ITenantRepository _tenants;
|
||||||
|
private readonly ISyncManager _syncManager;
|
||||||
private readonly ILogManager _logger;
|
private readonly ILogManager _logger;
|
||||||
private readonly Alias _alias;
|
private readonly Alias _alias;
|
||||||
|
|
||||||
public AliasController(IAliasRepository aliases, ITenantRepository tenants, ILogManager logger, ITenantManager tenantManager)
|
public AliasController(IAliasRepository aliases, ITenantRepository tenants, ISyncManager syncManager, ILogManager logger, ITenantManager tenantManager)
|
||||||
{
|
{
|
||||||
_aliases = aliases;
|
_aliases = aliases;
|
||||||
_tenants = tenants;
|
_tenants = tenants;
|
||||||
|
_syncManager = syncManager;
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
_alias = tenantManager.GetAlias();
|
_alias = tenantManager.GetAlias();
|
||||||
}
|
}
|
||||||
@ -57,6 +59,7 @@ namespace Oqtane.Controllers
|
|||||||
if (ModelState.IsValid)
|
if (ModelState.IsValid)
|
||||||
{
|
{
|
||||||
alias = _aliases.AddAlias(alias);
|
alias = _aliases.AddAlias(alias);
|
||||||
|
_syncManager.AddSyncEvent(alias.TenantId, EntityNames.Alias, alias.AliasId, SyncEventActions.Create);
|
||||||
_logger.Log(LogLevel.Information, this, LogFunction.Create, "Alias Added {Alias}", alias);
|
_logger.Log(LogLevel.Information, this, LogFunction.Create, "Alias Added {Alias}", alias);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -76,6 +79,7 @@ namespace Oqtane.Controllers
|
|||||||
if (ModelState.IsValid && _aliases.GetAlias(alias.AliasId, false) != null)
|
if (ModelState.IsValid && _aliases.GetAlias(alias.AliasId, false) != null)
|
||||||
{
|
{
|
||||||
alias = _aliases.UpdateAlias(alias);
|
alias = _aliases.UpdateAlias(alias);
|
||||||
|
_syncManager.AddSyncEvent(alias.TenantId, EntityNames.Alias, alias.AliasId, SyncEventActions.Update);
|
||||||
_logger.Log(LogLevel.Information, this, LogFunction.Update, "Alias Updated {Alias}", alias);
|
_logger.Log(LogLevel.Information, this, LogFunction.Update, "Alias Updated {Alias}", alias);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -96,6 +100,7 @@ namespace Oqtane.Controllers
|
|||||||
if (alias != null)
|
if (alias != null)
|
||||||
{
|
{
|
||||||
_aliases.DeleteAlias(id);
|
_aliases.DeleteAlias(id);
|
||||||
|
_syncManager.AddSyncEvent(alias.TenantId, EntityNames.Alias, alias.AliasId, SyncEventActions.Delete);
|
||||||
_logger.Log(LogLevel.Information, this, LogFunction.Delete, "Alias Deleted {AliasId}", id);
|
_logger.Log(LogLevel.Information, this, LogFunction.Delete, "Alias Deleted {AliasId}", id);
|
||||||
|
|
||||||
var aliases = _aliases.GetAliases();
|
var aliases = _aliases.GetAliases();
|
||||||
|
@ -20,6 +20,7 @@ using SixLabors.ImageSharp;
|
|||||||
using SixLabors.ImageSharp.Processing;
|
using SixLabors.ImageSharp.Processing;
|
||||||
using SixLabors.ImageSharp.Formats.Png;
|
using SixLabors.ImageSharp.Formats.Png;
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
|
using Oqtane.Migrations.Tenant;
|
||||||
|
|
||||||
// ReSharper disable StringIndexOfIsCultureSpecific.1
|
// ReSharper disable StringIndexOfIsCultureSpecific.1
|
||||||
|
|
||||||
@ -32,15 +33,17 @@ namespace Oqtane.Controllers
|
|||||||
private readonly IFileRepository _files;
|
private readonly IFileRepository _files;
|
||||||
private readonly IFolderRepository _folders;
|
private readonly IFolderRepository _folders;
|
||||||
private readonly IUserPermissions _userPermissions;
|
private readonly IUserPermissions _userPermissions;
|
||||||
|
private readonly ISyncManager _syncManager;
|
||||||
private readonly ILogManager _logger;
|
private readonly ILogManager _logger;
|
||||||
private readonly Alias _alias;
|
private readonly Alias _alias;
|
||||||
|
|
||||||
public FileController(IWebHostEnvironment environment, IFileRepository files, IFolderRepository folders, IUserPermissions userPermissions, ILogManager logger, ITenantManager tenantManager)
|
public FileController(IWebHostEnvironment environment, IFileRepository files, IFolderRepository folders, IUserPermissions userPermissions, ISyncManager syncManager, ILogManager logger, ITenantManager tenantManager)
|
||||||
{
|
{
|
||||||
_environment = environment;
|
_environment = environment;
|
||||||
_files = files;
|
_files = files;
|
||||||
_folders = folders;
|
_folders = folders;
|
||||||
_userPermissions = userPermissions;
|
_userPermissions = userPermissions;
|
||||||
|
_syncManager = syncManager;
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
_alias = tenantManager.GetAlias();
|
_alias = tenantManager.GetAlias();
|
||||||
}
|
}
|
||||||
@ -133,7 +136,9 @@ namespace Oqtane.Controllers
|
|||||||
public Models.File Put(int id, [FromBody] Models.File file)
|
public Models.File Put(int id, [FromBody] Models.File file)
|
||||||
{
|
{
|
||||||
var File = _files.GetFile(file.FileId, false);
|
var File = _files.GetFile(file.FileId, false);
|
||||||
if (ModelState.IsValid && File != null && File.Folder.SiteId == _alias.SiteId && _userPermissions.IsAuthorized(User, EntityNames.Folder, file.FolderId, PermissionNames.Edit))
|
if (ModelState.IsValid && file.Folder.SiteId == _alias.SiteId && File != null // ensure file exists
|
||||||
|
&& _userPermissions.IsAuthorized(User, file.Folder.SiteId, EntityNames.Folder, File.FolderId, PermissionNames.Edit) // ensure user had edit rights to original folder
|
||||||
|
&& _userPermissions.IsAuthorized(User, file.Folder.SiteId, EntityNames.Folder, file.FolderId, PermissionNames.Edit)) // ensure user has edit rights to new folder
|
||||||
{
|
{
|
||||||
if (File.Name != file.Name || File.FolderId != file.FolderId)
|
if (File.Name != file.Name || File.FolderId != file.FolderId)
|
||||||
{
|
{
|
||||||
@ -146,8 +151,17 @@ namespace Oqtane.Controllers
|
|||||||
System.IO.File.Move(_files.GetFilePath(File), Path.Combine(folderpath, file.Name));
|
System.IO.File.Move(_files.GetFilePath(File), Path.Combine(folderpath, file.Name));
|
||||||
}
|
}
|
||||||
|
|
||||||
file.Extension = Path.GetExtension(file.Name).ToLower().Replace(".", "");
|
var newfile = CreateFile(file.Name, file.Folder.FolderId, _files.GetFilePath(file));
|
||||||
|
if (newfile != null)
|
||||||
|
{
|
||||||
|
file.Extension = newfile.Extension;
|
||||||
|
file.Size = newfile.Size;
|
||||||
|
file.ImageWidth = newfile.ImageWidth;
|
||||||
|
file.ImageHeight = newfile.ImageHeight;
|
||||||
|
}
|
||||||
|
|
||||||
file = _files.UpdateFile(file);
|
file = _files.UpdateFile(file);
|
||||||
|
_syncManager.AddSyncEvent(_alias.TenantId, EntityNames.File, file.FileId, SyncEventActions.Update);
|
||||||
_logger.Log(LogLevel.Information, this, LogFunction.Update, "File Updated {File}", file);
|
_logger.Log(LogLevel.Information, this, LogFunction.Update, "File Updated {File}", file);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -166,10 +180,8 @@ namespace Oqtane.Controllers
|
|||||||
public void Delete(int id)
|
public void Delete(int id)
|
||||||
{
|
{
|
||||||
Models.File file = _files.GetFile(id);
|
Models.File file = _files.GetFile(id);
|
||||||
if (file != null && file.Folder.SiteId == _alias.SiteId && _userPermissions.IsAuthorized(User, EntityNames.Folder, file.Folder.FolderId, PermissionNames.Edit))
|
if (file != null && file.Folder.SiteId == _alias.SiteId && _userPermissions.IsAuthorized(User, file.Folder.SiteId, EntityNames.Folder, file.Folder.FolderId, PermissionNames.Edit))
|
||||||
{
|
{
|
||||||
_files.DeleteFile(id);
|
|
||||||
|
|
||||||
string filepath = _files.GetFilePath(file);
|
string filepath = _files.GetFilePath(file);
|
||||||
if (System.IO.File.Exists(filepath))
|
if (System.IO.File.Exists(filepath))
|
||||||
{
|
{
|
||||||
@ -180,6 +192,8 @@ namespace Oqtane.Controllers
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_files.DeleteFile(id);
|
||||||
|
_syncManager.AddSyncEvent(_alias.TenantId, EntityNames.File, file.FileId, SyncEventActions.Delete);
|
||||||
_logger.Log(LogLevel.Information, this, LogFunction.Delete, "File Deleted {File}", file);
|
_logger.Log(LogLevel.Information, this, LogFunction.Delete, "File Deleted {File}", file);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -251,6 +265,7 @@ namespace Oqtane.Controllers
|
|||||||
if (file != null)
|
if (file != null)
|
||||||
{
|
{
|
||||||
file = _files.AddFile(file);
|
file = _files.AddFile(file);
|
||||||
|
_syncManager.AddSyncEvent(_alias.TenantId, EntityNames.File, file.FileId, SyncEventActions.Create);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
@ -324,7 +339,16 @@ namespace Oqtane.Controllers
|
|||||||
var file = CreateFile(upload, FolderId, Path.Combine(folderPath, upload));
|
var file = CreateFile(upload, FolderId, Path.Combine(folderPath, upload));
|
||||||
if (file != null)
|
if (file != null)
|
||||||
{
|
{
|
||||||
_files.AddFile(file);
|
if (file.FileId == 0)
|
||||||
|
{
|
||||||
|
file = _files.AddFile(file);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
file = _files.UpdateFile(file);
|
||||||
|
}
|
||||||
|
_logger.Log(LogLevel.Information, this, LogFunction.Create, "File Upload Succeeded {File}", Path.Combine(folderPath, upload));
|
||||||
|
_syncManager.AddSyncEvent(_alias.TenantId, EntityNames.File, file.FileId, SyncEventActions.Create);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -345,16 +369,16 @@ namespace Oqtane.Controllers
|
|||||||
int totalparts = int.Parse(parts?.Substring(parts.IndexOf("_") + 1));
|
int totalparts = int.Parse(parts?.Substring(parts.IndexOf("_") + 1));
|
||||||
|
|
||||||
filename = Path.GetFileNameWithoutExtension(filename); // base filename
|
filename = Path.GetFileNameWithoutExtension(filename); // base filename
|
||||||
string[] fileParts = Directory.GetFiles(folder, filename + token + "*"); // list of all file parts
|
string[] fileparts = Directory.GetFiles(folder, filename + token + "*"); // list of all file parts
|
||||||
|
|
||||||
// if all of the file parts exist ( note that file parts can arrive out of order )
|
// if all of the file parts exist ( note that file parts can arrive out of order )
|
||||||
if (fileParts.Length == totalparts && CanAccessFiles(fileParts))
|
if (fileparts.Length == totalparts && CanAccessFiles(fileparts))
|
||||||
{
|
{
|
||||||
// merge file parts
|
// merge file parts into temp file ( in case another user is trying to get the file )
|
||||||
bool success = true;
|
bool success = true;
|
||||||
using (var stream = new FileStream(Path.Combine(folder, filename + ".tmp"), FileMode.Create))
|
using (var stream = new FileStream(Path.Combine(folder, filename + ".tmp"), FileMode.Create))
|
||||||
{
|
{
|
||||||
foreach (string filepart in fileParts)
|
foreach (string filepart in fileparts)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@ -370,52 +394,49 @@ namespace Oqtane.Controllers
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// delete file parts and rename file
|
// clean up file parts
|
||||||
|
foreach (var file in Directory.GetFiles(folder, "*" + token + "*"))
|
||||||
|
{
|
||||||
|
// file name matches part or is more than 2 hours old (ie. a prior file upload failed)
|
||||||
|
if (fileparts.Contains(file) || System.IO.File.GetCreationTime(file).ToUniversalTime() < DateTime.UtcNow.AddHours(-2))
|
||||||
|
{
|
||||||
|
System.IO.File.Delete(file);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// rename temp file
|
||||||
if (success)
|
if (success)
|
||||||
{
|
{
|
||||||
foreach (string filepart in fileParts)
|
// remove file if it already exists (as well as any thumbnails)
|
||||||
|
foreach (var file in Directory.GetFiles(folder, Path.GetFileNameWithoutExtension(filename) + ".*"))
|
||||||
{
|
{
|
||||||
System.IO.File.Delete(filepart);
|
if (Path.GetExtension(file) != ".tmp")
|
||||||
|
{
|
||||||
|
System.IO.File.Delete(file);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// remove file if it already exists
|
// rename temp file now that the entire process is completed
|
||||||
if (System.IO.File.Exists(Path.Combine(folder, filename)))
|
|
||||||
{
|
|
||||||
System.IO.File.Delete(Path.Combine(folder, filename));
|
|
||||||
}
|
|
||||||
|
|
||||||
// rename file now that the entire process is completed
|
|
||||||
System.IO.File.Move(Path.Combine(folder, filename + ".tmp"), Path.Combine(folder, filename));
|
System.IO.File.Move(Path.Combine(folder, filename + ".tmp"), Path.Combine(folder, filename));
|
||||||
_logger.Log(LogLevel.Information, this, LogFunction.Create, "File Uploaded {File}", Path.Combine(folder, filename));
|
|
||||||
|
|
||||||
|
// return filename
|
||||||
merged = filename;
|
merged = filename;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// clean up file parts which are more than 2 hours old ( which can happen if a prior file upload failed )
|
|
||||||
var cleanupFiles = Directory.EnumerateFiles(folder, "*" + token + "*")
|
|
||||||
.Where(f => Path.GetExtension(f).StartsWith(token) && !Path.GetFileName(f).StartsWith(filename));
|
|
||||||
foreach (var file in cleanupFiles)
|
|
||||||
{
|
|
||||||
var createdDate = System.IO.File.GetCreationTime(file).ToUniversalTime();
|
|
||||||
if (createdDate < DateTime.UtcNow.AddHours(-2))
|
|
||||||
{
|
|
||||||
System.IO.File.Delete(file);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return merged;
|
return merged;
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool CanAccessFiles(string[] files)
|
private bool CanAccessFiles(string[] files)
|
||||||
{
|
{
|
||||||
// ensure files are not locked by another process ( ie. still being written to )
|
// ensure files are not locked by another process
|
||||||
bool canaccess = true;
|
|
||||||
FileStream stream = null;
|
FileStream stream = null;
|
||||||
|
bool locked = false;
|
||||||
foreach (string file in files)
|
foreach (string file in files)
|
||||||
{
|
{
|
||||||
|
locked = true;
|
||||||
int attempts = 0;
|
int attempts = 0;
|
||||||
bool locked = true;
|
// note that this will wait a maximum of 15 seconds for a file to become unlocked
|
||||||
while (attempts < 5 && locked)
|
while (attempts < 5 && locked)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
@ -425,7 +446,8 @@ namespace Oqtane.Controllers
|
|||||||
}
|
}
|
||||||
catch // file is locked by another process
|
catch // file is locked by another process
|
||||||
{
|
{
|
||||||
Thread.Sleep(1000); // wait 1 second
|
attempts += 1;
|
||||||
|
Thread.Sleep(1000 * attempts); // progressive retry
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
@ -434,20 +456,15 @@ namespace Oqtane.Controllers
|
|||||||
stream.Close();
|
stream.Close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
attempts += 1;
|
|
||||||
}
|
}
|
||||||
|
if (locked)
|
||||||
if (locked && canaccess)
|
|
||||||
{
|
{
|
||||||
canaccess = false;
|
break; // file still locked after retrying
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return !locked;
|
||||||
return canaccess;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Get file with header
|
/// Get file with header
|
||||||
/// Content-Disposition: inline
|
/// Content-Disposition: inline
|
||||||
@ -486,10 +503,15 @@ namespace Oqtane.Controllers
|
|||||||
var filepath = _files.GetFilePath(file);
|
var filepath = _files.GetFilePath(file);
|
||||||
if (System.IO.File.Exists(filepath))
|
if (System.IO.File.Exists(filepath))
|
||||||
{
|
{
|
||||||
var result = asAttachment
|
if (asAttachment)
|
||||||
? PhysicalFile(filepath, file.GetMimeType(), file.Name)
|
{
|
||||||
: PhysicalFile(filepath, file.GetMimeType());
|
_syncManager.AddSyncEvent(_alias.TenantId, EntityNames.File, file.FileId, "Download");
|
||||||
return result;
|
return PhysicalFile(filepath, file.GetMimeType(), file.Name);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return PhysicalFile(filepath, file.GetMimeType());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -634,7 +656,7 @@ namespace Oqtane.Controllers
|
|||||||
|
|
||||||
private Models.File CreateFile(string filename, int folderid, string filepath)
|
private Models.File CreateFile(string filename, int folderid, string filepath)
|
||||||
{
|
{
|
||||||
Models.File file = null;
|
var file = _files.GetFile(folderid, filename);
|
||||||
|
|
||||||
int size = 0;
|
int size = 0;
|
||||||
var folder = _folders.GetFolder(folderid);
|
var folder = _folders.GetFolder(folderid);
|
||||||
@ -649,7 +671,10 @@ namespace Oqtane.Controllers
|
|||||||
FileInfo fileinfo = new FileInfo(filepath);
|
FileInfo fileinfo = new FileInfo(filepath);
|
||||||
if (folder.Capacity == 0 || ((size + fileinfo.Length) / 1000000) < folder.Capacity)
|
if (folder.Capacity == 0 || ((size + fileinfo.Length) / 1000000) < folder.Capacity)
|
||||||
{
|
{
|
||||||
file = new Models.File();
|
if (file == null)
|
||||||
|
{
|
||||||
|
file = new Models.File();
|
||||||
|
}
|
||||||
file.Name = filename;
|
file.Name = filename;
|
||||||
file.FolderId = folderid;
|
file.FolderId = folderid;
|
||||||
|
|
||||||
@ -679,7 +704,11 @@ namespace Oqtane.Controllers
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
System.IO.File.Delete(filepath);
|
System.IO.File.Delete(filepath);
|
||||||
_logger.Log(LogLevel.Warning, this, LogFunction.Create, "File Exceeds Folder Capacity {Folder} {File}", folder, filepath);
|
if (file != null)
|
||||||
|
{
|
||||||
|
_files.DeleteFile(file.FileId);
|
||||||
|
}
|
||||||
|
_logger.Log(LogLevel.Warning, this, LogFunction.Create, "File Exceeds Folder Capacity And Has Been Removed {Folder} {File}", folder, filepath);
|
||||||
}
|
}
|
||||||
|
|
||||||
return file;
|
return file;
|
||||||
|
@ -20,13 +20,15 @@ namespace Oqtane.Controllers
|
|||||||
{
|
{
|
||||||
private readonly IFolderRepository _folders;
|
private readonly IFolderRepository _folders;
|
||||||
private readonly IUserPermissions _userPermissions;
|
private readonly IUserPermissions _userPermissions;
|
||||||
|
private readonly ISyncManager _syncManager;
|
||||||
private readonly ILogManager _logger;
|
private readonly ILogManager _logger;
|
||||||
private readonly Alias _alias;
|
private readonly Alias _alias;
|
||||||
|
|
||||||
public FolderController(IFolderRepository folders, IUserPermissions userPermissions, ILogManager logger, ITenantManager tenantManager)
|
public FolderController(IFolderRepository folders, IUserPermissions userPermissions, ISyncManager syncManager, ILogManager logger, ITenantManager tenantManager)
|
||||||
{
|
{
|
||||||
_folders = folders;
|
_folders = folders;
|
||||||
_userPermissions = userPermissions;
|
_userPermissions = userPermissions;
|
||||||
|
_syncManager = syncManager;
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
_alias = tenantManager.GetAlias();
|
_alias = tenantManager.GetAlias();
|
||||||
}
|
}
|
||||||
@ -124,6 +126,7 @@ namespace Oqtane.Controllers
|
|||||||
}
|
}
|
||||||
folder.Path = folder.Path + "/";
|
folder.Path = folder.Path + "/";
|
||||||
folder = _folders.AddFolder(folder);
|
folder = _folders.AddFolder(folder);
|
||||||
|
_syncManager.AddSyncEvent(_alias.TenantId, EntityNames.Folder, folder.FolderId, SyncEventActions.Create);
|
||||||
_logger.Log(LogLevel.Information, this, LogFunction.Create, "Folder Added {Folder}", folder);
|
_logger.Log(LogLevel.Information, this, LogFunction.Create, "Folder Added {Folder}", folder);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -154,7 +157,7 @@ namespace Oqtane.Controllers
|
|||||||
[Authorize(Roles = RoleNames.Registered)]
|
[Authorize(Roles = RoleNames.Registered)]
|
||||||
public Folder Put(int id, [FromBody] Folder folder)
|
public Folder Put(int id, [FromBody] Folder folder)
|
||||||
{
|
{
|
||||||
if (ModelState.IsValid && folder.SiteId == _alias.SiteId && _folders.GetFolder(folder.FolderId, false) != null && _userPermissions.IsAuthorized(User, EntityNames.Folder, folder.FolderId, PermissionNames.Edit))
|
if (ModelState.IsValid && folder.SiteId == _alias.SiteId && _folders.GetFolder(folder.FolderId, false) != null && _userPermissions.IsAuthorized(User, folder.SiteId, EntityNames.Folder, folder.FolderId, PermissionNames.Edit))
|
||||||
{
|
{
|
||||||
if (folder.IsPathValid())
|
if (folder.IsPathValid())
|
||||||
{
|
{
|
||||||
@ -172,6 +175,7 @@ namespace Oqtane.Controllers
|
|||||||
}
|
}
|
||||||
|
|
||||||
folder = _folders.UpdateFolder(folder);
|
folder = _folders.UpdateFolder(folder);
|
||||||
|
_syncManager.AddSyncEvent(_alias.TenantId, EntityNames.Folder, folder.FolderId, SyncEventActions.Update);
|
||||||
_logger.Log(LogLevel.Information, this, LogFunction.Update, "Folder Updated {Folder}", folder);
|
_logger.Log(LogLevel.Information, this, LogFunction.Update, "Folder Updated {Folder}", folder);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -195,7 +199,7 @@ namespace Oqtane.Controllers
|
|||||||
[Authorize(Roles = RoleNames.Registered)]
|
[Authorize(Roles = RoleNames.Registered)]
|
||||||
public void Put(int siteid, int folderid, int? parentid)
|
public void Put(int siteid, int folderid, int? parentid)
|
||||||
{
|
{
|
||||||
if (siteid == _alias.SiteId && _folders.GetFolder(folderid, false) != null && _userPermissions.IsAuthorized(User, EntityNames.Folder, folderid, PermissionNames.Edit))
|
if (siteid == _alias.SiteId && _folders.GetFolder(folderid, false) != null && _userPermissions.IsAuthorized(User, siteid, EntityNames.Folder, folderid, PermissionNames.Edit))
|
||||||
{
|
{
|
||||||
int order = 1;
|
int order = 1;
|
||||||
List<Folder> folders = _folders.GetFolders(siteid).ToList();
|
List<Folder> folders = _folders.GetFolders(siteid).ToList();
|
||||||
@ -205,6 +209,7 @@ namespace Oqtane.Controllers
|
|||||||
{
|
{
|
||||||
folder.Order = order;
|
folder.Order = order;
|
||||||
_folders.UpdateFolder(folder);
|
_folders.UpdateFolder(folder);
|
||||||
|
_syncManager.AddSyncEvent(_alias.TenantId, EntityNames.Folder, folder.FolderId, SyncEventActions.Update);
|
||||||
}
|
}
|
||||||
order += 2;
|
order += 2;
|
||||||
}
|
}
|
||||||
@ -223,13 +228,14 @@ namespace Oqtane.Controllers
|
|||||||
public void Delete(int id)
|
public void Delete(int id)
|
||||||
{
|
{
|
||||||
var folder = _folders.GetFolder(id, false);
|
var folder = _folders.GetFolder(id, false);
|
||||||
if (folder != null && folder.SiteId == _alias.SiteId && _userPermissions.IsAuthorized(User, EntityNames.Folder, id, PermissionNames.Edit))
|
if (folder != null && folder.SiteId == _alias.SiteId && _userPermissions.IsAuthorized(User, folder.SiteId, EntityNames.Folder, id, PermissionNames.Edit))
|
||||||
{
|
{
|
||||||
if (Directory.Exists(_folders.GetFolderPath(folder)))
|
if (Directory.Exists(_folders.GetFolderPath(folder)))
|
||||||
{
|
{
|
||||||
Directory.Delete(_folders.GetFolderPath(folder));
|
Directory.Delete(_folders.GetFolderPath(folder));
|
||||||
}
|
}
|
||||||
_folders.DeleteFolder(id);
|
_folders.DeleteFolder(id);
|
||||||
|
_syncManager.AddSyncEvent(_alias.TenantId, EntityNames.Folder, folder.FolderId, SyncEventActions.Delete);
|
||||||
_logger.Log(LogLevel.Information, this, LogFunction.Delete, "Folder Deleted {FolderId}", id);
|
_logger.Log(LogLevel.Information, this, LogFunction.Delete, "Folder Deleted {FolderId}", id);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -103,10 +103,7 @@ namespace Oqtane.Controllers
|
|||||||
[HttpGet("list")]
|
[HttpGet("list")]
|
||||||
public List<string> List()
|
public List<string> List()
|
||||||
{
|
{
|
||||||
return _cache.GetOrCreate("assemblieslist", entry =>
|
return GetAssemblyList().Select(item => item.HashedName).ToList();
|
||||||
{
|
|
||||||
return GetAssemblyList();
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// GET api/<controller>/load?list=x,y
|
// GET api/<controller>/load?list=x,y
|
||||||
@ -116,81 +113,90 @@ namespace Oqtane.Controllers
|
|||||||
return File(GetAssemblies(list), System.Net.Mime.MediaTypeNames.Application.Octet, "oqtane.dll");
|
return File(GetAssemblies(list), System.Net.Mime.MediaTypeNames.Application.Octet, "oqtane.dll");
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<string> GetAssemblyList()
|
private List<ClientAssembly> GetAssemblyList()
|
||||||
{
|
{
|
||||||
// get list of assemblies which should be downloaded to client
|
return _cache.GetOrCreate("assemblieslist", entry =>
|
||||||
var binFolder = Path.GetDirectoryName(Assembly.GetEntryAssembly().Location);
|
|
||||||
var assemblies = AppDomain.CurrentDomain.GetOqtaneClientAssemblies();
|
|
||||||
var list = assemblies.Select(a => a.GetName().Name).ToList();
|
|
||||||
|
|
||||||
// include version numbers
|
|
||||||
for (int i = 0; i < list.Count; i++)
|
|
||||||
{
|
{
|
||||||
list[i] = Path.GetFileName(AddFileDate(Path.Combine(binFolder, list[i] + ".dll")));
|
var binFolder = Path.GetDirectoryName(Assembly.GetEntryAssembly().Location);
|
||||||
}
|
var assemblyList = new List<ClientAssembly>();
|
||||||
|
|
||||||
// insert satellite assemblies at beginning of list
|
// get list of assemblies which should be downloaded to client
|
||||||
foreach (var culture in _localizationManager.GetInstalledCultures())
|
var assemblies = AppDomain.CurrentDomain.GetOqtaneClientAssemblies();
|
||||||
{
|
var list = assemblies.Select(a => a.GetName().Name).ToList();
|
||||||
var assembliesFolderPath = Path.Combine(binFolder, culture);
|
|
||||||
if (culture == Constants.DefaultCulture)
|
// populate assemblies
|
||||||
|
for (int i = 0; i < list.Count; i++)
|
||||||
{
|
{
|
||||||
continue;
|
assemblyList.Add(new ClientAssembly(Path.Combine(binFolder, list[i] + ".dll")));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Directory.Exists(assembliesFolderPath))
|
// insert satellite assemblies at beginning of list
|
||||||
|
foreach (var culture in _localizationManager.GetInstalledCultures())
|
||||||
{
|
{
|
||||||
foreach (var resourceFile in Directory.EnumerateFiles(assembliesFolderPath))
|
var assembliesFolderPath = Path.Combine(binFolder, culture);
|
||||||
|
if (culture == Constants.DefaultCulture)
|
||||||
{
|
{
|
||||||
list.Insert(0, culture + "/" + Path.GetFileName(AddFileDate(resourceFile)));
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Directory.Exists(assembliesFolderPath))
|
||||||
|
{
|
||||||
|
foreach (var resourceFile in Directory.EnumerateFiles(assembliesFolderPath))
|
||||||
|
{
|
||||||
|
assemblyList.Insert(0, new ClientAssembly(resourceFile));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_filelogger.LogError(Utilities.LogMessage(this, $"The Satellite Assembly Folder For {culture} Does Not Exist"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
_filelogger.LogError(Utilities.LogMessage(this, $"The Satellite Assembly Folder For {culture} Does Not Exist"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// insert module and theme dependencies at beginning of list
|
// insert module and theme dependencies at beginning of list
|
||||||
foreach (var assembly in assemblies)
|
foreach (var assembly in assemblies)
|
||||||
{
|
|
||||||
foreach (var type in assembly.GetTypes().Where(item => item.GetInterfaces().Contains(typeof(IModule))))
|
|
||||||
{
|
{
|
||||||
var instance = Activator.CreateInstance(type) as IModule;
|
foreach (var type in assembly.GetTypes().Where(item => item.GetInterfaces().Contains(typeof(IModule))))
|
||||||
foreach (string name in instance.ModuleDefinition.Dependencies.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries))
|
|
||||||
{
|
{
|
||||||
var path = Path.Combine(binFolder, name + ".dll");
|
var instance = Activator.CreateInstance(type) as IModule;
|
||||||
if (System.IO.File.Exists(path))
|
foreach (string name in instance.ModuleDefinition.Dependencies.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries).Reverse())
|
||||||
{
|
{
|
||||||
path = Path.GetFileName(AddFileDate(path));
|
var filepath = Path.Combine(binFolder, name + ".dll");
|
||||||
if (!list.Contains(path)) list.Insert(0, path);
|
if (System.IO.File.Exists(filepath))
|
||||||
|
{
|
||||||
|
if (!assemblyList.Exists(item => item.FilePath == filepath))
|
||||||
|
{
|
||||||
|
assemblyList.Insert(0, new ClientAssembly(filepath));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_filelogger.LogError(Utilities.LogMessage(this, $"Module {instance.ModuleDefinition.ModuleDefinitionName} Dependency {name}.dll Does Not Exist"));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
}
|
||||||
|
foreach (var type in assembly.GetTypes().Where(item => item.GetInterfaces().Contains(typeof(ITheme))))
|
||||||
|
{
|
||||||
|
var instance = Activator.CreateInstance(type) as ITheme;
|
||||||
|
foreach (string name in instance.Theme.Dependencies.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries).Reverse())
|
||||||
{
|
{
|
||||||
_filelogger.LogError(Utilities.LogMessage(this, $"Module {instance.ModuleDefinition.ModuleDefinitionName} Dependency {name}.dll Does Not Exist"));
|
var filepath = Path.Combine(binFolder, name + ".dll");
|
||||||
|
if (System.IO.File.Exists(filepath))
|
||||||
|
{
|
||||||
|
if (!assemblyList.Exists(item => item.FilePath == filepath))
|
||||||
|
{
|
||||||
|
assemblyList.Insert(0, new ClientAssembly(filepath));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_filelogger.LogError(Utilities.LogMessage(this, $"Theme {instance.Theme.ThemeName} Dependency {name}.dll Does Not Exist"));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
foreach (var type in assembly.GetTypes().Where(item => item.GetInterfaces().Contains(typeof(ITheme))))
|
|
||||||
{
|
|
||||||
var instance = Activator.CreateInstance(type) as ITheme;
|
|
||||||
foreach (string name in instance.Theme.Dependencies.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries))
|
|
||||||
{
|
|
||||||
var path = Path.Combine(binFolder, name + ".dll");
|
|
||||||
if (System.IO.File.Exists(path))
|
|
||||||
{
|
|
||||||
path = Path.GetFileName(AddFileDate(path));
|
|
||||||
if (!list.Contains(path)) list.Insert(0, path);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_filelogger.LogError(Utilities.LogMessage(this, $"Theme {instance.Theme.ThemeName} Dependency {name}.dll Does Not Exist"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return list;
|
return assemblyList;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private byte[] GetAssemblies(string list)
|
private byte[] GetAssemblies(string list)
|
||||||
@ -213,14 +219,11 @@ namespace Oqtane.Controllers
|
|||||||
var binFolder = Path.GetDirectoryName(Assembly.GetEntryAssembly().Location);
|
var binFolder = Path.GetDirectoryName(Assembly.GetEntryAssembly().Location);
|
||||||
|
|
||||||
// get list of assemblies which should be downloaded to client
|
// get list of assemblies which should be downloaded to client
|
||||||
List<string> assemblies;
|
List<ClientAssembly> assemblies = GetAssemblyList();
|
||||||
if (list == "*")
|
if (list != "*")
|
||||||
{
|
{
|
||||||
assemblies = GetAssemblyList();
|
var filter = list.Split(',').ToList();
|
||||||
}
|
assemblies.RemoveAll(item => !filter.Contains(item.HashedName));
|
||||||
else
|
|
||||||
{
|
|
||||||
assemblies = list.Split(',').ToList();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// create zip file containing assemblies and debug symbols
|
// create zip file containing assemblies and debug symbols
|
||||||
@ -228,22 +231,21 @@ namespace Oqtane.Controllers
|
|||||||
{
|
{
|
||||||
using (var archive = new ZipArchive(memoryStream, ZipArchiveMode.Create, true))
|
using (var archive = new ZipArchive(memoryStream, ZipArchiveMode.Create, true))
|
||||||
{
|
{
|
||||||
foreach (string file in assemblies)
|
foreach (var assembly in assemblies)
|
||||||
{
|
{
|
||||||
var filename = RemoveFileDate(file);
|
if (System.IO.File.Exists(assembly.FilePath))
|
||||||
if (System.IO.File.Exists(Path.Combine(binFolder, filename)))
|
|
||||||
{
|
{
|
||||||
using (var filestream = new FileStream(Path.Combine(binFolder, filename), FileMode.Open, FileAccess.Read))
|
using (var filestream = new FileStream(assembly.FilePath, FileMode.Open, FileAccess.Read))
|
||||||
using (var entrystream = archive.CreateEntry(file).Open())
|
using (var entrystream = archive.CreateEntry(assembly.HashedName).Open())
|
||||||
{
|
{
|
||||||
filestream.CopyTo(entrystream);
|
filestream.CopyTo(entrystream);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
filename = filename.Replace(".dll", ".pdb");
|
var pdb = assembly.FilePath.Replace(".dll", ".pdb");
|
||||||
if (System.IO.File.Exists(Path.Combine(binFolder, filename)))
|
if (System.IO.File.Exists(pdb))
|
||||||
{
|
{
|
||||||
using (var filestream = new FileStream(Path.Combine(binFolder, filename), FileMode.Open, FileAccess.Read))
|
using (var filestream = new FileStream(pdb, FileMode.Open, FileAccess.Read))
|
||||||
using (var entrystream = archive.CreateEntry(file.Replace(".dll", ".pdb")).Open())
|
using (var entrystream = archive.CreateEntry(assembly.HashedName.Replace(".dll", ".pdb")).Open())
|
||||||
{
|
{
|
||||||
filestream.CopyTo(entrystream);
|
filestream.CopyTo(entrystream);
|
||||||
}
|
}
|
||||||
@ -255,18 +257,6 @@ namespace Oqtane.Controllers
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private string AddFileDate(string filepath)
|
|
||||||
{
|
|
||||||
DateTime lastwritetime = System.IO.File.GetLastWriteTime(filepath);
|
|
||||||
return Path.GetFileNameWithoutExtension(filepath) + "." + lastwritetime.ToString("yyyyMMddHHmmss") + Path.GetExtension(filepath);
|
|
||||||
}
|
|
||||||
|
|
||||||
private string RemoveFileDate(string filepath)
|
|
||||||
{
|
|
||||||
var segments = filepath.Split(".");
|
|
||||||
return string.Join(".", segments, 0, segments.Length - 2) + Path.GetExtension(filepath);
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task RegisterContact(string email)
|
private async Task RegisterContact(string email)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
@ -292,5 +282,38 @@ namespace Oqtane.Controllers
|
|||||||
{
|
{
|
||||||
await RegisterContact(email);
|
await RegisterContact(email);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public struct ClientAssembly
|
||||||
|
{
|
||||||
|
public ClientAssembly(string filepath)
|
||||||
|
{
|
||||||
|
FilePath = filepath;
|
||||||
|
DateTime lastwritetime = System.IO.File.GetLastWriteTime(filepath);
|
||||||
|
HashedName = GetDeterministicHashCode(filepath).ToString("X8") + "." + lastwritetime.ToString("yyyyMMddHHmmss") + Path.GetExtension(filepath);
|
||||||
|
}
|
||||||
|
|
||||||
|
public string FilePath { get; private set; }
|
||||||
|
public string HashedName { get; private set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int GetDeterministicHashCode(string value)
|
||||||
|
{
|
||||||
|
unchecked
|
||||||
|
{
|
||||||
|
int hash1 = (5381 << 16) + 5381;
|
||||||
|
int hash2 = hash1;
|
||||||
|
|
||||||
|
for (int i = 0; i < value.Length; i += 2)
|
||||||
|
{
|
||||||
|
hash1 = ((hash1 << 5) + hash1) ^ value[i];
|
||||||
|
if (i == value.Length - 1)
|
||||||
|
break;
|
||||||
|
hash2 = ((hash2 << 5) + hash2) ^ value[i + 1];
|
||||||
|
}
|
||||||
|
|
||||||
|
return hash1 + (hash2 * 1566083941);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -42,10 +42,13 @@ namespace Oqtane.Controllers
|
|||||||
{
|
{
|
||||||
if (!string.IsNullOrEmpty(packagename))
|
if (!string.IsNullOrEmpty(packagename))
|
||||||
{
|
{
|
||||||
foreach (var file in Directory.EnumerateFiles(Path.GetDirectoryName(Assembly.GetEntryAssembly().Location), $"{packagename}.*{Constants.SatelliteAssemblyExtension}", SearchOption.AllDirectories))
|
foreach (var file in Directory.EnumerateFiles(Path.GetDirectoryName(Assembly.GetEntryAssembly().Location), $"{packagename}*{Constants.SatelliteAssemblyExtension}", SearchOption.AllDirectories))
|
||||||
{
|
{
|
||||||
var code = Path.GetFileName(Path.GetDirectoryName(file));
|
var code = Path.GetFileName(Path.GetDirectoryName(file));
|
||||||
languages.Add(new Language { Code = code, Name = CultureInfo.GetCultureInfo(code).DisplayName, Version = FileVersionInfo.GetVersionInfo(file).FileVersion, IsDefault = false });
|
if (!languages.Any(item => item.Code == code))
|
||||||
|
{
|
||||||
|
languages.Add(new Language { Code = code, Name = CultureInfo.GetCultureInfo(code).DisplayName, Version = FileVersionInfo.GetVersionInfo(file).ProductVersion, IsDefault = false });
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -54,12 +57,12 @@ namespace Oqtane.Controllers
|
|||||||
languages = _languages.GetLanguages(SiteId).ToList();
|
languages = _languages.GetLanguages(SiteId).ToList();
|
||||||
if (!string.IsNullOrEmpty(packagename))
|
if (!string.IsNullOrEmpty(packagename))
|
||||||
{
|
{
|
||||||
foreach (var file in Directory.EnumerateFiles(Path.GetDirectoryName(Assembly.GetEntryAssembly().Location), $"{packagename}.*{Constants.SatelliteAssemblyExtension}", SearchOption.AllDirectories))
|
foreach (var file in Directory.EnumerateFiles(Path.GetDirectoryName(Assembly.GetEntryAssembly().Location), $"{packagename}*{Constants.SatelliteAssemblyExtension}", SearchOption.AllDirectories))
|
||||||
{
|
{
|
||||||
var code = Path.GetFileName(Path.GetDirectoryName(file));
|
var code = Path.GetFileName(Path.GetDirectoryName(file));
|
||||||
if (languages.Any(item => item.Code == code))
|
if (languages.Any(item => item.Code == code))
|
||||||
{
|
{
|
||||||
languages.Single(item => item.Code == code).Version = FileVersionInfo.GetVersionInfo(file).FileVersion;
|
languages.Single(item => item.Code == code).Version = FileVersionInfo.GetVersionInfo(file).ProductVersion;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -99,7 +102,8 @@ namespace Oqtane.Controllers
|
|||||||
if (ModelState.IsValid && language.SiteId == _alias.SiteId)
|
if (ModelState.IsValid && language.SiteId == _alias.SiteId)
|
||||||
{
|
{
|
||||||
language = _languages.AddLanguage(language);
|
language = _languages.AddLanguage(language);
|
||||||
_syncManager.AddSyncEvent(_alias.TenantId, EntityNames.Site, _alias.SiteId);
|
_syncManager.AddSyncEvent(_alias.TenantId, EntityNames.Language, language.LanguageId, SyncEventActions.Create);
|
||||||
|
_syncManager.AddSyncEvent(_alias.TenantId, EntityNames.Site, _alias.SiteId, SyncEventActions.Refresh);
|
||||||
_logger.Log(LogLevel.Information, this, LogFunction.Create, "Language Added {Language}", language);
|
_logger.Log(LogLevel.Information, this, LogFunction.Create, "Language Added {Language}", language);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -119,7 +123,8 @@ namespace Oqtane.Controllers
|
|||||||
if (language != null && language.SiteId == _alias.SiteId)
|
if (language != null && language.SiteId == _alias.SiteId)
|
||||||
{
|
{
|
||||||
_languages.DeleteLanguage(id);
|
_languages.DeleteLanguage(id);
|
||||||
_syncManager.AddSyncEvent(_alias.TenantId, EntityNames.Site, _alias.SiteId);
|
_syncManager.AddSyncEvent(_alias.TenantId, EntityNames.Language, language.LanguageId, SyncEventActions.Delete);
|
||||||
|
_syncManager.AddSyncEvent(_alias.TenantId, EntityNames.Site, _alias.SiteId, SyncEventActions.Refresh);
|
||||||
_logger.Log(LogLevel.Information, this, LogFunction.Delete, "Language Deleted {LanguageId}", id);
|
_logger.Log(LogLevel.Information, this, LogFunction.Delete, "Language Deleted {LanguageId}", id);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -21,9 +21,18 @@ namespace Oqtane.Controllers
|
|||||||
|
|
||||||
// GET: api/localization
|
// GET: api/localization
|
||||||
[HttpGet()]
|
[HttpGet()]
|
||||||
public IEnumerable<Culture> Get()
|
public IEnumerable<Culture> Get(bool installed)
|
||||||
{
|
{
|
||||||
var cultures = _localizationManager.GetSupportedCultures().Select(c => new Culture
|
string[] culturecodes;
|
||||||
|
if (installed)
|
||||||
|
{
|
||||||
|
culturecodes = _localizationManager.GetInstalledCultures();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
culturecodes = _localizationManager.GetSupportedCultures();
|
||||||
|
}
|
||||||
|
var cultures = culturecodes.Select(c => new Culture
|
||||||
{
|
{
|
||||||
Name = CultureInfo.GetCultureInfo(c).Name,
|
Name = CultureInfo.GetCultureInfo(c).Name,
|
||||||
DisplayName = CultureInfo.GetCultureInfo(c).DisplayName,
|
DisplayName = CultureInfo.GetCultureInfo(c).DisplayName,
|
||||||
|
@ -47,7 +47,6 @@ namespace Oqtane.Controllers
|
|||||||
int SiteId;
|
int SiteId;
|
||||||
if (int.TryParse(siteid, out SiteId) && SiteId == _alias.SiteId)
|
if (int.TryParse(siteid, out SiteId) && SiteId == _alias.SiteId)
|
||||||
{
|
{
|
||||||
List<ModuleDefinition> moduledefinitions = _moduleDefinitions.GetModuleDefinitions(SiteId).ToList();
|
|
||||||
List<Setting> settings = _settings.GetSettings(EntityNames.Module).ToList();
|
List<Setting> settings = _settings.GetSettings(EntityNames.Module).ToList();
|
||||||
|
|
||||||
foreach (PageModule pagemodule in _pageModules.GetPageModules(SiteId))
|
foreach (PageModule pagemodule in _pageModules.GetPageModules(SiteId))
|
||||||
@ -75,7 +74,6 @@ namespace Oqtane.Controllers
|
|||||||
module.Order = pagemodule.Order;
|
module.Order = pagemodule.Order;
|
||||||
module.ContainerType = pagemodule.ContainerType;
|
module.ContainerType = pagemodule.ContainerType;
|
||||||
|
|
||||||
module.ModuleDefinition = moduledefinitions.Find(item => item.ModuleDefinitionName == module.ModuleDefinitionName);
|
|
||||||
module.Settings = settings.Where(item => item.EntityId == pagemodule.ModuleId)
|
module.Settings = settings.Where(item => item.EntityId == pagemodule.ModuleId)
|
||||||
.Where(item => !item.IsPrivate || _userPermissions.IsAuthorized(User, PermissionNames.Edit, pagemodule.Module.Permissions))
|
.Where(item => !item.IsPrivate || _userPermissions.IsAuthorized(User, PermissionNames.Edit, pagemodule.Module.Permissions))
|
||||||
.ToDictionary(setting => setting.SettingName, setting => setting.SettingValue);
|
.ToDictionary(setting => setting.SettingName, setting => setting.SettingValue);
|
||||||
@ -121,10 +119,11 @@ namespace Oqtane.Controllers
|
|||||||
[Authorize(Roles = RoleNames.Registered)]
|
[Authorize(Roles = RoleNames.Registered)]
|
||||||
public Module Post([FromBody] Module module)
|
public Module Post([FromBody] Module module)
|
||||||
{
|
{
|
||||||
if (ModelState.IsValid && module.SiteId == _alias.SiteId && _userPermissions.IsAuthorized(User, EntityNames.Page, module.PageId, PermissionNames.Edit))
|
if (ModelState.IsValid && module.SiteId == _alias.SiteId && _userPermissions.IsAuthorized(User, module.SiteId, EntityNames.Page, module.PageId, PermissionNames.Edit))
|
||||||
{
|
{
|
||||||
module = _modules.AddModule(module);
|
module = _modules.AddModule(module);
|
||||||
_syncManager.AddSyncEvent(_alias.TenantId, EntityNames.Site, _alias.SiteId);
|
_syncManager.AddSyncEvent(_alias.TenantId, EntityNames.Module, module.ModuleId, SyncEventActions.Create);
|
||||||
|
_syncManager.AddSyncEvent(_alias.TenantId, EntityNames.Site, _alias.SiteId, SyncEventActions.Refresh);
|
||||||
_logger.Log(LogLevel.Information, this, LogFunction.Create, "Module Added {Module}", module);
|
_logger.Log(LogLevel.Information, this, LogFunction.Create, "Module Added {Module}", module);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -143,7 +142,7 @@ namespace Oqtane.Controllers
|
|||||||
{
|
{
|
||||||
var _module = _modules.GetModule(module.ModuleId, false);
|
var _module = _modules.GetModule(module.ModuleId, false);
|
||||||
|
|
||||||
if (ModelState.IsValid && module.SiteId == _alias.SiteId && _module != null && _userPermissions.IsAuthorized(User, EntityNames.Module, module.ModuleId, PermissionNames.Edit))
|
if (ModelState.IsValid && module.SiteId == _alias.SiteId && _module != null && _userPermissions.IsAuthorized(User, module.SiteId, EntityNames.Module, module.ModuleId, PermissionNames.Edit))
|
||||||
{
|
{
|
||||||
module = _modules.UpdateModule(module);
|
module = _modules.UpdateModule(module);
|
||||||
|
|
||||||
@ -174,7 +173,8 @@ namespace Oqtane.Controllers
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_syncManager.AddSyncEvent(_alias.TenantId, EntityNames.Site, _alias.SiteId);
|
_syncManager.AddSyncEvent(_alias.TenantId, EntityNames.Module, module.ModuleId, SyncEventActions.Update);
|
||||||
|
_syncManager.AddSyncEvent(_alias.TenantId, EntityNames.Site, _alias.SiteId, SyncEventActions.Refresh);
|
||||||
_logger.Log(LogLevel.Information, this, LogFunction.Update, "Module Updated {Module}", module);
|
_logger.Log(LogLevel.Information, this, LogFunction.Update, "Module Updated {Module}", module);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -192,10 +192,11 @@ namespace Oqtane.Controllers
|
|||||||
public void Delete(int id)
|
public void Delete(int id)
|
||||||
{
|
{
|
||||||
var module = _modules.GetModule(id);
|
var module = _modules.GetModule(id);
|
||||||
if (module != null && module.SiteId == _alias.SiteId && _userPermissions.IsAuthorized(User, EntityNames.Module, module.ModuleId, PermissionNames.Edit))
|
if (module != null && module.SiteId == _alias.SiteId && _userPermissions.IsAuthorized(User, module.SiteId, EntityNames.Module, module.ModuleId, PermissionNames.Edit))
|
||||||
{
|
{
|
||||||
_modules.DeleteModule(id);
|
_modules.DeleteModule(id);
|
||||||
_syncManager.AddSyncEvent(_alias.TenantId, EntityNames.Site, _alias.SiteId);
|
_syncManager.AddSyncEvent(_alias.TenantId, EntityNames.Module, module.ModuleId, SyncEventActions.Delete);
|
||||||
|
_syncManager.AddSyncEvent(_alias.TenantId, EntityNames.Site, _alias.SiteId, SyncEventActions.Refresh);
|
||||||
_logger.Log(LogLevel.Information, this, LogFunction.Delete, "Module Deleted {ModuleId}", id);
|
_logger.Log(LogLevel.Information, this, LogFunction.Delete, "Module Deleted {ModuleId}", id);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -212,7 +213,7 @@ namespace Oqtane.Controllers
|
|||||||
{
|
{
|
||||||
string content = "";
|
string content = "";
|
||||||
var module = _modules.GetModule(moduleid);
|
var module = _modules.GetModule(moduleid);
|
||||||
if (module != null && module.SiteId == _alias.SiteId && _userPermissions.IsAuthorized(User, EntityNames.Page, pageid, PermissionNames.Edit))
|
if (module != null && module.SiteId == _alias.SiteId && _userPermissions.IsAuthorized(User, module.SiteId, EntityNames.Page, pageid, PermissionNames.Edit))
|
||||||
{
|
{
|
||||||
content = _modules.ExportModule(moduleid);
|
content = _modules.ExportModule(moduleid);
|
||||||
if (!string.IsNullOrEmpty(content))
|
if (!string.IsNullOrEmpty(content))
|
||||||
@ -239,7 +240,7 @@ namespace Oqtane.Controllers
|
|||||||
{
|
{
|
||||||
bool success = false;
|
bool success = false;
|
||||||
var module = _modules.GetModule(moduleid);
|
var module = _modules.GetModule(moduleid);
|
||||||
if (ModelState.IsValid && module != null && module.SiteId == _alias.SiteId && _userPermissions.IsAuthorized(User, EntityNames.Page, pageid, PermissionNames.Edit))
|
if (ModelState.IsValid && module != null && module.SiteId == _alias.SiteId && _userPermissions.IsAuthorized(User, module.SiteId, EntityNames.Page, pageid, PermissionNames.Edit))
|
||||||
{
|
{
|
||||||
success = _modules.ImportModule(moduleid, content);
|
success = _modules.ImportModule(moduleid, content);
|
||||||
if (success)
|
if (success)
|
||||||
|
@ -3,6 +3,7 @@ using Microsoft.AspNetCore.Http;
|
|||||||
using Oqtane.Infrastructure;
|
using Oqtane.Infrastructure;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System;
|
using System;
|
||||||
|
using Oqtane.Shared;
|
||||||
|
|
||||||
namespace Oqtane.Controllers
|
namespace Oqtane.Controllers
|
||||||
{
|
{
|
||||||
@ -48,5 +49,9 @@ namespace Oqtane.Controllers
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected bool IsAuthorizedEntityId(string entityname, int entityid)
|
||||||
|
{
|
||||||
|
return (entityid == AuthEntityId(entityname)) || User.IsInRole(RoleNames.Host);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -29,10 +29,11 @@ namespace Oqtane.Controllers
|
|||||||
private readonly IWebHostEnvironment _environment;
|
private readonly IWebHostEnvironment _environment;
|
||||||
private readonly IServiceProvider _serviceProvider;
|
private readonly IServiceProvider _serviceProvider;
|
||||||
private readonly ITenantManager _tenantManager;
|
private readonly ITenantManager _tenantManager;
|
||||||
|
private readonly ISyncManager _syncManager;
|
||||||
private readonly ILogManager _logger;
|
private readonly ILogManager _logger;
|
||||||
private readonly Alias _alias;
|
private readonly Alias _alias;
|
||||||
|
|
||||||
public ModuleDefinitionController(IModuleDefinitionRepository moduleDefinitions, ITenantRepository tenants, ISqlRepository sql, IUserPermissions userPermissions, IInstallationManager installationManager, IWebHostEnvironment environment, IServiceProvider serviceProvider, ITenantManager tenantManager, ILogManager logger)
|
public ModuleDefinitionController(IModuleDefinitionRepository moduleDefinitions, ITenantRepository tenants, ISqlRepository sql, IUserPermissions userPermissions, IInstallationManager installationManager, IWebHostEnvironment environment, IServiceProvider serviceProvider, ITenantManager tenantManager, ISyncManager syncManager, ILogManager logger)
|
||||||
{
|
{
|
||||||
_moduleDefinitions = moduleDefinitions;
|
_moduleDefinitions = moduleDefinitions;
|
||||||
_tenants = tenants;
|
_tenants = tenants;
|
||||||
@ -42,6 +43,7 @@ namespace Oqtane.Controllers
|
|||||||
_environment = environment;
|
_environment = environment;
|
||||||
_serviceProvider = serviceProvider;
|
_serviceProvider = serviceProvider;
|
||||||
_tenantManager = tenantManager;
|
_tenantManager = tenantManager;
|
||||||
|
_syncManager = syncManager;
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
_alias = tenantManager.GetAlias();
|
_alias = tenantManager.GetAlias();
|
||||||
}
|
}
|
||||||
@ -145,6 +147,7 @@ namespace Oqtane.Controllers
|
|||||||
if (ModelState.IsValid && moduleDefinition.SiteId == _alias.SiteId && _moduleDefinitions.GetModuleDefinition(moduleDefinition.ModuleDefinitionId, moduleDefinition.SiteId) != null)
|
if (ModelState.IsValid && moduleDefinition.SiteId == _alias.SiteId && _moduleDefinitions.GetModuleDefinition(moduleDefinition.ModuleDefinitionId, moduleDefinition.SiteId) != null)
|
||||||
{
|
{
|
||||||
_moduleDefinitions.UpdateModuleDefinition(moduleDefinition);
|
_moduleDefinitions.UpdateModuleDefinition(moduleDefinition);
|
||||||
|
_syncManager.AddSyncEvent(_alias.TenantId, EntityNames.ModuleDefinition, moduleDefinition.ModuleDefinitionId, SyncEventActions.Update);
|
||||||
_logger.Log(LogLevel.Information, this, LogFunction.Update, "Module Definition Updated {ModuleDefinition}", moduleDefinition);
|
_logger.Log(LogLevel.Information, this, LogFunction.Update, "Module Definition Updated {ModuleDefinition}", moduleDefinition);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -227,6 +230,7 @@ namespace Oqtane.Controllers
|
|||||||
|
|
||||||
// remove module definition
|
// remove module definition
|
||||||
_moduleDefinitions.DeleteModuleDefinition(id);
|
_moduleDefinitions.DeleteModuleDefinition(id);
|
||||||
|
_syncManager.AddSyncEvent(_alias.TenantId, EntityNames.ModuleDefinition, moduledefinition.ModuleDefinitionId, SyncEventActions.Delete);
|
||||||
_logger.Log(LogLevel.Information, this, LogFunction.Delete, "Module Definition {ModuleDefinitionName} Deleted", moduledefinition.Name);
|
_logger.Log(LogLevel.Information, this, LogFunction.Delete, "Module Definition {ModuleDefinitionName} Deleted", moduledefinition.Name);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -8,6 +8,7 @@ using Oqtane.Infrastructure;
|
|||||||
using Oqtane.Repository;
|
using Oqtane.Repository;
|
||||||
using Oqtane.Security;
|
using Oqtane.Security;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
|
using System.Reflection.Metadata;
|
||||||
|
|
||||||
namespace Oqtane.Controllers
|
namespace Oqtane.Controllers
|
||||||
{
|
{
|
||||||
@ -16,13 +17,15 @@ namespace Oqtane.Controllers
|
|||||||
{
|
{
|
||||||
private readonly INotificationRepository _notifications;
|
private readonly INotificationRepository _notifications;
|
||||||
private readonly IUserPermissions _userPermissions;
|
private readonly IUserPermissions _userPermissions;
|
||||||
|
private readonly ISyncManager _syncManager;
|
||||||
private readonly ILogManager _logger;
|
private readonly ILogManager _logger;
|
||||||
private readonly Alias _alias;
|
private readonly Alias _alias;
|
||||||
|
|
||||||
public NotificationController(INotificationRepository notifications, IUserPermissions userPermissions, ILogManager logger, ITenantManager tenantManager)
|
public NotificationController(INotificationRepository notifications, IUserPermissions userPermissions, ISyncManager syncManager, ILogManager logger, ITenantManager tenantManager)
|
||||||
{
|
{
|
||||||
_notifications = notifications;
|
_notifications = notifications;
|
||||||
_userPermissions = userPermissions;
|
_userPermissions = userPermissions;
|
||||||
|
_syncManager = syncManager;
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
_alias = tenantManager.GetAlias();
|
_alias = tenantManager.GetAlias();
|
||||||
}
|
}
|
||||||
@ -83,6 +86,7 @@ namespace Oqtane.Controllers
|
|||||||
if (ModelState.IsValid && notification.SiteId == _alias.SiteId && IsAuthorized(notification.FromUserId))
|
if (ModelState.IsValid && notification.SiteId == _alias.SiteId && IsAuthorized(notification.FromUserId))
|
||||||
{
|
{
|
||||||
notification = _notifications.AddNotification(notification);
|
notification = _notifications.AddNotification(notification);
|
||||||
|
_syncManager.AddSyncEvent(_alias.TenantId, EntityNames.Notification, notification.NotificationId, SyncEventActions.Create);
|
||||||
_logger.Log(LogLevel.Information, this, LogFunction.Create, "Notification Added {NotificationId}", notification.NotificationId);
|
_logger.Log(LogLevel.Information, this, LogFunction.Create, "Notification Added {NotificationId}", notification.NotificationId);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -102,6 +106,7 @@ namespace Oqtane.Controllers
|
|||||||
if (ModelState.IsValid && notification.SiteId == _alias.SiteId && _notifications.GetNotification(notification.NotificationId, false) != null && IsAuthorized(notification.FromUserId))
|
if (ModelState.IsValid && notification.SiteId == _alias.SiteId && _notifications.GetNotification(notification.NotificationId, false) != null && IsAuthorized(notification.FromUserId))
|
||||||
{
|
{
|
||||||
notification = _notifications.UpdateNotification(notification);
|
notification = _notifications.UpdateNotification(notification);
|
||||||
|
_syncManager.AddSyncEvent(_alias.TenantId, EntityNames.Notification, notification.NotificationId, SyncEventActions.Update);
|
||||||
_logger.Log(LogLevel.Information, this, LogFunction.Update, "Notification Updated {NotificationId}", notification.NotificationId);
|
_logger.Log(LogLevel.Information, this, LogFunction.Update, "Notification Updated {NotificationId}", notification.NotificationId);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -122,6 +127,7 @@ namespace Oqtane.Controllers
|
|||||||
if (notification != null && notification.SiteId == _alias.SiteId && (IsAuthorized(notification.FromUserId) || IsAuthorized(notification.ToUserId)))
|
if (notification != null && notification.SiteId == _alias.SiteId && (IsAuthorized(notification.FromUserId) || IsAuthorized(notification.ToUserId)))
|
||||||
{
|
{
|
||||||
_notifications.DeleteNotification(id);
|
_notifications.DeleteNotification(id);
|
||||||
|
_syncManager.AddSyncEvent(_alias.TenantId, EntityNames.Notification, notification.NotificationId, SyncEventActions.Delete);
|
||||||
_logger.Log(LogLevel.Information, this, LogFunction.Delete, "Notification Deleted {NotificationId}", id);
|
_logger.Log(LogLevel.Information, this, LogFunction.Delete, "Notification Deleted {NotificationId}", id);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Oqtane.Models;
|
using Oqtane.Models;
|
||||||
using Newtonsoft.Json;
|
|
||||||
using System;
|
using System;
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
@ -12,6 +11,7 @@ using Oqtane.Shared;
|
|||||||
using Oqtane.Infrastructure;
|
using Oqtane.Infrastructure;
|
||||||
using Oqtane.Enums;
|
using Oqtane.Enums;
|
||||||
using System.Net.Http.Headers;
|
using System.Net.Http.Headers;
|
||||||
|
using System.Text.Json;
|
||||||
// ReSharper disable PartialTypeWithSinglePart
|
// ReSharper disable PartialTypeWithSinglePart
|
||||||
|
|
||||||
namespace Oqtane.Controllers
|
namespace Oqtane.Controllers
|
||||||
@ -106,11 +106,7 @@ namespace Oqtane.Controllers
|
|||||||
var stream = await response.Content.ReadAsStreamAsync();
|
var stream = await response.Content.ReadAsStreamAsync();
|
||||||
using (var streamReader = new StreamReader(stream))
|
using (var streamReader = new StreamReader(stream))
|
||||||
{
|
{
|
||||||
using (var jsonTextReader = new JsonTextReader(streamReader))
|
return await JsonSerializer.DeserializeAsync<T>(stream, new JsonSerializerOptions(JsonSerializerDefaults.Web));
|
||||||
{
|
|
||||||
var serializer = new JsonSerializer();
|
|
||||||
return serializer.Deserialize<T>(jsonTextReader);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return default(T);
|
return default(T);
|
||||||
|
@ -143,7 +143,8 @@ namespace Oqtane.Controllers
|
|||||||
if (_userPermissions.IsAuthorized(User,PermissionNames.Edit, permissions))
|
if (_userPermissions.IsAuthorized(User,PermissionNames.Edit, permissions))
|
||||||
{
|
{
|
||||||
page = _pages.AddPage(page);
|
page = _pages.AddPage(page);
|
||||||
_syncManager.AddSyncEvent(_alias.TenantId, EntityNames.Site, page.SiteId);
|
_syncManager.AddSyncEvent(_alias.TenantId, EntityNames.Page, page.PageId, SyncEventActions.Create);
|
||||||
|
_syncManager.AddSyncEvent(_alias.TenantId, EntityNames.Site, page.SiteId, SyncEventActions.Refresh);
|
||||||
_logger.Log(LogLevel.Information, this, LogFunction.Create, "Page Added {Page}", page);
|
_logger.Log(LogLevel.Information, this, LogFunction.Create, "Page Added {Page}", page);
|
||||||
|
|
||||||
if (!page.Path.StartsWith("admin/"))
|
if (!page.Path.StartsWith("admin/"))
|
||||||
@ -200,7 +201,8 @@ namespace Oqtane.Controllers
|
|||||||
page.IsPersonalizable = false;
|
page.IsPersonalizable = false;
|
||||||
page.UserId = int.Parse(userid);
|
page.UserId = int.Parse(userid);
|
||||||
page = _pages.AddPage(page);
|
page = _pages.AddPage(page);
|
||||||
_syncManager.AddSyncEvent(_alias.TenantId, EntityNames.Site, page.SiteId);
|
_syncManager.AddSyncEvent(_alias.TenantId, EntityNames.Page, page.PageId, SyncEventActions.Create);
|
||||||
|
_syncManager.AddSyncEvent(_alias.TenantId, EntityNames.Site, page.SiteId, SyncEventActions.Refresh);
|
||||||
|
|
||||||
// copy modules
|
// copy modules
|
||||||
List<PageModule> pagemodules = _pageModules.GetPageModules(page.SiteId).ToList();
|
List<PageModule> pagemodules = _pageModules.GetPageModules(page.SiteId).ToList();
|
||||||
@ -251,10 +253,10 @@ namespace Oqtane.Controllers
|
|||||||
// get current page
|
// get current page
|
||||||
var currentPage = _pages.GetPage(page.PageId, false);
|
var currentPage = _pages.GetPage(page.PageId, false);
|
||||||
|
|
||||||
if (ModelState.IsValid && page.SiteId == _alias.SiteId && currentPage != null && _userPermissions.IsAuthorized(User, EntityNames.Page, page.PageId, PermissionNames.Edit))
|
if (ModelState.IsValid && page.SiteId == _alias.SiteId && currentPage != null && _userPermissions.IsAuthorized(User, page.SiteId, EntityNames.Page, page.PageId, PermissionNames.Edit))
|
||||||
{
|
{
|
||||||
// get current page permissions
|
// get current page permissions
|
||||||
var currentPermissions = _permissionRepository.GetPermissions(EntityNames.Page, page.PageId).ToList();
|
var currentPermissions = _permissionRepository.GetPermissions(page.SiteId, EntityNames.Page, page.PageId).ToList();
|
||||||
|
|
||||||
page = _pages.UpdatePage(page);
|
page = _pages.UpdatePage(page);
|
||||||
|
|
||||||
@ -279,9 +281,9 @@ namespace Oqtane.Controllers
|
|||||||
// synchronize module permissions
|
// synchronize module permissions
|
||||||
if (added.Count > 0 || removed.Count > 0)
|
if (added.Count > 0 || removed.Count > 0)
|
||||||
{
|
{
|
||||||
foreach (PageModule pageModule in _pageModules.GetPageModules(page.PageId, "").ToList())
|
foreach (PageModule pageModule in _pageModules.GetPageModules(page.SiteId).Where(item => item.PageId == page.PageId).ToList())
|
||||||
{
|
{
|
||||||
var modulePermissions = _permissionRepository.GetPermissions(EntityNames.Module, pageModule.Module.ModuleId).ToList();
|
var modulePermissions = _permissionRepository.GetPermissions(pageModule.Module.SiteId, EntityNames.Module, pageModule.Module.ModuleId).ToList();
|
||||||
// permissions added
|
// permissions added
|
||||||
foreach(Permission permission in added)
|
foreach(Permission permission in added)
|
||||||
{
|
{
|
||||||
@ -313,7 +315,8 @@ namespace Oqtane.Controllers
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_syncManager.AddSyncEvent(_alias.TenantId, EntityNames.Site, page.SiteId);
|
_syncManager.AddSyncEvent(_alias.TenantId, EntityNames.Page, page.PageId, SyncEventActions.Update);
|
||||||
|
_syncManager.AddSyncEvent(_alias.TenantId, EntityNames.Site, page.SiteId, SyncEventActions.Refresh);
|
||||||
_logger.Log(LogLevel.Information, this, LogFunction.Update, "Page Updated {Page}", page);
|
_logger.Log(LogLevel.Information, this, LogFunction.Update, "Page Updated {Page}", page);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -343,7 +346,7 @@ namespace Oqtane.Controllers
|
|||||||
[Authorize(Roles = RoleNames.Registered)]
|
[Authorize(Roles = RoleNames.Registered)]
|
||||||
public void Put(int siteid, int pageid, int? parentid)
|
public void Put(int siteid, int pageid, int? parentid)
|
||||||
{
|
{
|
||||||
if (siteid == _alias.SiteId && siteid == _alias.SiteId && _pages.GetPage(pageid, false) != null && _userPermissions.IsAuthorized(User, EntityNames.Page, pageid, PermissionNames.Edit))
|
if (siteid == _alias.SiteId && siteid == _alias.SiteId && _pages.GetPage(pageid, false) != null && _userPermissions.IsAuthorized(User, siteid, EntityNames.Page, pageid, PermissionNames.Edit))
|
||||||
{
|
{
|
||||||
int order = 1;
|
int order = 1;
|
||||||
List<Page> pages = _pages.GetPages(siteid).ToList();
|
List<Page> pages = _pages.GetPages(siteid).ToList();
|
||||||
@ -353,10 +356,12 @@ namespace Oqtane.Controllers
|
|||||||
{
|
{
|
||||||
page.Order = order;
|
page.Order = order;
|
||||||
_pages.UpdatePage(page);
|
_pages.UpdatePage(page);
|
||||||
|
_syncManager.AddSyncEvent(_alias.TenantId, EntityNames.Page, page.PageId, SyncEventActions.Update);
|
||||||
}
|
}
|
||||||
order += 2;
|
order += 2;
|
||||||
}
|
}
|
||||||
_syncManager.AddSyncEvent(_alias.TenantId, EntityNames.Site, siteid);
|
|
||||||
|
_syncManager.AddSyncEvent(_alias.TenantId, EntityNames.Site, siteid, SyncEventActions.Refresh);
|
||||||
_logger.Log(LogLevel.Information, this, LogFunction.Update, "Page Order Updated {SiteId} {PageId} {ParentId}", siteid, pageid, parentid);
|
_logger.Log(LogLevel.Information, this, LogFunction.Update, "Page Order Updated {SiteId} {PageId} {ParentId}", siteid, pageid, parentid);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -372,10 +377,11 @@ namespace Oqtane.Controllers
|
|||||||
public void Delete(int id)
|
public void Delete(int id)
|
||||||
{
|
{
|
||||||
Page page = _pages.GetPage(id);
|
Page page = _pages.GetPage(id);
|
||||||
if (page != null && page.SiteId == _alias.SiteId && _userPermissions.IsAuthorized(User, EntityNames.Page, page.PageId, PermissionNames.Edit))
|
if (page != null && page.SiteId == _alias.SiteId && _userPermissions.IsAuthorized(User, page.SiteId, EntityNames.Page, page.PageId, PermissionNames.Edit))
|
||||||
{
|
{
|
||||||
_pages.DeletePage(page.PageId);
|
_pages.DeletePage(page.PageId);
|
||||||
_syncManager.AddSyncEvent(_alias.TenantId, EntityNames.Site, page.SiteId);
|
_syncManager.AddSyncEvent(_alias.TenantId, EntityNames.Page, page.PageId, SyncEventActions.Delete);
|
||||||
|
_syncManager.AddSyncEvent(_alias.TenantId, EntityNames.Site, page.SiteId, SyncEventActions.Refresh);
|
||||||
_logger.Log(LogLevel.Information, this, LogFunction.Delete, "Page Deleted {PageId}", page.PageId);
|
_logger.Log(LogLevel.Information, this, LogFunction.Delete, "Page Deleted {PageId}", page.PageId);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -9,6 +9,7 @@ using Oqtane.Infrastructure;
|
|||||||
using Oqtane.Repository;
|
using Oqtane.Repository;
|
||||||
using Oqtane.Security;
|
using Oqtane.Security;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
|
using Microsoft.AspNetCore.Mvc.RazorPages;
|
||||||
|
|
||||||
namespace Oqtane.Controllers
|
namespace Oqtane.Controllers
|
||||||
{
|
{
|
||||||
@ -72,10 +73,11 @@ namespace Oqtane.Controllers
|
|||||||
public PageModule Post([FromBody] PageModule pageModule)
|
public PageModule Post([FromBody] PageModule pageModule)
|
||||||
{
|
{
|
||||||
var page = _pages.GetPage(pageModule.PageId);
|
var page = _pages.GetPage(pageModule.PageId);
|
||||||
if (ModelState.IsValid && page != null && page.SiteId == _alias.SiteId && _userPermissions.IsAuthorized(User, EntityNames.Page, pageModule.PageId, PermissionNames.Edit))
|
if (ModelState.IsValid && page != null && page.SiteId == _alias.SiteId && _userPermissions.IsAuthorized(User, page.SiteId, EntityNames.Page, pageModule.PageId, PermissionNames.Edit))
|
||||||
{
|
{
|
||||||
pageModule = _pageModules.AddPageModule(pageModule);
|
pageModule = _pageModules.AddPageModule(pageModule);
|
||||||
_syncManager.AddSyncEvent(_alias.TenantId, EntityNames.Site, _alias.SiteId);
|
_syncManager.AddSyncEvent(_alias.TenantId, EntityNames.PageModule, pageModule.PageModuleId, SyncEventActions.Create);
|
||||||
|
_syncManager.AddSyncEvent(_alias.TenantId, EntityNames.Site, _alias.SiteId, SyncEventActions.Refresh);
|
||||||
_logger.Log(LogLevel.Information, this, LogFunction.Create, "Page Module Added {PageModule}", pageModule);
|
_logger.Log(LogLevel.Information, this, LogFunction.Create, "Page Module Added {PageModule}", pageModule);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -93,10 +95,11 @@ namespace Oqtane.Controllers
|
|||||||
public PageModule Put(int id, [FromBody] PageModule pageModule)
|
public PageModule Put(int id, [FromBody] PageModule pageModule)
|
||||||
{
|
{
|
||||||
var page = _pages.GetPage(pageModule.PageId);
|
var page = _pages.GetPage(pageModule.PageId);
|
||||||
if (ModelState.IsValid && page != null && page.SiteId == _alias.SiteId && _pageModules.GetPageModule(pageModule.PageModuleId, false) != null && _userPermissions.IsAuthorized(User, EntityNames.Page, pageModule.PageId, PermissionNames.Edit))
|
if (ModelState.IsValid && page != null && page.SiteId == _alias.SiteId && _pageModules.GetPageModule(pageModule.PageModuleId, false) != null && _userPermissions.IsAuthorized(User, page.SiteId, EntityNames.Page, pageModule.PageId, PermissionNames.Edit))
|
||||||
{
|
{
|
||||||
pageModule = _pageModules.UpdatePageModule(pageModule);
|
pageModule = _pageModules.UpdatePageModule(pageModule);
|
||||||
_syncManager.AddSyncEvent(_alias.TenantId, EntityNames.Site, _alias.SiteId);
|
_syncManager.AddSyncEvent(_alias.TenantId, EntityNames.PageModule, pageModule.PageModuleId, SyncEventActions.Update);
|
||||||
|
_syncManager.AddSyncEvent(_alias.TenantId, EntityNames.Site, _alias.SiteId, SyncEventActions.Refresh);
|
||||||
_logger.Log(LogLevel.Information, this, LogFunction.Update, "Page Module Updated {PageModule}", pageModule);
|
_logger.Log(LogLevel.Information, this, LogFunction.Update, "Page Module Updated {PageModule}", pageModule);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -114,20 +117,22 @@ namespace Oqtane.Controllers
|
|||||||
public void Put(int pageid, string pane)
|
public void Put(int pageid, string pane)
|
||||||
{
|
{
|
||||||
var page = _pages.GetPage(pageid);
|
var page = _pages.GetPage(pageid);
|
||||||
if (page != null && page.SiteId == _alias.SiteId && _userPermissions.IsAuthorized(User, EntityNames.Page, pageid, PermissionNames.Edit))
|
if (page != null && page.SiteId == _alias.SiteId && _userPermissions.IsAuthorized(User, page.SiteId, EntityNames.Page, pageid, PermissionNames.Edit))
|
||||||
{
|
{
|
||||||
int order = 1;
|
int order = 1;
|
||||||
List<PageModule> pagemodules = _pageModules.GetPageModules(pageid, pane).OrderBy(item => item.Order).ToList();
|
List<PageModule> pagemodules = _pageModules.GetPageModules(page.SiteId)
|
||||||
|
.Where(item => item.PageId == pageid && item.Pane == pane).OrderBy(item => item.Order).ToList();
|
||||||
foreach (PageModule pagemodule in pagemodules)
|
foreach (PageModule pagemodule in pagemodules)
|
||||||
{
|
{
|
||||||
if (pagemodule.Order != order)
|
if (pagemodule.Order != order)
|
||||||
{
|
{
|
||||||
pagemodule.Order = order;
|
pagemodule.Order = order;
|
||||||
_pageModules.UpdatePageModule(pagemodule);
|
_pageModules.UpdatePageModule(pagemodule);
|
||||||
|
_syncManager.AddSyncEvent(_alias.TenantId, EntityNames.PageModule, pagemodule.PageModuleId, SyncEventActions.Update);
|
||||||
}
|
}
|
||||||
order += 2;
|
order += 2;
|
||||||
}
|
}
|
||||||
_syncManager.AddSyncEvent(_alias.TenantId, EntityNames.Site, _alias.SiteId);
|
_syncManager.AddSyncEvent(_alias.TenantId, EntityNames.Site, _alias.SiteId, SyncEventActions.Refresh);
|
||||||
_logger.Log(LogLevel.Information, this, LogFunction.Update, "Page Module Order Updated {PageId} {Pane}", pageid, pane);
|
_logger.Log(LogLevel.Information, this, LogFunction.Update, "Page Module Order Updated {PageId} {Pane}", pageid, pane);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -143,10 +148,11 @@ namespace Oqtane.Controllers
|
|||||||
public void Delete(int id)
|
public void Delete(int id)
|
||||||
{
|
{
|
||||||
PageModule pagemodule = _pageModules.GetPageModule(id);
|
PageModule pagemodule = _pageModules.GetPageModule(id);
|
||||||
if (pagemodule != null && pagemodule.Module.SiteId == _alias.SiteId && _userPermissions.IsAuthorized(User, EntityNames.Page, pagemodule.PageId, PermissionNames.Edit))
|
if (pagemodule != null && pagemodule.Module.SiteId == _alias.SiteId && _userPermissions.IsAuthorized(User, pagemodule.Module.SiteId, EntityNames.Page, pagemodule.PageId, PermissionNames.Edit))
|
||||||
{
|
{
|
||||||
_pageModules.DeletePageModule(id);
|
_pageModules.DeletePageModule(id);
|
||||||
_syncManager.AddSyncEvent(_alias.TenantId, EntityNames.Site, _alias.SiteId);
|
_syncManager.AddSyncEvent(_alias.TenantId, EntityNames.PageModule, pagemodule.PageModuleId, SyncEventActions.Delete);
|
||||||
|
_syncManager.AddSyncEvent(_alias.TenantId, EntityNames.Site, _alias.SiteId, SyncEventActions.Refresh);
|
||||||
_logger.Log(LogLevel.Information, this, LogFunction.Delete, "Page Module Deleted {PageModuleId}", id);
|
_logger.Log(LogLevel.Information, this, LogFunction.Delete, "Page Module Deleted {PageModuleId}", id);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user