2
.gitattributes
vendored
Normal file
2
.gitattributes
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
Oqtane.Server/wwwroot/Modules/Templates/External/Package/*.sh eol=lf
|
||||
Oqtane.Server/wwwroot/Themes/Templates/External/Package/*.sh eol=lf
|
@ -22,7 +22,7 @@ else
|
||||
<th>@Localizer["Default"]</th>
|
||||
@if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host))
|
||||
{
|
||||
<th style="width: 1px;">@Localizer["Translation"]</th>
|
||||
<th style="width: 1px;">@Localizer["Translation"]</th>
|
||||
<th style="width: 1px;"> </th>
|
||||
}
|
||||
</Header>
|
||||
@ -33,7 +33,7 @@ else
|
||||
<td><TriStateCheckBox Value="@(context.IsDefault)" Disabled="true"></TriStateCheckBox></td>
|
||||
@if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host))
|
||||
{
|
||||
<td>@((string.IsNullOrEmpty(context.Version)) ? "---" : context.Version)</td>
|
||||
<td>@((string.IsNullOrEmpty(context.Version)) ? "---" : context.Version)</td>
|
||||
<td>
|
||||
@{
|
||||
var translation = TranslationAvailable(context.Code, context.Version);
|
||||
@ -99,56 +99,64 @@ else
|
||||
}
|
||||
|
||||
@code {
|
||||
private List<Language> _languages;
|
||||
private List<Package> _packages;
|
||||
private Package _package;
|
||||
private List<Language> _languages;
|
||||
private List<Package> _packages;
|
||||
private Package _package;
|
||||
|
||||
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Admin;
|
||||
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Admin;
|
||||
|
||||
protected override async Task OnParametersSetAsync()
|
||||
{
|
||||
await GetLanguages();
|
||||
protected override async Task OnParametersSetAsync()
|
||||
{
|
||||
await GetLanguages();
|
||||
|
||||
if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host))
|
||||
{
|
||||
_packages = await PackageService.GetPackagesAsync("translation");
|
||||
}
|
||||
}
|
||||
if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host))
|
||||
{
|
||||
_packages = await PackageService.GetPackagesAsync("translation");
|
||||
}
|
||||
}
|
||||
|
||||
private async Task GetLanguages()
|
||||
{
|
||||
_languages = await LanguageService.GetLanguagesAsync(PageState.Site.SiteId, Constants.ClientId);
|
||||
}
|
||||
private async Task GetLanguages()
|
||||
{
|
||||
_languages = await LanguageService.GetLanguagesAsync(PageState.Site.SiteId, Constants.ClientId);
|
||||
}
|
||||
|
||||
private async Task DeleteLanguage(Language language)
|
||||
{
|
||||
try
|
||||
{
|
||||
await LanguageService.DeleteLanguageAsync(language.LanguageId);
|
||||
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);
|
||||
private async Task DeleteLanguage(Language language)
|
||||
{
|
||||
try
|
||||
{
|
||||
await LanguageService.DeleteLanguageAsync(language.LanguageId);
|
||||
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);
|
||||
|
||||
AddModuleMessage(Localizer["Error.Language.Delete"], MessageType.Error);
|
||||
}
|
||||
}
|
||||
AddModuleMessage(Localizer["Error.Language.Delete"], MessageType.Error);
|
||||
}
|
||||
}
|
||||
|
||||
private Package TranslationAvailable(string code, string version)
|
||||
{
|
||||
return _packages?.FirstOrDefault(item => item.PackageId == (Constants.PackageId + "." + code));
|
||||
}
|
||||
private Package TranslationAvailable(string code, string version)
|
||||
{
|
||||
return _packages?.FirstOrDefault(item => item.PackageId == (Constants.PackageId + "." + code));
|
||||
}
|
||||
|
||||
private async Task GetPackage(string code, string version)
|
||||
{
|
||||
try
|
||||
{
|
||||
_package = await PackageService.GetPackageAsync(Constants.PackageId + "." + code, version);
|
||||
StateHasChanged();
|
||||
}
|
||||
private async Task GetPackage(string code, string version)
|
||||
{
|
||||
try
|
||||
{
|
||||
_package = await PackageService.GetPackageAsync(Constants.PackageId + "." + code, version);
|
||||
if (_package != null)
|
||||
{
|
||||
StateHasChanged();
|
||||
}
|
||||
else
|
||||
{
|
||||
await logger.LogError("Error Getting Package {PackageId} {Version}", Constants.PackageId + "." + code, Constants.Version);
|
||||
AddModuleMessage(Localizer["Error.Translation.Download"], MessageType.Error);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
await logger.LogError(ex, "Error Getting Package {PackageId} {Version}", Constants.PackageId + "." + code, Constants.Version);
|
||||
|
@ -37,7 +37,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="category" HelpText="The categories that were affected" ResourceKey="Category">Category: </Label>
|
||||
<Label Class="col-sm-3" For="category" HelpText="The fully qualified type type that was affected" ResourceKey="Category">Type Name: </Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="category" class="form-control" @bind="@_category" readonly />
|
||||
</div>
|
||||
|
@ -217,8 +217,13 @@
|
||||
_packagelicense = package.License.Replace("\n", "<br />");
|
||||
}
|
||||
_packageversion = package.Version;
|
||||
StateHasChanged();
|
||||
}
|
||||
else
|
||||
{
|
||||
await logger.LogError("Error Getting Package {PackageId} {Version}", packageid, version);
|
||||
AddModuleMessage(Localizer["Error.Module.Download"], MessageType.Error);
|
||||
}
|
||||
StateHasChanged();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
@ -319,55 +319,63 @@
|
||||
AddModuleMessage(Localizer["Message.DuplicateName"], MessageType.Warning);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
await logger.LogError(ex, "Error Saving ModuleDefinition {ModuleDefinitionId} {Error}", _moduleDefinitionId, ex.Message);
|
||||
AddModuleMessage(Localizer["Error.Module.Save"], MessageType.Error);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
AddModuleMessage(SharedLocalizer["Message.InfoRequired"], MessageType.Warning);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
await logger.LogError(ex, "Error Saving ModuleDefinition {ModuleDefinitionId} {Error}", _moduleDefinitionId, ex.Message);
|
||||
AddModuleMessage(Localizer["Error.Module.Save"], MessageType.Error);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
AddModuleMessage(SharedLocalizer["Message.InfoRequired"], MessageType.Warning);
|
||||
}
|
||||
}
|
||||
|
||||
private void HideModal()
|
||||
{
|
||||
_package = null;
|
||||
StateHasChanged();
|
||||
}
|
||||
private void HideModal()
|
||||
{
|
||||
_package = null;
|
||||
StateHasChanged();
|
||||
}
|
||||
|
||||
private string TranslationAvailable(string packagename, string version)
|
||||
{
|
||||
if (_packages != null)
|
||||
{
|
||||
var package = _packages.Where(item => item.PackageId == packagename).FirstOrDefault();
|
||||
if (package != null)
|
||||
{
|
||||
if (string.IsNullOrEmpty(version))
|
||||
{
|
||||
return "install";
|
||||
}
|
||||
else
|
||||
{
|
||||
if (Version.Parse(package.Version).CompareTo(Version.Parse(version)) > 0)
|
||||
{
|
||||
return "upgrade";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return "";
|
||||
}
|
||||
private string TranslationAvailable(string packagename, string version)
|
||||
{
|
||||
if (_packages != null)
|
||||
{
|
||||
var package = _packages.Where(item => item.PackageId == packagename).FirstOrDefault();
|
||||
if (package != null)
|
||||
{
|
||||
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 packagename)
|
||||
{
|
||||
var version = _packages.Where(item => item.PackageId == packagename).FirstOrDefault().Version;
|
||||
try
|
||||
{
|
||||
_package = await PackageService.GetPackageAsync(packagename, version);
|
||||
StateHasChanged();
|
||||
}
|
||||
private async Task GetPackage(string packagename)
|
||||
{
|
||||
var version = _packages.Where(item => item.PackageId == packagename).FirstOrDefault().Version;
|
||||
try
|
||||
{
|
||||
_package = await PackageService.GetPackageAsync(packagename, version);
|
||||
if (_package != null)
|
||||
{
|
||||
StateHasChanged();
|
||||
}
|
||||
else
|
||||
{
|
||||
await logger.LogError("Error Getting Package {PackageId} {Version}", packagename, version);
|
||||
AddModuleMessage(Localizer["Error.Translation.Download"], MessageType.Error);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
await logger.LogError(ex, "Error Getting Package {PackageId} {Version}", packagename, version);
|
||||
|
@ -45,6 +45,7 @@ else
|
||||
<th>@SharedLocalizer["Version"]</th>
|
||||
<th>@Localizer["Enabled"]</th>
|
||||
<th>@Localizer["InUse"]</th>
|
||||
<th>@SharedLocalizer["Support"]</th>
|
||||
<th>@SharedLocalizer["Expires"]</th>
|
||||
<th style="width: 1px;"> </th>
|
||||
</Header>
|
||||
@ -78,6 +79,9 @@ else
|
||||
<span>@SharedLocalizer["No"]</span>
|
||||
}
|
||||
</td>
|
||||
<td>
|
||||
@((MarkupString)SupportLink(context.PackageName))
|
||||
</td>
|
||||
<td>
|
||||
@((MarkupString)PurchaseLink(context.PackageName))
|
||||
</td>
|
||||
@ -130,7 +134,7 @@ else
|
||||
{
|
||||
if (package.ExpiryDate != null && package.ExpiryDate.Value.Date != DateTime.MaxValue.Date)
|
||||
{
|
||||
link += "<span>" + package.ExpiryDate.Value.Date.ToString("MMM dd, yyyy") + "</span>";
|
||||
link += "<span>" + package.ExpiryDate.Value.Date.ToString("MMM dd, yyyy") + "</span><br />";
|
||||
if (!string.IsNullOrEmpty(package.PaymentUrl))
|
||||
{
|
||||
link += " <a class=\"btn btn-primary\" style=\"text-decoration: none !important\" href=\"" + package.PaymentUrl + "\" target=\"_new\">" + SharedLocalizer["Extend"] + "</a>";
|
||||
@ -141,6 +145,20 @@ else
|
||||
return link;
|
||||
}
|
||||
|
||||
private string SupportLink(string packagename)
|
||||
{
|
||||
string link = "";
|
||||
if (!string.IsNullOrEmpty(packagename) && _packages != null)
|
||||
{
|
||||
var package = _packages.Where(item => item.PackageId == packagename).FirstOrDefault();
|
||||
if (package != null && !string.IsNullOrEmpty(package.SupportUrl))
|
||||
{
|
||||
link += "<a class=\"btn btn-success\" style=\"text-decoration: none !important\" href=\"" + package.SupportUrl + "\" target=\"_new\">" + SharedLocalizer["Help"] + "</a>";
|
||||
}
|
||||
}
|
||||
return link;
|
||||
}
|
||||
|
||||
private string UpgradeAvailable(string packagename, string version)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(packagename) && _packages != null)
|
||||
|
@ -14,10 +14,16 @@
|
||||
@if (_containers != null)
|
||||
{
|
||||
<div class="container">
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="module" HelpText="The name of the module" ResourceKey="Module">Module: </Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="module" type="text" class="form-control" @bind="@_module" disabled />
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="title" HelpText="Enter the title of the module" ResourceKey="Title">Title: </Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="title" type="text" name="Title" class="form-control" @bind="@_title" required />
|
||||
<input id="title" type="text" class="form-control" @bind="@_title" required />
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
@ -104,6 +110,7 @@
|
||||
private ElementReference form;
|
||||
private bool validated = false;
|
||||
private List<ThemeControl> _containers = new List<ThemeControl>();
|
||||
private string _module;
|
||||
private string _title;
|
||||
private string _containerType;
|
||||
private string _allPages = "false";
|
||||
@ -125,6 +132,7 @@
|
||||
|
||||
protected override void OnInitialized()
|
||||
{
|
||||
_module = ModuleState.ModuleDefinition.Name;
|
||||
_title = ModuleState.Title;
|
||||
_containers = ThemeService.GetContainerControls(PageState.Site.Themes, PageState.Page.ThemeType);
|
||||
_containerType = ModuleState.ContainerType;
|
||||
|
@ -137,7 +137,7 @@
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="theme" HelpText="Select the theme for this page" ResourceKey="Theme">Theme: </Label>
|
||||
<div class="col-sm-9">
|
||||
<select id="theme" class="form-select" @bind="@_themetype" required>
|
||||
<select id="theme" class="form-select" value="@_themetype" @onchange="(e => ThemeChanged(e))" required>
|
||||
@foreach (var theme in _themes)
|
||||
{
|
||||
<option value="@theme.TypeName">@theme.Name</option>
|
||||
@ -246,7 +246,7 @@
|
||||
if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Admin) || (_parent != null && UserSecurity.IsAuthorized(PageState.User, PermissionNames.Edit, _parent.PermissionList)))
|
||||
{
|
||||
_themetype = PageState.Site.DefaultThemeType;
|
||||
_themes = ThemeService.GetThemeControls(PageState.Site.Themes, _themetype);
|
||||
_themes = ThemeService.GetThemeControls(PageState.Site.Themes);
|
||||
_containers = ThemeService.GetContainerControls(PageState.Site.Themes, _themetype);
|
||||
_containertype = PageState.Site.DefaultContainerType;
|
||||
_children = PageState.Pages.Where(item => item.ParentId == null).ToList();
|
||||
@ -301,6 +301,19 @@
|
||||
}
|
||||
}
|
||||
|
||||
private void ThemeChanged(ChangeEventArgs e)
|
||||
{
|
||||
_themetype = (string)e.Value;
|
||||
if (ThemeService.GetTheme(PageState.Site.Themes, _themetype)?.ThemeName != ThemeService.GetTheme(PageState.Site.Themes, PageState.Site.DefaultThemeType)?.ThemeName)
|
||||
{
|
||||
AddModuleMessage(Localizer["ThemeChanged.Message"], MessageType.Warning);
|
||||
_containers = ThemeService.GetContainerControls(PageState.Site.Themes, _themetype);
|
||||
_containertype = _containers.First().TypeName;
|
||||
ThemeSettings();
|
||||
StateHasChanged();
|
||||
}
|
||||
}
|
||||
|
||||
private void ThemeSettings()
|
||||
{
|
||||
_themeSettingsType = null;
|
||||
|
@ -148,7 +148,7 @@
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="theme" HelpText="Select the theme for this page" ResourceKey="Theme">Theme: </Label>
|
||||
<div class="col-sm-9">
|
||||
<select id="theme" class="form-select" @bind="@_themetype" required>
|
||||
<select id="theme" class="form-select" value="@_themetype" @onchange="(e => ThemeChanged(e))" required>
|
||||
@foreach (var theme in _themes)
|
||||
{
|
||||
<option value="@theme.TypeName">@theme.Name</option>
|
||||
@ -359,7 +359,7 @@
|
||||
{
|
||||
_themetype = PageState.Site.DefaultThemeType;
|
||||
}
|
||||
_themes = ThemeService.GetThemeControls(PageState.Site.Themes, _themetype);
|
||||
_themes = ThemeService.GetThemeControls(PageState.Site.Themes);
|
||||
_containers = ThemeService.GetContainerControls(PageState.Site.Themes, _themetype);
|
||||
_containertype = _page.DefaultContainerType;
|
||||
if (string.IsNullOrEmpty(_containertype))
|
||||
@ -401,25 +401,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
private async Task DeleteModule(Module module)
|
||||
{
|
||||
try
|
||||
{
|
||||
PageModule pagemodule = await PageModuleService.GetPageModuleAsync(_page.PageId, module.ModuleId);
|
||||
pagemodule.IsDeleted = true;
|
||||
await PageModuleService.UpdatePageModuleAsync(pagemodule);
|
||||
await logger.LogInformation(LogFunction.Update,"Module Deleted {Title}", module.Title);
|
||||
_pageModules.RemoveAll(item => item.PageModuleId == pagemodule.PageModuleId);
|
||||
StateHasChanged();
|
||||
NavigationManager.NavigateTo(NavigationManager.Uri + "&tab=PageModules");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
await logger.LogError(ex, "Error Deleting Module {Title} {Error}", module.Title, ex.Message);
|
||||
AddModuleMessage(Localizer["Error.Module.Delete"], MessageType.Error);
|
||||
}
|
||||
}
|
||||
|
||||
private async void ParentChanged(ChangeEventArgs e)
|
||||
{
|
||||
try
|
||||
@ -463,6 +444,19 @@
|
||||
}
|
||||
}
|
||||
|
||||
private void ThemeChanged(ChangeEventArgs e)
|
||||
{
|
||||
_themetype = (string)e.Value;
|
||||
if (ThemeService.GetTheme(PageState.Site.Themes, _themetype)?.ThemeName != ThemeService.GetTheme(PageState.Site.Themes, PageState.Site.DefaultThemeType)?.ThemeName)
|
||||
{
|
||||
AddModuleMessage(Localizer["ThemeChanged.Message"], MessageType.Warning);
|
||||
_containers = ThemeService.GetContainerControls(PageState.Site.Themes, _themetype);
|
||||
_containertype = _containers.First().TypeName;
|
||||
ThemeSettings();
|
||||
StateHasChanged();
|
||||
}
|
||||
}
|
||||
|
||||
private void ThemeSettings()
|
||||
{
|
||||
_themeSettingsType = null;
|
||||
@ -603,16 +597,6 @@
|
||||
await PageService.UpdatePageOrderAsync(_page.SiteId, _page.PageId, int.Parse(_currentparentid));
|
||||
}
|
||||
|
||||
// update child paths
|
||||
if (_parentid != _currentparentid)
|
||||
{
|
||||
foreach (Page p in PageState.Pages.Where(item => item.Path.StartsWith(currentPath)))
|
||||
{
|
||||
p.Path = p.Path.Replace(currentPath, _page.Path);
|
||||
await PageService.UpdatePageAsync(p);
|
||||
}
|
||||
}
|
||||
|
||||
if (_themeSettingsType != null && _themeSettings is ISettingsControl themeSettingsControl)
|
||||
{
|
||||
await themeSettingsControl.UpdateSettings();
|
||||
@ -656,4 +640,24 @@
|
||||
NavigationManager.NavigateTo(NavigateUrl());
|
||||
}
|
||||
}
|
||||
|
||||
private async Task DeleteModule(Module module)
|
||||
{
|
||||
try
|
||||
{
|
||||
PageModule pagemodule = await PageModuleService.GetPageModuleAsync(_page.PageId, module.ModuleId);
|
||||
pagemodule.IsDeleted = true;
|
||||
await PageModuleService.UpdatePageModuleAsync(pagemodule);
|
||||
await logger.LogInformation(LogFunction.Update, "Module Deleted {Title}", module.Title);
|
||||
_pageModules.RemoveAll(item => item.PageModuleId == pagemodule.PageModuleId);
|
||||
StateHasChanged();
|
||||
NavigationManager.NavigateTo(NavigationManager.Uri + "&tab=PageModules");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
await logger.LogError(ex, "Error Deleting Module {Title} {Error}", module.Title, ex.Message);
|
||||
AddModuleMessage(Localizer["Error.Module.Delete"], MessageType.Error);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -234,53 +234,53 @@
|
||||
{
|
||||
<Section Name="Aliases" Heading="Aliases" ResourceKey="Aliases">
|
||||
<div class="container">
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="aliases" HelpText="The list of aliases for this site" ResourceKey="Aliases">Aliases: </Label>
|
||||
<div class="col-sm-9">
|
||||
<button type="button" class="btn btn-primary" @onclick="AddAlias">@SharedLocalizer["Add"]</button>
|
||||
<Pager Items="@_aliases">
|
||||
<Header>
|
||||
<th style="width: 1px;"> </th>
|
||||
<th style="width: 1px;"> </th>
|
||||
<th>@Localizer["AliasName"]</th>
|
||||
<th>@Localizer["AliasDefault"]</th>
|
||||
</Header>
|
||||
<Row>
|
||||
@if (context.AliasId != _aliasid)
|
||||
{
|
||||
<td>
|
||||
@if (_aliasid == -1)
|
||||
{
|
||||
<button type="button" class="btn btn-primary" @onclick="@(() => EditAlias(context))">@SharedLocalizer["Edit"]</button>
|
||||
}
|
||||
</td>
|
||||
<td>
|
||||
@if (_aliasid == -1)
|
||||
{
|
||||
<ActionDialog Action="Delete" OnClick="@(async () => await DeleteAlias(context))" ResourceKey="DeleteModule" Class="btn btn-danger" Header="Delete Alias" Message="@string.Format(Localizer["Confirm.Alias.Delete", context.Name])" />
|
||||
}
|
||||
</td>
|
||||
<td>@context.Name</td>
|
||||
<td>@context.IsDefault</td>
|
||||
}
|
||||
else
|
||||
{
|
||||
<td><button type="button" class="btn btn-success" @onclick="@(async () => await SaveAlias())">@SharedLocalizer["Save"]</button></td>
|
||||
<td><button type="button" class="btn btn-secondary" @onclick="@(async () => await CancelAlias())">@SharedLocalizer["Cancel"]</button></td>
|
||||
<td>
|
||||
<input id="aliasname" class="form-control" @bind="@_aliasname" />
|
||||
</td>
|
||||
<td>
|
||||
<select id="defaultaias" class="form-select" @bind="@_defaultalias" required>
|
||||
<option value="True">@SharedLocalizer["Yes"]</option>
|
||||
<option value="False">@SharedLocalizer["No"]</option>
|
||||
</select>
|
||||
</td>
|
||||
}
|
||||
</Row>
|
||||
</Pager>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="aliases" HelpText="The urls for the site. This can include domain names (ie. domain.com), subdomains (ie. sub.domain.com) or virtual folders (ie. domain.com/folder)." ResourceKey="Aliases">Aliases: </Label>
|
||||
<div class="col-sm-9">
|
||||
<button type="button" class="btn btn-primary" @onclick="AddAlias">@SharedLocalizer["Add"]</button>
|
||||
<Pager Items="@_aliases">
|
||||
<Header>
|
||||
<th style="width: 1px;"> </th>
|
||||
<th style="width: 1px;"> </th>
|
||||
<th>@Localizer["AliasName"]</th>
|
||||
<th>@Localizer["AliasDefault"]</th>
|
||||
</Header>
|
||||
<Row>
|
||||
@if (context.AliasId != _aliasid)
|
||||
{
|
||||
<td>
|
||||
@if (_aliasid == -1)
|
||||
{
|
||||
<button type="button" class="btn btn-primary" @onclick="@(() => EditAlias(context))">@SharedLocalizer["Edit"]</button>
|
||||
}
|
||||
</td>
|
||||
<td>
|
||||
@if (_aliasid == -1)
|
||||
{
|
||||
<ActionDialog Action="Delete" OnClick="@(async () => await DeleteAlias(context))" ResourceKey="DeleteModule" Class="btn btn-danger" Header="Delete Alias" Message="@string.Format(Localizer["Confirm.Alias.Delete", context.Name])" />
|
||||
}
|
||||
</td>
|
||||
<td>@context.Name</td>
|
||||
<td>@context.IsDefault</td>
|
||||
}
|
||||
else
|
||||
{
|
||||
<td><button type="button" class="btn btn-success" @onclick="@(async () => await SaveAlias())">@SharedLocalizer["Save"]</button></td>
|
||||
<td><button type="button" class="btn btn-secondary" @onclick="@(async () => await CancelAlias())">@SharedLocalizer["Cancel"]</button></td>
|
||||
<td>
|
||||
<input id="aliasname" class="form-control" @bind="@_aliasname" />
|
||||
</td>
|
||||
<td>
|
||||
<select id="defaultalias" class="form-select" @bind="@_defaultalias" required>
|
||||
<option value="True">@SharedLocalizer["Yes"]</option>
|
||||
<option value="False">@SharedLocalizer["No"]</option>
|
||||
</select>
|
||||
</td>
|
||||
}
|
||||
</Row>
|
||||
</Pager>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Section>
|
||||
<Section Name="Hosting" Heading="Hosting Model" ResourceKey="Hosting">
|
||||
|
@ -29,7 +29,7 @@ else
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="alias" HelpText="Enter the aliases for the site. An alias can be a domain name (www.site.com) or a virtual folder (ie. www.site.com/folder)." ResourceKey="Aliases">Aliases: </Label>
|
||||
<Label Class="col-sm-3" For="alias" HelpText="The urls for the site (comman delimited). This can include domain names (ie. domain.com), subdomains (ie. sub.domain.com) or virtual folders (ie. domain.com/folder)." ResourceKey="Aliases">Urls: </Label>
|
||||
<div class="col-sm-9">
|
||||
<textarea id="alias" class="form-control" @bind="@_urls" rows="3" required></textarea>
|
||||
</div>
|
||||
@ -288,12 +288,13 @@ else
|
||||
if (_themetype != "-")
|
||||
{
|
||||
_containers = ThemeService.GetContainerControls(_themeList, _themetype);
|
||||
}
|
||||
_containertype = _containers.First().TypeName;
|
||||
}
|
||||
else
|
||||
{
|
||||
_containers = new List<ThemeControl>();
|
||||
}
|
||||
_containertype = "-";
|
||||
_containertype = "-";
|
||||
}
|
||||
_admincontainertype = "";
|
||||
StateHasChanged();
|
||||
}
|
||||
|
@ -18,7 +18,7 @@ else
|
||||
<Header>
|
||||
<th style="width: 1px;"> </th>
|
||||
<th style="width: 1px;"> </th>
|
||||
<th>@SharedLocalizer["Name"]</th>
|
||||
<th>@Localizer["AliasName"]</th>
|
||||
</Header>
|
||||
<Row>
|
||||
<td><button type="button" class="btn btn-primary" @onclick="@(async () => Edit(context.Name))">@SharedLocalizer["Edit"]</button></td>
|
||||
|
@ -217,8 +217,13 @@
|
||||
}
|
||||
_packageid = package.PackageId;
|
||||
_version = package.Version;
|
||||
StateHasChanged();
|
||||
}
|
||||
else
|
||||
{
|
||||
await logger.LogError("Error Getting Package {PackageId} {Version}", packageid, version);
|
||||
AddModuleMessage(Localizer["Error.Theme.Download"], MessageType.Error);
|
||||
}
|
||||
StateHasChanged();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
@ -24,6 +24,7 @@ else
|
||||
<th>@SharedLocalizer["Name"]</th>
|
||||
<th>@SharedLocalizer["Version"]</th>
|
||||
<th>@Localizer["Enabled"]</th>
|
||||
<th>@SharedLocalizer["Support"]</th>
|
||||
<th>@SharedLocalizer["Expires"]</th>
|
||||
<th> </th>
|
||||
</Header>
|
||||
@ -47,6 +48,9 @@ else
|
||||
<span>@SharedLocalizer["No"]</span>
|
||||
}
|
||||
</td>
|
||||
<td>
|
||||
@((MarkupString)SupportLink(context.PackageName))
|
||||
</td>
|
||||
<td>
|
||||
@((MarkupString)PurchaseLink(context.PackageName))
|
||||
</td>
|
||||
@ -97,7 +101,7 @@ else
|
||||
{
|
||||
if (package.ExpiryDate != null && package.ExpiryDate.Value.Date != DateTime.MaxValue.Date)
|
||||
{
|
||||
link += "<span>" + package.ExpiryDate.Value.Date.ToString("MMM dd, yyyy") + "</span>";
|
||||
link += "<span>" + package.ExpiryDate.Value.Date.ToString("MMM dd, yyyy") + "</span><br />";
|
||||
if (!string.IsNullOrEmpty(package.PaymentUrl))
|
||||
{
|
||||
link += " <a class=\"btn btn-primary\" style=\"text-decoration: none !important\" href=\"" + package.PaymentUrl + "\" target=\"_new\">" + SharedLocalizer["Extend"] + "</a>";
|
||||
@ -108,6 +112,20 @@ else
|
||||
return link;
|
||||
}
|
||||
|
||||
private string SupportLink(string packagename)
|
||||
{
|
||||
string link = "";
|
||||
if (!string.IsNullOrEmpty(packagename) && _packages != null)
|
||||
{
|
||||
var package = _packages.Where(item => item.PackageId == packagename).FirstOrDefault();
|
||||
if (package != null && !string.IsNullOrEmpty(package.SupportUrl))
|
||||
{
|
||||
link += "<a class=\"btn btn-success\" style=\"text-decoration: none !important\" href=\"" + package.SupportUrl + "\" target=\"_new\">" + SharedLocalizer["Help"] + "</a>";
|
||||
}
|
||||
}
|
||||
return link;
|
||||
}
|
||||
|
||||
private string UpgradeAvailable(string packagename, string version)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(packagename) && _packages != null)
|
||||
|
@ -144,6 +144,11 @@ else
|
||||
<TabPanel Name="Notifications" ResourceKey="Notifications">
|
||||
@if (notifications != null)
|
||||
{
|
||||
<select class="form-select" @onchange="(e => FilterChanged(e))">
|
||||
<option value="to">@Localizer["Inbox"]</option>
|
||||
<option value="from">@Localizer["Items.Sent"]</option>
|
||||
</select>
|
||||
<br />
|
||||
<ActionLink Action="Add" Text="Send Notification" Security="SecurityAccessLevel.View" EditMode="false" ResourceKey="SendNotification" />
|
||||
<br /><br />
|
||||
@if (filter == "to")
|
||||
@ -159,22 +164,41 @@ else
|
||||
<Row>
|
||||
<td><ActionLink Action="View" Parameters="@($"id=" + context.NotificationId.ToString())" Security="SecurityAccessLevel.View" EditMode="false" ResourceKey="ViewNotification" /></td>
|
||||
<td><ActionDialog Header="Delete Notification" Message="Are You Sure You Wish To Delete This Notification?" Action="Delete" Security="SecurityAccessLevel.View" Class="btn btn-danger" OnClick="@(async () => await Delete(context))" EditMode="false" ResourceKey="DeleteNotification" /></td>
|
||||
<td>@context.FromDisplayName</td>
|
||||
<td>@context.Subject</td>
|
||||
<td>@string.Format("{0:dd-MMM-yyyy HH:mm:ss}", @context.CreatedOn)</td>
|
||||
|
||||
@if (context.IsRead)
|
||||
{
|
||||
<td>@context.FromDisplayName</td>
|
||||
<td>@context.Subject</td>
|
||||
<td>@string.Format("{0:dd-MMM-yyyy HH:mm:ss}", @context.CreatedOn)</td>
|
||||
}
|
||||
else
|
||||
{
|
||||
<td><b>@context.FromDisplayName</b></td>
|
||||
<td><b>@context.Subject</b></td>
|
||||
<td><b>@string.Format("{0:dd-MMM-yyyy HH:mm:ss}", @context.CreatedOn)</b></td>
|
||||
}
|
||||
</Row>
|
||||
<Detail>
|
||||
<td colspan="2"></td>
|
||||
<td colspan="3">
|
||||
@{
|
||||
string input = "___";
|
||||
if (context.Body.Contains(input))
|
||||
{
|
||||
context.Body = context.Body.Split(input)[0];
|
||||
context.Body = context.Body.Replace("\n", "");
|
||||
context.Body = context.Body.Replace("\r", "");
|
||||
} }
|
||||
@(context.Body.Length > 100 ? (context.Body.Substring(0, 97) + "...") : context.Body)
|
||||
string input = "___";
|
||||
if (context.Body.Contains(input))
|
||||
{
|
||||
context.Body = context.Body.Split(input)[0];
|
||||
context.Body = context.Body.Replace("\n", "");
|
||||
context.Body = context.Body.Replace("\r", "");
|
||||
}
|
||||
notificationSummary = context.Body.Length > 100 ? (context.Body.Substring(0, 97) + "...") : context.Body;
|
||||
}
|
||||
@if (context.IsRead)
|
||||
{
|
||||
@notificationSummary
|
||||
}
|
||||
else
|
||||
{
|
||||
<b>@notificationSummary</b>
|
||||
}
|
||||
</td>
|
||||
</Detail>
|
||||
</Pager>
|
||||
@ -192,22 +216,42 @@ else
|
||||
<Row>
|
||||
<td><ActionLink Action="View" Parameters="@($"id=" + context.NotificationId.ToString())" Security="SecurityAccessLevel.View" EditMode="false" ResourceKey="ViewNotification" /></td>
|
||||
<td><ActionDialog Header="Delete Notification" Message="Are You Sure You Wish To Delete This Notification?" Action="Delete" Security="SecurityAccessLevel.View" Class="btn btn-danger" OnClick="@(async () => await Delete(context))" EditMode="false" ResourceKey="DeleteNotification" /></td>
|
||||
<td>@context.ToDisplayName</td>
|
||||
<td>@context.Subject</td>
|
||||
<td>@string.Format("{0:dd-MMM-yyyy HH:mm:ss}", @context.CreatedOn)</td>
|
||||
|
||||
@if (context.IsRead)
|
||||
{
|
||||
<td>@context.ToDisplayName</td>
|
||||
<td>@context.Subject</td>
|
||||
<td>@string.Format("{0:dd-MMM-yyyy HH:mm:ss}", @context.CreatedOn)</td>
|
||||
}
|
||||
else
|
||||
{
|
||||
<td><b>@context.ToDisplayName</b></td>
|
||||
<td><b>@context.Subject</b></td>
|
||||
<td><b>@string.Format("{0:dd-MMM-yyyy HH:mm:ss}", @context.CreatedOn)</b></td>
|
||||
}
|
||||
|
||||
</Row>
|
||||
<Detail>
|
||||
<td colspan="2"></td>
|
||||
<td colspan="3">
|
||||
@{
|
||||
string input = "___";
|
||||
if (context.Body.Contains(input))
|
||||
{
|
||||
context.Body = context.Body.Split(input)[0];
|
||||
context.Body = context.Body.Replace("\n", "");
|
||||
context.Body = context.Body.Replace("\r", "");
|
||||
} }
|
||||
@(context.Body.Length > 100 ? (context.Body.Substring(0, 97) + "...") : context.Body)
|
||||
string input = "___";
|
||||
if (context.Body.Contains(input))
|
||||
{
|
||||
context.Body = context.Body.Split(input)[0];
|
||||
context.Body = context.Body.Replace("\n", "");
|
||||
context.Body = context.Body.Replace("\r", "");
|
||||
}
|
||||
notificationSummary = context.Body.Length > 100 ? (context.Body.Substring(0, 97) + "...") : context.Body;
|
||||
}
|
||||
@if (context.IsRead)
|
||||
{
|
||||
@notificationSummary
|
||||
}
|
||||
else
|
||||
{
|
||||
<b>@notificationSummary</b>
|
||||
}
|
||||
</td>
|
||||
</Detail>
|
||||
</Pager>
|
||||
@ -217,11 +261,6 @@ else
|
||||
<br />
|
||||
<ActionDialog Header="Clear Notifications" Message="Are You Sure You Wish To Permanently Delete All Notifications ?" Action="Delete All Notifications" Security="SecurityAccessLevel.Admin" Class="btn btn-danger" OnClick="@(async () => await DeleteAllNotifications())" ResourceKey="DeleteAllNotifications" />
|
||||
}
|
||||
<br /><hr />
|
||||
<select class="form-select" @onchange="(e => FilterChanged(e))">
|
||||
<option value="to">@Localizer["Inbox"]</option>
|
||||
<option value="from">@Localizer["Items.Sent"]</option>
|
||||
</select>
|
||||
}
|
||||
</TabPanel>
|
||||
</TabStrip>
|
||||
@ -246,6 +285,7 @@ else
|
||||
private string category = string.Empty;
|
||||
private string filter = "to";
|
||||
private List<Notification> notifications;
|
||||
private string notificationSummary = string.Empty;
|
||||
|
||||
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.View;
|
||||
|
||||
|
@ -118,6 +118,9 @@
|
||||
Notification notification = await NotificationService.GetNotificationAsync(notificationid);
|
||||
if (notification != null)
|
||||
{
|
||||
notification.IsRead = true;
|
||||
notification = await NotificationService.UpdateNotificationAsync(notification);
|
||||
|
||||
int userid = -1;
|
||||
if (notification.ToUserId == PageState.User.UserId)
|
||||
{
|
||||
|
@ -35,9 +35,9 @@ else
|
||||
<th style="width: 1px;"> </th>
|
||||
<th style="width: 1px;"> </th>
|
||||
<th style="width: 1px;"> </th>
|
||||
<th>@SharedLocalizer["Username"]</th>
|
||||
<th>@SharedLocalizer["Name"]</th>
|
||||
<th>@Localizer["LastLoginOn"]</th>
|
||||
<th class="app-sort-th" @onclick="@(() => SortTable("Username"))">@Localizer["Username"]<i class="@(SetSortIcon("Username"))"></i></th>
|
||||
<th class="app-sort-th" @onclick="@(() => SortTable("DisplayName"))">@Localizer["Name"]<i class="@(SetSortIcon("DisplayName"))"></i></th>
|
||||
<th class="app-sort-th" @onclick="@(() => SortTable("LastLoginOn"))">@Localizer["LastLoginOn"]<i class="@(SetSortIcon("LastLoginOn"))"></i></th>
|
||||
</Header>
|
||||
<Row>
|
||||
<td>
|
||||
@ -413,6 +413,9 @@ else
|
||||
private string _lifetime;
|
||||
private string _token;
|
||||
|
||||
private bool isSortedAscending;
|
||||
private string activeSortColumn;
|
||||
|
||||
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.View;
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
@ -654,4 +657,43 @@ else
|
||||
_togglesecret = SharedLocalizer["ShowPassword"];
|
||||
}
|
||||
}
|
||||
|
||||
private void SortTable(string columnName)
|
||||
{
|
||||
if (columnName != activeSortColumn)
|
||||
{
|
||||
users = users.OrderBy(x => x.User.GetType().GetProperty(columnName)?.GetValue(x.User)).ToList();
|
||||
isSortedAscending = true;
|
||||
activeSortColumn = columnName;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (isSortedAscending)
|
||||
{
|
||||
users = users.OrderByDescending(x => x.User.GetType().GetProperty(columnName)?.GetValue(x.User)).ToList();
|
||||
}
|
||||
else
|
||||
{
|
||||
users = users.OrderBy(x => x.User.GetType().GetProperty(columnName)?.GetValue(x.User)).ToList();
|
||||
}
|
||||
|
||||
isSortedAscending = !isSortedAscending;
|
||||
}
|
||||
}
|
||||
|
||||
private string SetSortIcon(string columnName)
|
||||
{
|
||||
if (activeSortColumn != columnName)
|
||||
{
|
||||
return "app-fas pe-3 ";
|
||||
}
|
||||
if (isSortedAscending)
|
||||
{
|
||||
return "app-fas oi oi-sort-ascending";
|
||||
}
|
||||
else
|
||||
{
|
||||
return "app-fas oi oi-sort-descending";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -34,13 +34,13 @@ else
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="effectiveDate" HelpText="The date that this role assignment is active" ResourceKey="EffectiveDate">Effective Date: </Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="effectiveDate" class="form-control" @bind="@effectivedate" />
|
||||
<input type="date" id="effectiveDate" class="form-control" @bind="@effectivedate" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="expiryDate" HelpText="The date that this role assignment expires" ResourceKey="ExpiryDate">Expiry Date: </Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="expiryDate" class="form-control" @bind="@expirydate" />
|
||||
<input type="date" id="expiryDate" class="form-control" @bind="@expirydate" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -75,8 +75,8 @@ else
|
||||
private string name = string.Empty;
|
||||
private List<Role> roles;
|
||||
private int roleid = -1;
|
||||
private string effectivedate = string.Empty;
|
||||
private string expirydate = string.Empty;
|
||||
private DateTime? effectivedate = null;
|
||||
private DateTime? expirydate = null;
|
||||
private List<UserRole> userroles;
|
||||
|
||||
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Edit;
|
||||
@ -130,23 +130,8 @@ else
|
||||
var userrole = userroles.Where(item => item.UserId == userid && item.RoleId == roleid).FirstOrDefault();
|
||||
if (userrole != null)
|
||||
{
|
||||
if (string.IsNullOrEmpty(effectivedate))
|
||||
{
|
||||
userrole.EffectiveDate = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
userrole.EffectiveDate = DateTime.Parse(effectivedate);
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(expirydate))
|
||||
{
|
||||
userrole.ExpiryDate = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
userrole.ExpiryDate = DateTime.Parse(expirydate);
|
||||
}
|
||||
userrole.EffectiveDate = effectivedate;
|
||||
userrole.ExpiryDate = expirydate;
|
||||
await UserRoleService.UpdateUserRoleAsync(userrole);
|
||||
}
|
||||
else
|
||||
@ -154,25 +139,8 @@ else
|
||||
userrole = new UserRole();
|
||||
userrole.UserId = userid;
|
||||
userrole.RoleId = roleid;
|
||||
|
||||
if (string.IsNullOrEmpty(effectivedate))
|
||||
{
|
||||
userrole.EffectiveDate = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
userrole.EffectiveDate = DateTime.Parse(effectivedate);
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(expirydate))
|
||||
{
|
||||
userrole.ExpiryDate = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
userrole.ExpiryDate = DateTime.Parse(expirydate);
|
||||
}
|
||||
|
||||
userrole.EffectiveDate = effectivedate;
|
||||
userrole.ExpiryDate = expirydate;
|
||||
await UserRoleService.AddUserRoleAsync(userrole);
|
||||
}
|
||||
|
||||
|
@ -55,16 +55,26 @@
|
||||
</div>
|
||||
<div class="col mt-2 text-end">
|
||||
<button type="button" class="btn btn-success" @onclick="UploadFiles">@SharedLocalizer["Upload"]</button>
|
||||
@if (ShowFiles && GetFileId() != -1)
|
||||
@if (GetFileId() != -1)
|
||||
{
|
||||
<button type="button" class="btn btn-danger mx-1" @onclick="DeleteFile">@SharedLocalizer["Delete"]</button>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col mt-1"><span id="@_progressinfoid" style="display: none;"></span></div>
|
||||
<div class="col text-center mt-1"><progress id="@_progressbarid" class="mt-1" style="display: none;"></progress></div>
|
||||
</div>
|
||||
@if (ShowProgress)
|
||||
{
|
||||
<div class="row">
|
||||
<div class="col mt-1"><span id="@_progressinfoid" style="display: none;"></span></div>
|
||||
<div class="col mt-1"><progress id="@_progressbarid" class="mt-1" style="display: none;"></progress></div>
|
||||
</div>
|
||||
}
|
||||
else
|
||||
{
|
||||
if (_uploading)
|
||||
{
|
||||
<div class="app-progress-indicator"></div>
|
||||
}
|
||||
}
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
@ -100,6 +110,7 @@
|
||||
private string _guid;
|
||||
private string _message = string.Empty;
|
||||
private MessageType _messagetype;
|
||||
private bool _uploading = false;
|
||||
|
||||
[Parameter]
|
||||
public string Id { get; set; } // optional - for setting the id of the FileManager component for accessibility
|
||||
@ -128,6 +139,9 @@
|
||||
[Parameter]
|
||||
public bool ShowImage { get; set; } = true; // optional - for indicating whether an image thumbnail should be displayed - default is true
|
||||
|
||||
[Parameter]
|
||||
public bool ShowProgress { get; set; } = true; // optional - for indicating whether progress info should be displayed during upload - default is true
|
||||
|
||||
[Parameter]
|
||||
public bool ShowSuccess { get; set; } = false; // optional - for indicating whether a success message should be displayed upon successful upload - default is false
|
||||
|
||||
@ -143,7 +157,7 @@
|
||||
[Parameter]
|
||||
public EventCallback<int> OnDelete { get; set; } // optional - executes a method in the calling component when a file is deleted
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
protected override async Task OnParametersSetAsync()
|
||||
{
|
||||
// packages folder is a framework folder for uploading installable nuget packages
|
||||
if (Folder == Constants.PackagesFolder)
|
||||
@ -154,11 +168,6 @@
|
||||
ShowSuccess = true;
|
||||
}
|
||||
|
||||
if (!ShowFiles)
|
||||
{
|
||||
ShowImage = false;
|
||||
}
|
||||
|
||||
_folders = await FolderService.GetFoldersAsync(ModuleState.SiteId);
|
||||
|
||||
if (!string.IsNullOrEmpty(Folder) && Folder != Constants.PackagesFolder)
|
||||
@ -182,7 +191,6 @@
|
||||
if (file != null)
|
||||
{
|
||||
FolderId = file.FolderId;
|
||||
await OnSelect.InvokeAsync(FileId);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -224,7 +232,14 @@
|
||||
if (folder != null)
|
||||
{
|
||||
_haseditpermission = UserSecurity.IsAuthorized(PageState.User, PermissionNames.Edit, folder.PermissionList);
|
||||
_files = await FileService.GetFilesAsync(FolderId);
|
||||
if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.Browse, folder.PermissionList))
|
||||
{
|
||||
_files = await FileService.GetFilesAsync(FolderId);
|
||||
}
|
||||
else
|
||||
{
|
||||
_files = new List<File>();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -270,13 +285,10 @@
|
||||
{
|
||||
_message = string.Empty;
|
||||
FileId = int.Parse((string)e.Value);
|
||||
if (FileId != -1)
|
||||
{
|
||||
await OnSelect.InvokeAsync(FileId);
|
||||
}
|
||||
|
||||
await SetImage();
|
||||
await OnSelect.InvokeAsync(FileId);
|
||||
StateHasChanged();
|
||||
|
||||
}
|
||||
|
||||
private async Task SetImage()
|
||||
@ -320,6 +332,12 @@
|
||||
}
|
||||
if (restricted == "")
|
||||
{
|
||||
if (!ShowProgress)
|
||||
{
|
||||
_uploading = true;
|
||||
StateHasChanged();
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
// upload the files
|
||||
@ -327,32 +345,42 @@
|
||||
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)
|
||||
// uploading is asynchronous so we need to poll to determine if uploads are completed
|
||||
var success = true;
|
||||
int upload = 0;
|
||||
while (upload < uploads.Length && success)
|
||||
{
|
||||
attempts += 1;
|
||||
Thread.Sleep(1000 * attempts); // progressive retry
|
||||
|
||||
success = true;
|
||||
List<File> files = await FileService.GetFilesAsync(folder);
|
||||
if (files.Count > 0)
|
||||
success = false;
|
||||
// note that progressive retry will only wait a maximum of 15 seconds which may not be long enough for very large file uploads
|
||||
int attempts = 0;
|
||||
while (attempts < 5 && !success)
|
||||
{
|
||||
foreach (string upload in uploads)
|
||||
attempts += 1;
|
||||
Thread.Sleep(1000 * attempts); // progressive retry
|
||||
|
||||
var file = await FileService.GetFileAsync(int.Parse(folder), uploads[upload]);
|
||||
if (file != null)
|
||||
{
|
||||
if (!files.Exists(item => item.Name == upload))
|
||||
{
|
||||
success = false;
|
||||
}
|
||||
success = true;
|
||||
}
|
||||
}
|
||||
if (success)
|
||||
{
|
||||
upload++;
|
||||
}
|
||||
}
|
||||
|
||||
// reset progress indicators
|
||||
await interop.SetElementAttribute(_guid + "ProgressInfo", "style", "display: none;");
|
||||
await interop.SetElementAttribute(_guid + "ProgressBar", "style", "display: none;");
|
||||
if (ShowProgress)
|
||||
{
|
||||
await interop.SetElementAttribute(_guid + "ProgressInfo", "style", "display: none;");
|
||||
await interop.SetElementAttribute(_guid + "ProgressBar", "style", "display: none;");
|
||||
}
|
||||
else
|
||||
{
|
||||
_uploading = false;
|
||||
StateHasChanged();
|
||||
}
|
||||
|
||||
if (success)
|
||||
{
|
||||
@ -377,48 +405,53 @@
|
||||
else
|
||||
{
|
||||
// set FileId to first file in upload collection
|
||||
await GetFiles();
|
||||
var file = _files.Where(item => item.Name == uploads[0]).FirstOrDefault();
|
||||
var file = await FileService.GetFileAsync(int.Parse(folder), uploads[0]);
|
||||
if (file != null)
|
||||
{
|
||||
FileId = file.FileId;
|
||||
await SetImage();
|
||||
await OnUpload.InvokeAsync(FileId);
|
||||
}
|
||||
await GetFiles();
|
||||
StateHasChanged();
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
await logger.LogError(ex, "File Upload Failed {Error}", ex.Message);
|
||||
_message = Localizer["Error.File.Upload"];
|
||||
_messagetype = MessageType.Error;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_message = string.Format(Localizer["Message.File.Restricted"], restricted);
|
||||
_messagetype = MessageType.Warning;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_message = Localizer["Message.File.NotSelected"];
|
||||
_messagetype = MessageType.Warning;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
await logger.LogError(ex, "File Upload Failed {Error}", ex.Message);
|
||||
_message = Localizer["Error.File.Upload"];
|
||||
_messagetype = MessageType.Error;
|
||||
_uploading = false;
|
||||
}
|
||||
|
||||
private async Task DeleteFile()
|
||||
{
|
||||
_message = string.Empty;
|
||||
try
|
||||
{
|
||||
await FileService.DeleteFileAsync(FileId);
|
||||
await logger.LogInformation("File Deleted {File}", FileId);
|
||||
await OnDelete.InvokeAsync(FileId);
|
||||
}
|
||||
else
|
||||
{
|
||||
_message = string.Format(Localizer["Message.File.Restricted"], restricted);
|
||||
_messagetype = MessageType.Warning;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_message = Localizer["Message.File.NotSelected"];
|
||||
_messagetype = MessageType.Warning;
|
||||
}
|
||||
}
|
||||
|
||||
_message = Localizer["Success.File.Delete"];
|
||||
_messagetype = MessageType.Success;
|
||||
private async Task DeleteFile()
|
||||
{
|
||||
_message = string.Empty;
|
||||
try
|
||||
{
|
||||
await FileService.DeleteFileAsync(FileId);
|
||||
await logger.LogInformation("File Deleted {File}", FileId);
|
||||
await OnDelete.InvokeAsync(FileId);
|
||||
|
||||
if (ShowSuccess)
|
||||
{
|
||||
_message = Localizer["Success.File.Delete"];
|
||||
_messagetype = MessageType.Success;
|
||||
}
|
||||
|
||||
await GetFiles();
|
||||
FileId = -1;
|
||||
|
@ -17,7 +17,10 @@
|
||||
<hr class="app-rule" />
|
||||
</div>
|
||||
<div class="collapse @_show" id="@Name">
|
||||
@ChildContent
|
||||
@if (ChildContent != null)
|
||||
{
|
||||
@ChildContent
|
||||
}
|
||||
</div>
|
||||
|
||||
@code {
|
||||
@ -26,7 +29,7 @@
|
||||
private string _show = string.Empty;
|
||||
|
||||
[Parameter]
|
||||
public RenderFragment ChildContent { get; set; }
|
||||
public RenderFragment ChildContent { get; set; } = null;
|
||||
|
||||
[Parameter]
|
||||
public string Name { get; set; } // required - the name of the section
|
||||
@ -37,19 +40,10 @@
|
||||
[Parameter]
|
||||
public string Expanded { get; set; } // optional - will default to false if not provided
|
||||
|
||||
protected override void OnInitialized()
|
||||
{
|
||||
_heading = (!string.IsNullOrEmpty(Heading)) ? Heading : Name;
|
||||
_expanded = (!string.IsNullOrEmpty(Expanded)) ? Expanded : "false";
|
||||
if (_expanded == "true") { _show = "show"; }
|
||||
}
|
||||
|
||||
protected override void OnParametersSet()
|
||||
{
|
||||
base.OnParametersSet();
|
||||
|
||||
_heading = !string.IsNullOrEmpty(Heading)
|
||||
? Localize(nameof(Heading), Heading)
|
||||
: Localize(nameof(Name), Name);
|
||||
_heading = !string.IsNullOrEmpty(Heading) ? Localize(nameof(Heading), Heading) : Localize(nameof(Name), Name);
|
||||
_expanded = (!string.IsNullOrEmpty(Expanded)) ? Expanded.ToLower() : "false";
|
||||
if (_expanded == "true") { _show = "show"; }
|
||||
}
|
||||
}
|
||||
|
@ -8,7 +8,7 @@
|
||||
@foreach (TabPanel tabPanel in _tabPanels)
|
||||
{
|
||||
<li class="nav-item" @key="tabPanel.Name">
|
||||
@if (tabPanel.Name == ActiveTab)
|
||||
@if (tabPanel.Name.ToLower() == ActiveTab.ToLower())
|
||||
{
|
||||
<a class="nav-link active" data-bs-toggle="tab" href="#@(Id + tabPanel.Name)" role="tab" @onclick:preventDefault="true">
|
||||
@tabPanel.DisplayHeading()
|
||||
|
@ -9,7 +9,6 @@ using Oqtane.UI;
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.JSInterop;
|
||||
using System.Linq;
|
||||
using Oqtane.Themes;
|
||||
|
||||
namespace Oqtane.Modules
|
||||
{
|
||||
|
@ -3,9 +3,8 @@
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<OutputType>Exe</OutputType>
|
||||
<RazorLangVersion>3.0</RazorLangVersion>
|
||||
<Configurations>Debug;Release</Configurations>
|
||||
<Version>4.0.0</Version>
|
||||
<Version>4.0.1</Version>
|
||||
<Product>Oqtane</Product>
|
||||
<Authors>Shaun Walker</Authors>
|
||||
<Company>.NET Foundation</Company>
|
||||
@ -13,7 +12,7 @@
|
||||
<Copyright>.NET Foundation</Copyright>
|
||||
<PackageProjectUrl>https://www.oqtane.org</PackageProjectUrl>
|
||||
<PackageLicenseUrl>https://github.com/oqtane/oqtane.framework/blob/dev/LICENSE</PackageLicenseUrl>
|
||||
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v4.0.0</PackageReleaseNotes>
|
||||
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v4.0.1</PackageReleaseNotes>
|
||||
<RepositoryUrl>https://github.com/oqtane/oqtane.framework</RepositoryUrl>
|
||||
<RepositoryType>Git</RepositoryType>
|
||||
<RootNamespace>Oqtane</RootNamespace>
|
||||
|
@ -1,4 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<root>
|
||||
<!--
|
||||
Microsoft ResX Schema
|
||||
@ -136,7 +136,7 @@
|
||||
<value>The function that was performed</value>
|
||||
</data>
|
||||
<data name="Category.HelpText" xml:space="preserve">
|
||||
<value>The categories that were affected</value>
|
||||
<value>The fully qualified type type that was affected</value>
|
||||
</data>
|
||||
<data name="Page.HelpText" xml:space="preserve">
|
||||
<value>The page that was affected</value>
|
||||
@ -178,7 +178,7 @@
|
||||
<value>Function: </value>
|
||||
</data>
|
||||
<data name="Category.Text" xml:space="preserve">
|
||||
<value>Category: </value>
|
||||
<value>Type Name: </value>
|
||||
</data>
|
||||
<data name="Page.Text" xml:space="preserve">
|
||||
<value>Page: </value>
|
||||
|
@ -150,4 +150,10 @@
|
||||
<data name="Error.Module.Load" xml:space="preserve">
|
||||
<value>A Problem Was Encountered Loading Module {0}. The Module Is Either Invalid Or Does Not Exist.</value>
|
||||
</data>
|
||||
<data name="Module.HelpText" xml:space="preserve">
|
||||
<value>The name of the module</value>
|
||||
</data>
|
||||
<data name="Module.Text" xml:space="preserve">
|
||||
<value>Module:</value>
|
||||
</data>
|
||||
</root>
|
@ -246,4 +246,7 @@
|
||||
<data name="PageContent.Heading" xml:space="preserve">
|
||||
<value>Page Content</value>
|
||||
</data>
|
||||
<data name="ThemeChanged.Message" xml:space="preserve">
|
||||
<value>Please Note That Overriding The Default Site Theme With An Unrelated Page Theme May Result In Compatibility Issues For Your Site</value>
|
||||
</data>
|
||||
</root>
|
@ -282,4 +282,7 @@
|
||||
<data name="PageContent.Heading" xml:space="preserve">
|
||||
<value>Page Content</value>
|
||||
</data>
|
||||
<data name="ThemeChanged.Message" xml:space="preserve">
|
||||
<value>Please Note That Overriding The Default Site Theme With An Unrelated Page Theme May Result In Compatibility Issues For Your Site</value>
|
||||
</data>
|
||||
</root>
|
@ -166,7 +166,7 @@
|
||||
<value>The name of the database used for the site</value>
|
||||
</data>
|
||||
<data name="Aliases.HelpText" xml:space="preserve">
|
||||
<value>The aliases for the site. An alias can be a domain name (www.site.com) or a virtual folder (ie. www.site.com/folder).</value>
|
||||
<value>The urls for the site. This can include domain names (ie. domain.com), subdomains (ie. sub.domain.com) or virtual folders (ie. domain.com/folder).</value>
|
||||
</data>
|
||||
<data name="IsDeleted.HelpText" xml:space="preserve">
|
||||
<value>Is this site deleted?</value>
|
||||
@ -217,7 +217,7 @@
|
||||
<value>Database: </value>
|
||||
</data>
|
||||
<data name="Aliases.Text" xml:space="preserve">
|
||||
<value>Aliases: </value>
|
||||
<value>Urls:</value>
|
||||
</data>
|
||||
<data name="IsDeleted.Text" xml:space="preserve">
|
||||
<value>Deleted? </value>
|
||||
@ -322,10 +322,10 @@
|
||||
<value>Default Alias: </value>
|
||||
</data>
|
||||
<data name="Aliases.Heading" xml:space="preserve">
|
||||
<value>Aliases</value>
|
||||
<value>Urls</value>
|
||||
</data>
|
||||
<data name="AliasName" xml:space="preserve">
|
||||
<value>Name</value>
|
||||
<value>Url</value>
|
||||
</data>
|
||||
<data name="AliasDefault" xml:space="preserve">
|
||||
<value>Default?</value>
|
||||
|
@ -136,7 +136,7 @@
|
||||
<value>Default Admin Container</value>
|
||||
</data>
|
||||
<data name="Aliases.HelpText" xml:space="preserve">
|
||||
<value>Enter the alias for the server</value>
|
||||
<value>The urls for the site (comman delimited). This can include domain names (ie. domain.com), subdomains (ie. sub.domain.com) or a virtual folder (ie. domain.com/folder).</value>
|
||||
</data>
|
||||
<data name="DefaultContainer.HelpText" xml:space="preserve">
|
||||
<value>Select the default container for the site</value>
|
||||
@ -145,7 +145,7 @@
|
||||
<value>Database: </value>
|
||||
</data>
|
||||
<data name="Aliases.Text" xml:space="preserve">
|
||||
<value>Aliases: </value>
|
||||
<value>Urls: </value>
|
||||
</data>
|
||||
<data name="DefaultTheme.Text" xml:space="preserve">
|
||||
<value>Default Theme: </value>
|
||||
|
@ -135,4 +135,7 @@
|
||||
<data name="Browse" xml:space="preserve">
|
||||
<value>Browse</value>
|
||||
</data>
|
||||
<data name="AliasName" xml:space="preserve">
|
||||
<value>Url</value>
|
||||
</data>
|
||||
</root>
|
@ -343,7 +343,7 @@
|
||||
<value>Please note that third party extensions are registered in the <a href="https://www.oqtane.net" target="_new">Oqtane Marketplace</a> which enables them to be seamlessly downloaded and installed into the framework.</value>
|
||||
</data>
|
||||
<data name="Home" xml:space="preserve">
|
||||
<value>Home</value>
|
||||
<value>Home</value>
|
||||
</data>
|
||||
<data name="Close" xml:space="preserve">
|
||||
<value>Close</value>
|
||||
@ -384,4 +384,10 @@
|
||||
<data name="Confirm" xml:space="preserve">
|
||||
<value>Confirm</value>
|
||||
</data>
|
||||
</root>
|
||||
<data name="Help" xml:space="preserve">
|
||||
<value>Help</value>
|
||||
</data>
|
||||
<data name="Support" xml:space="preserve">
|
||||
<value>Support</value>
|
||||
</data>
|
||||
</root>
|
@ -46,6 +46,11 @@ namespace Oqtane.Services
|
||||
return await GetJsonAsync<File>($"{Apiurl}/{fileId}");
|
||||
}
|
||||
|
||||
public async Task<File> GetFileAsync(int folderId, string name)
|
||||
{
|
||||
return await GetJsonAsync<File>($"{Apiurl}/name/{name}/{folderId}");
|
||||
}
|
||||
|
||||
public async Task<File> AddFileAsync(File file)
|
||||
{
|
||||
return await PostJsonAsync<File>(Apiurl, file);
|
||||
|
@ -1,5 +1,6 @@
|
||||
using Oqtane.Models;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Oqtane.Services
|
||||
@ -33,6 +34,15 @@ namespace Oqtane.Services
|
||||
/// <returns></returns>
|
||||
Task<File> GetFileAsync(int fileId);
|
||||
|
||||
/// <summary>
|
||||
/// Get a <see cref="File"/> based on the <see cref="Folder"/> and file name.
|
||||
/// </summary>
|
||||
/// <param name="folderId">Reference to the <see cref="Folder"/></param>
|
||||
/// <param name="name">name of the file
|
||||
/// </param>
|
||||
/// <returns></returns>
|
||||
Task<File> GetFileAsync(int folderId, string name);
|
||||
|
||||
/// <summary>
|
||||
/// Add / store a <see cref="File"/> record.
|
||||
/// This does not contain the file contents.
|
||||
|
@ -18,6 +18,27 @@ namespace Oqtane.Services
|
||||
/// <returns></returns>
|
||||
Task<List<Notification>> GetNotificationsAsync(int siteId, string direction, int userId);
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="siteId"></param>
|
||||
/// <param name="direction"></param>
|
||||
/// <param name="userId"></param>
|
||||
/// <param name="count"></param>
|
||||
/// <param name="isRead"></param>
|
||||
/// <returns></returns>
|
||||
Task<List<Notification>> GetNotificationsAsync(int siteId, string direction, int userId, int count, bool isRead);
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="siteId"></param>
|
||||
/// <param name="direction"></param>
|
||||
/// <param name="userId"></param>
|
||||
/// <param name="isRead"></param>
|
||||
/// <returns></returns>
|
||||
Task<int> GetNotificationCountAsync(int siteId, string direction, int userId, bool isRead);
|
||||
|
||||
/// <summary>
|
||||
/// Returns a specific notifications
|
||||
/// </summary>
|
||||
|
@ -27,6 +27,17 @@ namespace Oqtane.Services
|
||||
/// <returns></returns>
|
||||
Task<List<Package>> GetPackagesAsync(string type, string search, string price, string package);
|
||||
|
||||
/// <summary>
|
||||
/// Returns a list of packages matching the given parameters
|
||||
/// </summary>
|
||||
/// <param name="type"></param>
|
||||
/// <param name="search"></param>
|
||||
/// <param name="price"></param>
|
||||
/// <param name="package"></param>
|
||||
/// <param name="sort"></param>
|
||||
/// <returns></returns>
|
||||
Task<List<Package>> GetPackagesAsync(string type, string search, string price, string package, string sort);
|
||||
|
||||
/// <summary>
|
||||
/// Returns a specific package
|
||||
/// </summary>
|
||||
|
@ -22,6 +22,20 @@ namespace Oqtane.Services
|
||||
return notifications.OrderByDescending(item => item.CreatedOn).ToList();
|
||||
}
|
||||
|
||||
public async Task<List<Notification>> GetNotificationsAsync(int siteId, string direction, int userId, int count, bool isRead)
|
||||
{
|
||||
var notifications = await GetJsonAsync<List<Notification>>($"{Apiurl}/read?siteid={siteId}&direction={direction.ToLower()}&userid={userId}&count={count}&isread={isRead}");
|
||||
|
||||
return notifications.OrderByDescending(item => item.CreatedOn).ToList();
|
||||
}
|
||||
|
||||
public async Task<int> GetNotificationCountAsync(int siteId, string direction, int userId, bool isRead)
|
||||
{
|
||||
var notificationCount = await GetJsonAsync<int>($"{Apiurl}/read-count?siteid={siteId}&direction={direction.ToLower()}&userid={userId}&isread={isRead}");
|
||||
|
||||
return notificationCount;
|
||||
}
|
||||
|
||||
public async Task<Notification> GetNotificationAsync(int notificationId)
|
||||
{
|
||||
return await GetJsonAsync<Notification>($"{Apiurl}/{notificationId}");
|
||||
|
@ -23,7 +23,12 @@ namespace Oqtane.Services
|
||||
|
||||
public async Task<List<Package>> GetPackagesAsync(string type, string search, string price, string package)
|
||||
{
|
||||
return await GetJsonAsync<List<Package>>($"{Apiurl}?type={type}&search={WebUtility.UrlEncode(search)}&price={price}&package={package}");
|
||||
return await GetPackagesAsync(type, search, price, package, "");
|
||||
}
|
||||
|
||||
public async Task<List<Package>> GetPackagesAsync(string type, string search, string price, string package, string sort)
|
||||
{
|
||||
return await GetJsonAsync<List<Package>>($"{Apiurl}?type={type}&search={WebUtility.UrlEncode(search)}&price={price}&package={package}&sort={sort}");
|
||||
}
|
||||
|
||||
public async Task<Package> GetPackageAsync(string packageId, string version)
|
||||
|
@ -3,8 +3,10 @@ using System.Collections.Generic;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Net.Http.Json;
|
||||
using System.Text.Json;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Oqtane.Enums;
|
||||
using Oqtane.Models;
|
||||
using Oqtane.Shared;
|
||||
|
||||
@ -21,7 +23,7 @@ namespace Oqtane.Services
|
||||
_siteState = siteState;
|
||||
}
|
||||
|
||||
private HttpClient GetHttpClient()
|
||||
public HttpClient GetHttpClient()
|
||||
{
|
||||
if (!_httpClient.DefaultRequestHeaders.Contains(Constants.AntiForgeryTokenHeaderName) && _siteState != null && !string.IsNullOrEmpty(_siteState.AntiForgeryToken))
|
||||
{
|
||||
@ -64,7 +66,7 @@ namespace Oqtane.Services
|
||||
// legacy support for ControllerRoutes.Default
|
||||
if (alias != null)
|
||||
{
|
||||
// include the alias for multi-tenant context
|
||||
// include the alias id for multi-tenant context
|
||||
apiurl += $"{alias.AliasId}/";
|
||||
}
|
||||
else
|
||||
@ -105,7 +107,7 @@ namespace Oqtane.Services
|
||||
protected async Task GetAsync(string uri)
|
||||
{
|
||||
var response = await GetHttpClient().GetAsync(uri);
|
||||
CheckResponse(response);
|
||||
await CheckResponse(response, uri);
|
||||
}
|
||||
|
||||
protected async Task<string> GetStringAsync(string uri)
|
||||
@ -139,7 +141,7 @@ namespace Oqtane.Services
|
||||
protected async Task<T> GetJsonAsync<T>(string uri)
|
||||
{
|
||||
var response = await GetHttpClient().GetAsync(uri, HttpCompletionOption.ResponseHeadersRead, CancellationToken.None);
|
||||
if (CheckResponse(response) && ValidateJsonContent(response.Content))
|
||||
if (await CheckResponse(response, uri) && ValidateJsonContent(response.Content))
|
||||
{
|
||||
return await response.Content.ReadFromJsonAsync<T>();
|
||||
}
|
||||
@ -150,7 +152,7 @@ namespace Oqtane.Services
|
||||
protected async Task PutAsync(string uri)
|
||||
{
|
||||
var response = await GetHttpClient().PutAsync(uri, null);
|
||||
CheckResponse(response);
|
||||
await CheckResponse(response, uri);
|
||||
}
|
||||
|
||||
protected async Task<T> PutJsonAsync<T>(string uri, T value)
|
||||
@ -161,7 +163,7 @@ namespace Oqtane.Services
|
||||
protected async Task<TResult> PutJsonAsync<TValue, TResult>(string uri, TValue value)
|
||||
{
|
||||
var response = await GetHttpClient().PutAsJsonAsync(uri, value);
|
||||
if (CheckResponse(response) && ValidateJsonContent(response.Content))
|
||||
if (await CheckResponse(response, uri) && ValidateJsonContent(response.Content))
|
||||
{
|
||||
var result = await response.Content.ReadFromJsonAsync<TResult>();
|
||||
return result;
|
||||
@ -172,7 +174,7 @@ namespace Oqtane.Services
|
||||
protected async Task PostAsync(string uri)
|
||||
{
|
||||
var response = await GetHttpClient().PostAsync(uri, null);
|
||||
CheckResponse(response);
|
||||
await CheckResponse(response, uri);
|
||||
}
|
||||
|
||||
protected async Task<T> PostJsonAsync<T>(string uri, T value)
|
||||
@ -183,7 +185,7 @@ namespace Oqtane.Services
|
||||
protected async Task<TResult> PostJsonAsync<TValue, TResult>(string uri, TValue value)
|
||||
{
|
||||
var response = await GetHttpClient().PostAsJsonAsync(uri, value);
|
||||
if (CheckResponse(response) && ValidateJsonContent(response.Content))
|
||||
if (await CheckResponse(response, uri) && ValidateJsonContent(response.Content))
|
||||
{
|
||||
var result = await response.Content.ReadFromJsonAsync<TResult>();
|
||||
return result;
|
||||
@ -195,18 +197,21 @@ namespace Oqtane.Services
|
||||
protected async Task DeleteAsync(string uri)
|
||||
{
|
||||
var response = await GetHttpClient().DeleteAsync(uri);
|
||||
CheckResponse(response);
|
||||
await CheckResponse(response, uri);
|
||||
}
|
||||
|
||||
private bool CheckResponse(HttpResponseMessage response)
|
||||
private async Task<bool> CheckResponse(HttpResponseMessage response, string uri)
|
||||
{
|
||||
if (response.IsSuccessStatusCode && uri.Contains("/api/") && !response.RequestMessage.RequestUri.AbsolutePath.Contains("/api/"))
|
||||
{
|
||||
await Log(uri, response.RequestMessage.Method.ToString(), response.StatusCode.ToString(), "Request {Uri} Not Mapped To An API Controller Method", uri);
|
||||
return false;
|
||||
}
|
||||
if (response.IsSuccessStatusCode) return true;
|
||||
if (response.StatusCode != HttpStatusCode.NoContent && response.StatusCode != HttpStatusCode.NotFound)
|
||||
{
|
||||
Console.WriteLine($"Request: {response.RequestMessage.RequestUri}");
|
||||
Console.WriteLine($"Response status: {response.StatusCode} {response.ReasonPhrase}");
|
||||
await Log(uri, response.RequestMessage.Method.ToString(), response.StatusCode.ToString(), "Request {Uri} Failed With Status {StatusCode} - {ReasonPhrase}", uri, response.StatusCode, response.ReasonPhrase);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -216,6 +221,51 @@ namespace Oqtane.Services
|
||||
return mediaType != null && mediaType.Equals("application/json", StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
private async Task Log(string uri, string method, string status, string message, params object[] args)
|
||||
{
|
||||
if (_siteState.Alias != null && !uri.StartsWith(CreateApiUrl("Log")))
|
||||
{
|
||||
var log = new Log();
|
||||
log.SiteId = _siteState.Alias.SiteId;
|
||||
log.PageId = null;
|
||||
log.ModuleId = null;
|
||||
log.UserId = null;
|
||||
log.Url = uri;
|
||||
log.Category = GetType().AssemblyQualifiedName;
|
||||
log.Feature = Utilities.GetTypeNameLastSegment(log.Category, 0);
|
||||
switch (method)
|
||||
{
|
||||
case "GET":
|
||||
log.Function = LogFunction.Read.ToString();
|
||||
break;
|
||||
case "POST":
|
||||
log.Function = LogFunction.Create.ToString();
|
||||
break;
|
||||
case "PUT":
|
||||
log.Function = LogFunction.Update.ToString();
|
||||
break;
|
||||
case "DELETE":
|
||||
log.Function = LogFunction.Delete.ToString();
|
||||
break;
|
||||
default:
|
||||
log.Function = LogFunction.Other.ToString();
|
||||
break;
|
||||
}
|
||||
if (status == "500")
|
||||
{
|
||||
log.Level = LogLevel.Error.ToString();
|
||||
}
|
||||
else
|
||||
{
|
||||
log.Level = LogLevel.Warning.ToString();
|
||||
}
|
||||
log.Message = message;
|
||||
log.MessageTemplate = "";
|
||||
log.Properties = JsonSerializer.Serialize(args);
|
||||
await PostJsonAsync(CreateApiUrl("Log"), log);
|
||||
}
|
||||
}
|
||||
|
||||
//[Obsolete("This constructor is obsolete. Use ServiceBase(HttpClient client, SiteState siteState) : base(http, siteState) {} instead.", false)]
|
||||
// This constructor is obsolete. Use ServiceBase(HttpClient client, SiteState siteState) : base(http, siteState) {} instead.
|
||||
protected ServiceBase(HttpClient client)
|
||||
|
@ -6,7 +6,7 @@
|
||||
{
|
||||
<div class="app-moduleactions py-2 px-3">
|
||||
<a class="nav-link dropdown-toggle" data-bs-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false"></a>
|
||||
<ul class="dropdown-menu" x-placement="bottom-start" style="position: absolute; will-change: transform; top: 0px; left: 0px; transform: translate3d(0px, 37px, 0px);">
|
||||
<ul class="dropdown-menu" x-placement="bottom-start" style="position: absolute; will-change: transform; top: 0px; left: 0px; transform: translate3d(0px, 37px, 0px);" role="button">
|
||||
@foreach (var action in Actions.Where(item => !item.Name.Contains("Pane")))
|
||||
{
|
||||
if (string.IsNullOrEmpty(action.Name))
|
||||
|
@ -139,11 +139,11 @@ namespace Oqtane.Themes.Controls
|
||||
var permissions = pagemodule.Module.PermissionList;
|
||||
if (!permissions.Any(item => item.PermissionName == PermissionNames.View && item.RoleName == RoleNames.Everyone))
|
||||
{
|
||||
permissions.Add(new Permission(ModuleState.SiteId, EntityNames.Page, pagemodule.PageId, PermissionNames.View, RoleNames.Everyone, null, true));
|
||||
permissions.Add(new Permission(ModuleState.SiteId, EntityNames.Module, pagemodule.ModuleId, PermissionNames.View, RoleNames.Everyone, null, true));
|
||||
}
|
||||
if (!permissions.Any(item => item.PermissionName == PermissionNames.View && item.RoleName == RoleNames.Registered))
|
||||
{
|
||||
permissions.Add(new Permission(ModuleState.SiteId, EntityNames.Page, pagemodule.PageId, PermissionNames.View, RoleNames.Registered, null, true));
|
||||
permissions.Add(new Permission(ModuleState.SiteId, EntityNames.Module, pagemodule.ModuleId, PermissionNames.View, RoleNames.Registered, null, true));
|
||||
}
|
||||
pagemodule.Module.PermissionList = permissions;
|
||||
await ModuleService.UpdateModuleAsync(pagemodule.Module);
|
||||
|
@ -36,7 +36,7 @@
|
||||
|
||||
@if (_canViewAdminDashboard || UserSecurity.IsAuthorized(PageState.User, PermissionNames.Edit, PageState.Page.PermissionList))
|
||||
{
|
||||
<button type="button" class="btn @ButtonClass" data-bs-toggle="offcanvas" data-bs-target="#offcanvasControlPanel" aria-controls="offcanvasControlPanel">
|
||||
<button type="button" class="btn @ButtonClass ms-1" data-bs-toggle="offcanvas" data-bs-target="#offcanvasControlPanel" aria-controls="offcanvasControlPanel">
|
||||
<span class="oi oi-cog"></span>
|
||||
</button>
|
||||
|
||||
@ -124,7 +124,7 @@
|
||||
{
|
||||
@if (_moduleDefinitions != null)
|
||||
{
|
||||
<select class="form-select" @onchange="(e => CategoryChanged(e))">
|
||||
<select class="form-select mt-1" @onchange="(e => CategoryChanged(e))">
|
||||
@foreach (var category in _categories)
|
||||
{
|
||||
if (category == Category)
|
||||
@ -137,7 +137,7 @@
|
||||
}
|
||||
}
|
||||
</select>
|
||||
<select class="form-select" @onchange="(e => ModuleChanged(e))">
|
||||
<select class="form-select mt-1" @onchange="(e => ModuleChanged(e))">
|
||||
@if (ModuleDefinitionName == "-")
|
||||
{
|
||||
<option value="-" selected><@Localizer["Module.Select"]></option>
|
||||
@ -161,14 +161,14 @@
|
||||
}
|
||||
else
|
||||
{
|
||||
<select class="form-select" @onchange="(e => PageChanged(e))">
|
||||
<select class="form-select mt-1" @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">
|
||||
<select class="form-select mt-1" @bind="@ModuleId">
|
||||
<option value="-"><@Localizer["Module.Select"]></option>
|
||||
@foreach (Module module in _modules)
|
||||
{
|
||||
@ -471,6 +471,12 @@
|
||||
|
||||
private async Task ToggleEditMode(bool EditMode)
|
||||
{
|
||||
Page page = null;
|
||||
if (PageState.Page.IsPersonalizable && PageState.User != null && UserSecurity.IsAuthorized(PageState.User, RoleNames.Registered))
|
||||
{
|
||||
page = await PageService.AddPageAsync(PageState.Page.PageId, PageState.User.UserId);
|
||||
}
|
||||
|
||||
if (_showEditMode)
|
||||
{
|
||||
if (EditMode)
|
||||
@ -490,9 +496,8 @@
|
||||
}
|
||||
else
|
||||
{
|
||||
if (PageState.Page.IsPersonalizable && PageState.User != null)
|
||||
if (PageState.Page.IsPersonalizable && PageState.User != null && UserSecurity.IsAuthorized(PageState.User, RoleNames.Registered))
|
||||
{
|
||||
var page = await PageService.AddPageAsync(PageState.Page.PageId, PageState.User.UserId);
|
||||
PageState.EditMode = true;
|
||||
NavigationManager.NavigateTo(NavigateUrl(page.Path, "edit=" + ((PageState.EditMode) ? "true" : "false")));
|
||||
}
|
||||
@ -545,13 +550,29 @@
|
||||
if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.Edit, PageState.Page.PermissionList))
|
||||
{
|
||||
var permissions = PageState.Page.PermissionList;
|
||||
if (!permissions.Any(item => item.PermissionName == PermissionNames.View && item.RoleName == RoleNames.Everyone))
|
||||
switch (action)
|
||||
{
|
||||
permissions.Add(new Permission(PageState.Site.SiteId, EntityNames.Page, PageState.Page.PageId, PermissionNames.View, RoleNames.Everyone, null, true));
|
||||
}
|
||||
if (!permissions.Any(item => item.PermissionName == PermissionNames.View && item.RoleName == RoleNames.Registered))
|
||||
{
|
||||
permissions.Add(new Permission(PageState.Site.SiteId, EntityNames.Page, PageState.Page.PageId, PermissionNames.View, RoleNames.Registered, null, true));
|
||||
case "publish":
|
||||
if (!permissions.Any(item => item.PermissionName == PermissionNames.View && item.RoleName == RoleNames.Everyone))
|
||||
{
|
||||
permissions.Add(new Permission(PageState.Site.SiteId, EntityNames.Page, PageState.Page.PageId, PermissionNames.View, RoleNames.Everyone, null, true));
|
||||
}
|
||||
if (!permissions.Any(item => item.PermissionName == PermissionNames.View && item.RoleName == RoleNames.Registered))
|
||||
{
|
||||
permissions.Add(new Permission(PageState.Site.SiteId, EntityNames.Page, PageState.Page.PageId, PermissionNames.View, RoleNames.Registered, null, true));
|
||||
}
|
||||
break;
|
||||
case "unpublish":
|
||||
if (permissions.Any(item => item.PermissionName == PermissionNames.View && item.RoleName == RoleNames.Everyone))
|
||||
{
|
||||
permissions.RemoveAll(item => item.PermissionName == PermissionNames.View && item.RoleName == RoleNames.Everyone);
|
||||
}
|
||||
|
||||
if (permissions.Any(item => item.PermissionName == PermissionNames.View && item.RoleName == RoleNames.Registered))
|
||||
{
|
||||
permissions.RemoveAll(item => item.PermissionName == PermissionNames.View && item.RoleName == RoleNames.Registered);
|
||||
}
|
||||
break;
|
||||
}
|
||||
PageState.Page.PermissionList = permissions;
|
||||
await PageService.UpdatePageAsync(PageState.Page);
|
||||
|
@ -19,7 +19,6 @@ namespace Oqtane.Themes.Controls
|
||||
[Inject] public IUserService UserService { get; set; }
|
||||
[Inject] public IJSRuntime jsRuntime { get; set; }
|
||||
[Inject] public IServiceProvider ServiceProvider { get; set; }
|
||||
[Inject] public ILogService LoggingService { get; set; }
|
||||
|
||||
protected void LoginUser()
|
||||
{
|
||||
|
@ -1,6 +1,9 @@
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using Microsoft.JSInterop;
|
||||
using Oqtane.Enums;
|
||||
using Oqtane.Models;
|
||||
using Oqtane.Modules;
|
||||
using Oqtane.Services;
|
||||
using Oqtane.Shared;
|
||||
using Oqtane.UI;
|
||||
using System;
|
||||
@ -13,6 +16,9 @@ namespace Oqtane.Themes
|
||||
{
|
||||
public abstract class ThemeBase : ComponentBase, IThemeControl
|
||||
{
|
||||
[Inject]
|
||||
protected ILogService LoggingService { get; set; }
|
||||
|
||||
[Inject]
|
||||
protected IJSRuntime JSRuntime { get; set; }
|
||||
|
||||
@ -186,6 +192,148 @@ namespace Oqtane.Themes
|
||||
await interop.ScrollTo(0, 0, "smooth");
|
||||
}
|
||||
|
||||
// logging methods
|
||||
public async Task Log(Alias alias, LogLevel level, string function, Exception exception, string message, params object[] args)
|
||||
{
|
||||
LogFunction logFunction;
|
||||
if (string.IsNullOrEmpty(function))
|
||||
{
|
||||
// try to infer from page action
|
||||
function = PageState.Action;
|
||||
}
|
||||
if (!Enum.TryParse(function, out logFunction))
|
||||
{
|
||||
switch (function.ToLower())
|
||||
{
|
||||
case "add":
|
||||
logFunction = LogFunction.Create;
|
||||
break;
|
||||
case "edit":
|
||||
logFunction = LogFunction.Update;
|
||||
break;
|
||||
case "delete":
|
||||
logFunction = LogFunction.Delete;
|
||||
break;
|
||||
case "":
|
||||
logFunction = LogFunction.Read;
|
||||
break;
|
||||
default:
|
||||
logFunction = LogFunction.Other;
|
||||
break;
|
||||
}
|
||||
}
|
||||
await Log(alias, level, logFunction, exception, message, args);
|
||||
}
|
||||
|
||||
public async Task Log(Alias alias, LogLevel level, LogFunction function, Exception exception, string message, params object[] args)
|
||||
{
|
||||
int pageId = PageState.Page.PageId;
|
||||
string category = GetType().AssemblyQualifiedName;
|
||||
string feature = Utilities.GetTypeNameLastSegment(category, 1);
|
||||
|
||||
await LoggingService.Log(alias, pageId, null, PageState.User?.UserId, category, feature, function, level, exception, message, args);
|
||||
}
|
||||
|
||||
public class Logger
|
||||
{
|
||||
private readonly ModuleBase _moduleBase;
|
||||
|
||||
public Logger(ModuleBase moduleBase)
|
||||
{
|
||||
_moduleBase = moduleBase;
|
||||
}
|
||||
|
||||
public async Task LogTrace(string message, params object[] args)
|
||||
{
|
||||
await _moduleBase.Log(null, LogLevel.Trace, "", null, message, args);
|
||||
}
|
||||
|
||||
public async Task LogTrace(LogFunction function, string message, params object[] args)
|
||||
{
|
||||
await _moduleBase.Log(null, LogLevel.Trace, function, null, message, args);
|
||||
}
|
||||
|
||||
public async Task LogTrace(Exception exception, string message, params object[] args)
|
||||
{
|
||||
await _moduleBase.Log(null, LogLevel.Trace, "", exception, message, args);
|
||||
}
|
||||
|
||||
public async Task LogDebug(string message, params object[] args)
|
||||
{
|
||||
await _moduleBase.Log(null, LogLevel.Debug, "", null, message, args);
|
||||
}
|
||||
|
||||
public async Task LogDebug(LogFunction function, string message, params object[] args)
|
||||
{
|
||||
await _moduleBase.Log(null, LogLevel.Debug, function, null, message, args);
|
||||
}
|
||||
|
||||
public async Task LogDebug(Exception exception, string message, params object[] args)
|
||||
{
|
||||
await _moduleBase.Log(null, LogLevel.Debug, "", exception, message, args);
|
||||
}
|
||||
|
||||
public async Task LogInformation(string message, params object[] args)
|
||||
{
|
||||
await _moduleBase.Log(null, LogLevel.Information, "", null, message, args);
|
||||
}
|
||||
|
||||
public async Task LogInformation(LogFunction function, string message, params object[] args)
|
||||
{
|
||||
await _moduleBase.Log(null, LogLevel.Information, function, null, message, args);
|
||||
}
|
||||
|
||||
public async Task LogInformation(Exception exception, string message, params object[] args)
|
||||
{
|
||||
await _moduleBase.Log(null, LogLevel.Information, "", exception, message, args);
|
||||
}
|
||||
|
||||
public async Task LogWarning(string message, params object[] args)
|
||||
{
|
||||
await _moduleBase.Log(null, LogLevel.Warning, "", null, message, args);
|
||||
}
|
||||
|
||||
public async Task LogWarning(LogFunction function, string message, params object[] args)
|
||||
{
|
||||
await _moduleBase.Log(null, LogLevel.Warning, function, null, message, args);
|
||||
}
|
||||
|
||||
public async Task LogWarning(Exception exception, string message, params object[] args)
|
||||
{
|
||||
await _moduleBase.Log(null, LogLevel.Warning, "", exception, message, args);
|
||||
}
|
||||
|
||||
public async Task LogError(string message, params object[] args)
|
||||
{
|
||||
await _moduleBase.Log(null, LogLevel.Error, "", null, message, args);
|
||||
}
|
||||
|
||||
public async Task LogError(LogFunction function, string message, params object[] args)
|
||||
{
|
||||
await _moduleBase.Log(null, LogLevel.Error, function, null, message, args);
|
||||
}
|
||||
|
||||
public async Task LogError(Exception exception, string message, params object[] args)
|
||||
{
|
||||
await _moduleBase.Log(null, LogLevel.Error, "", exception, message, args);
|
||||
}
|
||||
|
||||
public async Task LogCritical(string message, params object[] args)
|
||||
{
|
||||
await _moduleBase.Log(null, LogLevel.Critical, "", null, message, args);
|
||||
}
|
||||
|
||||
public async Task LogCritical(LogFunction function, string message, params object[] args)
|
||||
{
|
||||
await _moduleBase.Log(null, LogLevel.Critical, function, null, message, args);
|
||||
}
|
||||
|
||||
public async Task LogCritical(Exception exception, string message, params object[] 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)
|
||||
{
|
||||
|
@ -9,12 +9,12 @@
|
||||
@if (_useadminborder)
|
||||
{
|
||||
<div class="app-pane-admin-border">
|
||||
@DynamicComponent
|
||||
<DynamicComponent Type="@ComponentType"></DynamicComponent>
|
||||
</div>
|
||||
}
|
||||
else
|
||||
{
|
||||
@DynamicComponent
|
||||
<DynamicComponent Type="@ComponentType"></DynamicComponent>
|
||||
}
|
||||
</CascadingValue>
|
||||
|
||||
@ -23,6 +23,7 @@
|
||||
@code {
|
||||
private bool _visible = true;
|
||||
private bool _useadminborder = false;
|
||||
public Type ComponentType { get; set; }
|
||||
|
||||
[CascadingParameter]
|
||||
protected PageState PageState { get; set; }
|
||||
@ -30,8 +31,6 @@
|
||||
[Parameter]
|
||||
public Module ModuleState { get; set; }
|
||||
|
||||
RenderFragment DynamicComponent { get; set; }
|
||||
|
||||
protected override void OnInitialized()
|
||||
{
|
||||
((INotifyPropertyChanged)SiteState.Properties).PropertyChanged += PropertyChanged;
|
||||
@ -54,17 +53,7 @@
|
||||
_useadminborder = false;
|
||||
}
|
||||
|
||||
DynamicComponent = builder =>
|
||||
{
|
||||
Type containerType = Type.GetType(container);
|
||||
if (containerType == null)
|
||||
{
|
||||
// fallback
|
||||
containerType = Type.GetType(Constants.DefaultContainer);
|
||||
}
|
||||
builder.OpenComponent(0, containerType);
|
||||
builder.CloseComponent();
|
||||
};
|
||||
ComponentType = Type.GetType(container) ?? Type.GetType(Constants.DefaultContainer);
|
||||
}
|
||||
|
||||
private void PropertyChanged(object sender, PropertyChangedEventArgs e)
|
||||
@ -83,4 +72,6 @@
|
||||
{
|
||||
((INotifyPropertyChanged)SiteState.Properties).PropertyChanged -= PropertyChanged;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
@ -223,12 +223,12 @@
|
||||
}
|
||||
if (page == null)
|
||||
{
|
||||
// look for personalized page
|
||||
page = await PageService.GetPageAsync(route.PagePath, site.SiteId);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (user != null && page.IsPersonalizable)
|
||||
// look for personalized page
|
||||
if (user != null && page.IsPersonalizable && !UserSecurity.IsAuthorized(user, PermissionNames.Edit, page.PermissionList))
|
||||
{
|
||||
var personalized = await PageService.GetPageAsync(route.PagePath + "/" + user.Username, site.SiteId);
|
||||
if (personalized != null)
|
||||
|
@ -26,7 +26,7 @@
|
||||
}
|
||||
else
|
||||
{
|
||||
SiteState.Properties.PageTitle = PageState.Site.Name + " - " + PageState.Page.Name;
|
||||
SiteState.Properties.PageTitle = PageState.Page.Name + " - " + PageState.Site.Name;
|
||||
}
|
||||
|
||||
// set page head content
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<Version>4.0.0</Version>
|
||||
<Version>4.0.1</Version>
|
||||
<Product>Oqtane</Product>
|
||||
<Authors>Shaun Walker</Authors>
|
||||
<Company>.NET Foundation</Company>
|
||||
@ -10,7 +10,7 @@
|
||||
<Copyright>.NET Foundation</Copyright>
|
||||
<PackageProjectUrl>https://www.oqtane.org</PackageProjectUrl>
|
||||
<PackageLicenseUrl>https://github.com/oqtane/oqtane.framework/blob/dev/LICENSE</PackageLicenseUrl>
|
||||
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v4.0.0</PackageReleaseNotes>
|
||||
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v4.0.1</PackageReleaseNotes>
|
||||
<RepositoryUrl>https://github.com/oqtane/oqtane.framework</RepositoryUrl>
|
||||
<RepositoryType>Git</RepositoryType>
|
||||
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
|
||||
|
@ -2,7 +2,7 @@
|
||||
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
|
||||
<metadata>
|
||||
<id>Oqtane.Database.MySQL</id>
|
||||
<version>4.0.0</version>
|
||||
<version>4.0.1</version>
|
||||
<authors>Shaun Walker</authors>
|
||||
<owners>.NET Foundation</owners>
|
||||
<title>Oqtane MySQL Provider</title>
|
||||
@ -12,7 +12,7 @@
|
||||
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
||||
<license type="expression">MIT</license>
|
||||
<projectUrl>https://github.com/oqtane/oqtane.framework</projectUrl>
|
||||
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v4.0.0</releaseNotes>
|
||||
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v4.0.1</releaseNotes>
|
||||
<icon>icon.png</icon>
|
||||
<tags>oqtane</tags>
|
||||
</metadata>
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<Version>4.0.0</Version>
|
||||
<Version>4.0.1</Version>
|
||||
<Product>Oqtane</Product>
|
||||
<Authors>Shaun Walker</Authors>
|
||||
<Company>.NET Foundation</Company>
|
||||
@ -10,7 +10,7 @@
|
||||
<Copyright>.NET Foundation</Copyright>
|
||||
<PackageProjectUrl>https://www.oqtane.org</PackageProjectUrl>
|
||||
<PackageLicenseUrl>https://github.com/oqtane/oqtane.framework/blob/dev/LICENSE</PackageLicenseUrl>
|
||||
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v4.0.0</PackageReleaseNotes>
|
||||
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v4.0.1</PackageReleaseNotes>
|
||||
<RepositoryUrl>https://github.com/oqtane/oqtane.framework</RepositoryUrl>
|
||||
<RepositoryType>Git</RepositoryType>
|
||||
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
|
||||
|
@ -2,7 +2,7 @@
|
||||
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
|
||||
<metadata>
|
||||
<id>Oqtane.Database.PostgreSQL</id>
|
||||
<version>4.0.0</version>
|
||||
<version>4.0.1</version>
|
||||
<authors>Shaun Walker</authors>
|
||||
<owners>.NET Foundation</owners>
|
||||
<title>Oqtane PostgreSQL Provider</title>
|
||||
@ -12,7 +12,7 @@
|
||||
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
||||
<license type="expression">MIT</license>
|
||||
<projectUrl>https://github.com/oqtane/oqtane.framework</projectUrl>
|
||||
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v4.0.0</releaseNotes>
|
||||
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v4.0.1</releaseNotes>
|
||||
<icon>icon.png</icon>
|
||||
<tags>oqtane</tags>
|
||||
</metadata>
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<Version>4.0.0</Version>
|
||||
<Version>4.0.1</Version>
|
||||
<Product>Oqtane</Product>
|
||||
<Authors>Shaun Walker</Authors>
|
||||
<Company>.NET Foundation</Company>
|
||||
@ -10,7 +10,7 @@
|
||||
<Copyright>.NET Foundation</Copyright>
|
||||
<PackageProjectUrl>https://www.oqtane.org</PackageProjectUrl>
|
||||
<PackageLicenseUrl>https://github.com/oqtane/oqtane.framework/blob/dev/LICENSE</PackageLicenseUrl>
|
||||
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v4.0.0</PackageReleaseNotes>
|
||||
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v4.0.1</PackageReleaseNotes>
|
||||
<RepositoryUrl>https://github.com/oqtane/oqtane.framework</RepositoryUrl>
|
||||
<RepositoryType>Git</RepositoryType>
|
||||
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
|
||||
|
@ -2,7 +2,7 @@
|
||||
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
|
||||
<metadata>
|
||||
<id>Oqtane.Database.SqlServer</id>
|
||||
<version>4.0.0</version>
|
||||
<version>4.0.1</version>
|
||||
<authors>Shaun Walker</authors>
|
||||
<owners>.NET Foundation</owners>
|
||||
<title>Oqtane SQL Server Provider</title>
|
||||
@ -12,7 +12,7 @@
|
||||
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
||||
<license type="expression">MIT</license>
|
||||
<projectUrl>https://github.com/oqtane/oqtane.framework</projectUrl>
|
||||
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v4.0.0</releaseNotes>
|
||||
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v4.0.1</releaseNotes>
|
||||
<icon>icon.png</icon>
|
||||
<tags>oqtane</tags>
|
||||
</metadata>
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<Version>4.0.0</Version>
|
||||
<Version>4.0.1</Version>
|
||||
<Product>Oqtane</Product>
|
||||
<Authors>Shaun Walker</Authors>
|
||||
<Company>.NET Foundation</Company>
|
||||
@ -10,7 +10,7 @@
|
||||
<Copyright>.NET Foundation</Copyright>
|
||||
<PackageProjectUrl>https://www.oqtane.org</PackageProjectUrl>
|
||||
<PackageLicenseUrl>https://github.com/oqtane/oqtane.framework/blob/dev/LICENSE</PackageLicenseUrl>
|
||||
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v4.0.0</PackageReleaseNotes>
|
||||
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v4.0.1</PackageReleaseNotes>
|
||||
<RepositoryUrl>https://github.com/oqtane/oqtane.framework</RepositoryUrl>
|
||||
<RepositoryType>Git</RepositoryType>
|
||||
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
|
||||
|
@ -2,7 +2,7 @@
|
||||
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
|
||||
<metadata>
|
||||
<id>Oqtane.Database.Sqlite</id>
|
||||
<version>4.0.0</version>
|
||||
<version>4.0.1</version>
|
||||
<authors>Shaun Walker</authors>
|
||||
<owners>.NET Foundation</owners>
|
||||
<title>Oqtane SQLite Provider</title>
|
||||
@ -12,7 +12,7 @@
|
||||
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
||||
<license type="expression">MIT</license>
|
||||
<projectUrl>https://github.com/oqtane/oqtane.framework</projectUrl>
|
||||
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v4.0.0</releaseNotes>
|
||||
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v4.0.1</releaseNotes>
|
||||
<icon>icon.png</icon>
|
||||
<tags>oqtane</tags>
|
||||
</metadata>
|
||||
|
@ -6,7 +6,7 @@
|
||||
<!-- <TargetFrameworks>net7.0-android;net7.0-ios;net7.0-maccatalyst</TargetFrameworks> -->
|
||||
<!-- <TargetFrameworks>$(TargetFrameworks);net7.0-tizen</TargetFrameworks> -->
|
||||
<OutputType>Exe</OutputType>
|
||||
<Version>4.0.0</Version>
|
||||
<Version>4.0.1</Version>
|
||||
<Product>Oqtane</Product>
|
||||
<Authors>Shaun Walker</Authors>
|
||||
<Company>.NET Foundation</Company>
|
||||
@ -14,7 +14,7 @@
|
||||
<Copyright>.NET Foundation</Copyright>
|
||||
<PackageProjectUrl>https://www.oqtane.org</PackageProjectUrl>
|
||||
<PackageLicenseUrl>https://github.com/oqtane/oqtane.framework/blob/dev/LICENSE</PackageLicenseUrl>
|
||||
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v4.0.0</PackageReleaseNotes>
|
||||
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v4.0.1</PackageReleaseNotes>
|
||||
<RepositoryUrl>https://github.com/oqtane/oqtane.framework</RepositoryUrl>
|
||||
<RepositoryType>Git</RepositoryType>
|
||||
<RootNamespace>Oqtane.Maui</RootNamespace>
|
||||
@ -31,7 +31,7 @@
|
||||
<ApplicationIdGuid>0E29FC31-1B83-48ED-B6E0-9F3C67B775D4</ApplicationIdGuid>
|
||||
|
||||
<!-- Versions -->
|
||||
<ApplicationDisplayVersion>4.0.0</ApplicationDisplayVersion>
|
||||
<ApplicationDisplayVersion>4.0.1</ApplicationDisplayVersion>
|
||||
<ApplicationVersion>1</ApplicationVersion>
|
||||
|
||||
<SupportedOSPlatformVersion Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'ios'">14.2</SupportedOSPlatformVersion>
|
||||
|
@ -2,7 +2,7 @@
|
||||
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
|
||||
<metadata>
|
||||
<id>Oqtane.Client</id>
|
||||
<version>4.0.0</version>
|
||||
<version>4.0.1</version>
|
||||
<authors>Shaun Walker</authors>
|
||||
<owners>.NET Foundation</owners>
|
||||
<title>Oqtane Framework</title>
|
||||
@ -12,7 +12,7 @@
|
||||
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
||||
<license type="expression">MIT</license>
|
||||
<projectUrl>https://github.com/oqtane/oqtane.framework</projectUrl>
|
||||
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v4.0.0</releaseNotes>
|
||||
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v4.0.1</releaseNotes>
|
||||
<icon>icon.png</icon>
|
||||
<tags>oqtane</tags>
|
||||
</metadata>
|
||||
|
@ -2,7 +2,7 @@
|
||||
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
|
||||
<metadata>
|
||||
<id>Oqtane.Framework</id>
|
||||
<version>4.0.0</version>
|
||||
<version>4.0.1</version>
|
||||
<authors>Shaun Walker</authors>
|
||||
<owners>.NET Foundation</owners>
|
||||
<title>Oqtane Framework</title>
|
||||
@ -11,8 +11,8 @@
|
||||
<copyright>.NET Foundation</copyright>
|
||||
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
||||
<license type="expression">MIT</license>
|
||||
<projectUrl>https://github.com/oqtane/oqtane.framework/releases/download/v4.0.0/Oqtane.Framework.4.0.0.Upgrade.zip</projectUrl>
|
||||
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v4.0.0</releaseNotes>
|
||||
<projectUrl>https://github.com/oqtane/oqtane.framework/releases/download/v4.0.1/Oqtane.Framework.4.0.1.Upgrade.zip</projectUrl>
|
||||
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v4.0.1</releaseNotes>
|
||||
<icon>icon.png</icon>
|
||||
<tags>oqtane framework</tags>
|
||||
</metadata>
|
||||
|
@ -2,7 +2,7 @@
|
||||
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
|
||||
<metadata>
|
||||
<id>Oqtane.Server</id>
|
||||
<version>4.0.0</version>
|
||||
<version>4.0.1</version>
|
||||
<authors>Shaun Walker</authors>
|
||||
<owners>.NET Foundation</owners>
|
||||
<title>Oqtane Framework</title>
|
||||
@ -12,7 +12,7 @@
|
||||
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
||||
<license type="expression">MIT</license>
|
||||
<projectUrl>https://github.com/oqtane/oqtane.framework</projectUrl>
|
||||
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v4.0.0</releaseNotes>
|
||||
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v4.0.1</releaseNotes>
|
||||
<icon>icon.png</icon>
|
||||
<tags>oqtane</tags>
|
||||
</metadata>
|
||||
|
@ -2,7 +2,7 @@
|
||||
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
|
||||
<metadata>
|
||||
<id>Oqtane.Shared</id>
|
||||
<version>4.0.0</version>
|
||||
<version>4.0.1</version>
|
||||
<authors>Shaun Walker</authors>
|
||||
<owners>.NET Foundation</owners>
|
||||
<title>Oqtane Framework</title>
|
||||
@ -12,7 +12,7 @@
|
||||
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
||||
<license type="expression">MIT</license>
|
||||
<projectUrl>https://github.com/oqtane/oqtane.framework</projectUrl>
|
||||
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v4.0.0</releaseNotes>
|
||||
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v4.0.1</releaseNotes>
|
||||
<icon>icon.png</icon>
|
||||
<tags>oqtane</tags>
|
||||
</metadata>
|
||||
|
@ -2,7 +2,7 @@
|
||||
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
|
||||
<metadata>
|
||||
<id>Oqtane.Updater</id>
|
||||
<version>4.0.0</version>
|
||||
<version>4.0.1</version>
|
||||
<authors>Shaun Walker</authors>
|
||||
<owners>.NET Foundation</owners>
|
||||
<title>Oqtane Framework</title>
|
||||
@ -12,7 +12,7 @@
|
||||
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
||||
<license type="expression">MIT</license>
|
||||
<projectUrl>https://github.com/oqtane/oqtane.framework</projectUrl>
|
||||
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v4.0.0</releaseNotes>
|
||||
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v4.0.1</releaseNotes>
|
||||
<icon>icon.png</icon>
|
||||
<tags>oqtane</tags>
|
||||
</metadata>
|
||||
|
@ -1 +1 @@
|
||||
Compress-Archive -Path "..\Oqtane.Server\bin\Release\net7.0\publish\*" -DestinationPath "Oqtane.Framework.4.0.0.Install.zip" -Force
|
||||
Compress-Archive -Path "..\Oqtane.Server\bin\Release\net7.0\publish\*" -DestinationPath "Oqtane.Framework.4.0.1.Install.zip" -Force
|
@ -1 +1 @@
|
||||
Compress-Archive -Path "..\Oqtane.Server\bin\Release\net7.0\publish\*" -DestinationPath "Oqtane.Framework.4.0.0.Upgrade.zip" -Force
|
||||
Compress-Archive -Path "..\Oqtane.Server\bin\Release\net7.0\publish\*" -DestinationPath "Oqtane.Framework.4.0.1.Upgrade.zip" -Force
|
@ -123,8 +123,38 @@ namespace Oqtane.Controllers
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized File Get Attempt {FileId}", id);
|
||||
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
|
||||
if (file != null)
|
||||
{
|
||||
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized File Get Attempt {FileId}", id);
|
||||
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
|
||||
}
|
||||
else
|
||||
{
|
||||
HttpContext.Response.StatusCode = (int)HttpStatusCode.NotFound;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
[HttpGet("name/{name}/{folderId}")]
|
||||
public Models.File Get(string name, int folderId)
|
||||
{
|
||||
Models.File file = _files.GetFile(folderId, name);
|
||||
if (file != null && file.Folder.SiteId == _alias.SiteId && _userPermissions.IsAuthorized(User, PermissionNames.View, file.Folder.PermissionList))
|
||||
{
|
||||
return file;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (file != null)
|
||||
{
|
||||
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized File Get Attempt {Name} For Folder {FolderId}", name, folderId);
|
||||
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
|
||||
}
|
||||
else
|
||||
{
|
||||
HttpContext.Response.StatusCode = (int)HttpStatusCode.NotFound;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
@ -43,7 +43,7 @@ namespace Oqtane.Controllers
|
||||
{
|
||||
foreach (Folder folder in _folders.GetFolders(SiteId))
|
||||
{
|
||||
if (_userPermissions.IsAuthorized(User, PermissionNames.Browse, folder.PermissionList))
|
||||
if (_userPermissions.IsAuthorized(User, PermissionNames.View, folder.PermissionList))
|
||||
{
|
||||
folders.Add(folder);
|
||||
}
|
||||
@ -64,14 +64,21 @@ namespace Oqtane.Controllers
|
||||
public Folder Get(int id)
|
||||
{
|
||||
Folder folder = _folders.GetFolder(id);
|
||||
if (folder != null && folder.SiteId == _alias.SiteId && _userPermissions.IsAuthorized(User, PermissionNames.Browse, folder.PermissionList))
|
||||
if (folder != null && folder.SiteId == _alias.SiteId && _userPermissions.IsAuthorized(User, PermissionNames.View, folder.PermissionList))
|
||||
{
|
||||
return folder;
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized Folder Get Attempt {FolderId}", id);
|
||||
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
|
||||
if (folder != null)
|
||||
{
|
||||
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized Folder Get Attempt {FolderId}", id);
|
||||
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
|
||||
}
|
||||
else
|
||||
{
|
||||
HttpContext.Response.StatusCode = (int)HttpStatusCode.NotFound;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@ -80,19 +87,26 @@ namespace Oqtane.Controllers
|
||||
public Folder GetByPath(int siteId, string path)
|
||||
{
|
||||
var folderPath = WebUtility.UrlDecode(path).Replace("\\", "/");
|
||||
if (!folderPath.EndsWith("/"))
|
||||
if (!folderPath.EndsWith("/") && folderPath != "")
|
||||
{
|
||||
folderPath += "/";
|
||||
}
|
||||
Folder folder = _folders.GetFolder(siteId, folderPath);
|
||||
if (folder != null && folder.SiteId == _alias.SiteId && _userPermissions.IsAuthorized(User, PermissionNames.Browse, folder.PermissionList))
|
||||
if (folder != null && folder.SiteId == _alias.SiteId && _userPermissions.IsAuthorized(User, PermissionNames.View, folder.PermissionList))
|
||||
{
|
||||
return folder;
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized Folder Get Attempt {Path} For Site {SiteId}", path, siteId);
|
||||
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
|
||||
if (folder != null)
|
||||
{
|
||||
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized Folder Get Attempt {Path} For Site {SiteId}", path, siteId);
|
||||
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
|
||||
}
|
||||
else
|
||||
{
|
||||
HttpContext.Response.StatusCode = (int)HttpStatusCode.NotFound;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@ -124,7 +138,10 @@ namespace Oqtane.Controllers
|
||||
Folder parent = _folders.GetFolder(folder.ParentId.Value);
|
||||
folder.Path = Utilities.UrlCombine(parent.Path, folder.Name);
|
||||
}
|
||||
folder.Path = folder.Path + "/";
|
||||
if (!folder.Path.EndsWith("/"))
|
||||
{
|
||||
folder.Path = folder.Path + "/";
|
||||
}
|
||||
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);
|
||||
@ -166,7 +183,10 @@ namespace Oqtane.Controllers
|
||||
Folder parent = _folders.GetFolder(folder.ParentId.Value);
|
||||
folder.Path = Utilities.UrlCombine(parent.Path, folder.Name);
|
||||
}
|
||||
folder.Path = folder.Path + "/";
|
||||
if (!folder.Path.EndsWith("/"))
|
||||
{
|
||||
folder.Path = folder.Path + "/";
|
||||
}
|
||||
|
||||
Folder _folder = _folders.GetFolder(id, false);
|
||||
if (_folder.Path != folder.Path && Directory.Exists(_folders.GetFolderPath(_folder)))
|
||||
|
@ -89,8 +89,15 @@ namespace Oqtane.Controllers
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized Language Get Attempt {LanguageId}", id);
|
||||
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
|
||||
if (language != null)
|
||||
{
|
||||
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized Language Get Attempt {LanguageId}", id);
|
||||
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
|
||||
}
|
||||
else
|
||||
{
|
||||
HttpContext.Response.StatusCode = (int)HttpStatusCode.NotFound;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
@ -113,8 +113,15 @@ namespace Oqtane.Controllers
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized Module Get Attempt {ModuleId}", id);
|
||||
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
|
||||
if (module != null)
|
||||
{
|
||||
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized Module Get Attempt {ModuleId}", id);
|
||||
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
|
||||
}
|
||||
else
|
||||
{
|
||||
HttpContext.Response.StatusCode = (int)HttpStatusCode.NotFound;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
@ -89,15 +89,22 @@ namespace Oqtane.Controllers
|
||||
if (int.TryParse(siteid, out SiteId) && SiteId == _alias.SiteId)
|
||||
{
|
||||
ModuleDefinition moduledefinition = _moduleDefinitions.GetModuleDefinition(id, SiteId);
|
||||
if (_userPermissions.IsAuthorized(User, PermissionNames.Utilize, moduledefinition.PermissionList))
|
||||
if (moduledefinition != null && _userPermissions.IsAuthorized(User, PermissionNames.Utilize, moduledefinition.PermissionList))
|
||||
{
|
||||
if (string.IsNullOrEmpty(moduledefinition.Version)) moduledefinition.Version = new Version(1, 0, 0).ToString();
|
||||
moduledefinition.Version = (string.IsNullOrEmpty(moduledefinition.Version)) ? new Version(1, 0, 0).ToString() : moduledefinition.Version;
|
||||
return moduledefinition;
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized ModuleDefinition Get Attempt {ModuleDefinitionId} {SiteId}", id, siteid);
|
||||
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
|
||||
if (moduledefinition != null)
|
||||
{
|
||||
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized ModuleDefinition Get Attempt {ModuleDefinitionId} {SiteId}", id, siteid);
|
||||
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
|
||||
}
|
||||
else
|
||||
{
|
||||
HttpContext.Response.StatusCode = (int)HttpStatusCode.NotFound;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
@ -9,6 +9,9 @@ using Oqtane.Repository;
|
||||
using Oqtane.Security;
|
||||
using System.Net;
|
||||
using System.Reflection.Metadata;
|
||||
using Microsoft.Extensions.Localization;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
using System.Linq;
|
||||
|
||||
namespace Oqtane.Controllers
|
||||
{
|
||||
@ -30,6 +33,72 @@ namespace Oqtane.Controllers
|
||||
_alias = tenantManager.GetAlias();
|
||||
}
|
||||
|
||||
// GET: api/<controller>/read?siteid=x&direction=to&userid=1&count=5&isread=false
|
||||
[HttpGet("read")]
|
||||
[Authorize(Roles = RoleNames.Registered)]
|
||||
public IEnumerable<Notification> Get(string siteid, string direction, string userid, string count, string isread)
|
||||
{
|
||||
IEnumerable<Notification> notifications = null;
|
||||
|
||||
int SiteId;
|
||||
int UserId;
|
||||
int Count;
|
||||
bool IsRead;
|
||||
if (int.TryParse(siteid, out SiteId) && SiteId == _alias.SiteId && int.TryParse(userid, out UserId) && int.TryParse(count, out Count) && bool.TryParse(isread, out IsRead) && IsAuthorized(UserId))
|
||||
{
|
||||
if (direction == "to")
|
||||
{
|
||||
notifications = _notifications.GetNotifications(SiteId, -1, UserId, Count, IsRead);
|
||||
}
|
||||
else
|
||||
{
|
||||
notifications = _notifications.GetNotifications(SiteId, UserId, -1, Count, IsRead);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized Notification Get Attempt {SiteId} {Direction} {UserId} {Count} {isRead}", siteid, direction, userid, count, isread);
|
||||
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
|
||||
notifications = null;
|
||||
}
|
||||
|
||||
|
||||
return notifications;
|
||||
}
|
||||
|
||||
// GET: api/<controller>/read?siteid=x&direction=to&userid=1&count=5&isread=false
|
||||
[HttpGet("read-count")]
|
||||
[Authorize(Roles = RoleNames.Registered)]
|
||||
public int Get(string siteid, string direction, string userid, string isread)
|
||||
{
|
||||
int notificationsCount = 0;
|
||||
|
||||
int SiteId;
|
||||
int UserId;
|
||||
bool IsRead;
|
||||
if (int.TryParse(siteid, out SiteId) && SiteId == _alias.SiteId && int.TryParse(userid, out UserId) && bool.TryParse(isread, out IsRead) && IsAuthorized(UserId))
|
||||
{
|
||||
if (direction == "to")
|
||||
{
|
||||
notificationsCount = _notifications.GetNotificationCount(SiteId, -1, UserId, IsRead);
|
||||
}
|
||||
else
|
||||
{
|
||||
notificationsCount = _notifications.GetNotificationCount(SiteId, UserId, -1, IsRead);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized Notification Get Attempt {SiteId} {Direction} {UserId} {isRead}", siteid, direction, userid, isread);
|
||||
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
|
||||
notificationsCount = 0;
|
||||
}
|
||||
|
||||
|
||||
return notificationsCount;
|
||||
}
|
||||
|
||||
|
||||
// GET: api/<controller>?siteid=x&type=y&userid=z
|
||||
[HttpGet]
|
||||
[Authorize(Roles = RoleNames.Registered)]
|
||||
@ -72,8 +141,15 @@ namespace Oqtane.Controllers
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized Notification Get Attempt {NotificationId}", id);
|
||||
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
|
||||
if (notification != null)
|
||||
{
|
||||
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized Notification Get Attempt {NotificationId}", id);
|
||||
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
|
||||
}
|
||||
else
|
||||
{
|
||||
HttpContext.Response.StatusCode = (int)HttpStatusCode.NotFound;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
@ -34,7 +34,7 @@ namespace Oqtane.Controllers
|
||||
|
||||
// GET: api/<controller>?type=x&search=y&price=z&package=a
|
||||
[HttpGet]
|
||||
public async Task<IEnumerable<Package>> Get(string type, string search, string price, string package)
|
||||
public async Task<IEnumerable<Package>> Get(string type, string search, string price, string package, string sort)
|
||||
{
|
||||
// get packages
|
||||
List<Package> packages = new List<Package>();
|
||||
@ -44,8 +44,8 @@ namespace Oqtane.Controllers
|
||||
{
|
||||
client.DefaultRequestHeaders.Add("Referer", HttpContext.Request.Scheme + "://" + HttpContext.Request.Host.Value);
|
||||
client.DefaultRequestHeaders.UserAgent.Add(new ProductInfoHeaderValue(Constants.PackageId, Constants.Version));
|
||||
packages = await GetJson<List<Package>>(client, Constants.PackageRegistryUrl + $"/api/registry/packages/?id={_configManager.GetInstallationId()}&type={type.ToLower()}&version={Constants.Version}&search={search}&price={price}&package={package}");
|
||||
}
|
||||
packages = await GetJson<List<Package>>(client, Constants.PackageRegistryUrl + $"/api/registry/packages/?id={_configManager.GetInstallationId()}&type={type.ToLower()}&version={Constants.Version}&search={search}&price={price}&package={package}&sort={sort}");
|
||||
}
|
||||
}
|
||||
return packages;
|
||||
}
|
||||
|
@ -7,10 +7,8 @@ using System.Linq;
|
||||
using Oqtane.Security;
|
||||
using System.Net;
|
||||
using Oqtane.Enums;
|
||||
using Oqtane.Extensions;
|
||||
using Oqtane.Infrastructure;
|
||||
using Oqtane.Repository;
|
||||
using Oqtane.Modules.Admin.Users;
|
||||
using System.IO;
|
||||
|
||||
namespace Oqtane.Controllers
|
||||
@ -89,8 +87,15 @@ namespace Oqtane.Controllers
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized Page Get Attempt {PageId}", id);
|
||||
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
|
||||
if (page != null)
|
||||
{
|
||||
_logger.Log(LogLevel.Warning, this, LogFunction.Security, "Unauthorized Page Get Attempt {PageId}", id);
|
||||
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
|
||||
}
|
||||
else
|
||||
{
|
||||
HttpContext.Response.StatusCode = (int)HttpStatusCode.NotFound;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@ -109,8 +114,15 @@ namespace Oqtane.Controllers
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized Page Get Attempt {SiteId} {Path}", siteid, path);
|
||||
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
|
||||
if (page != null)
|
||||
{
|
||||
_logger.Log(LogLevel.Warning, this, LogFunction.Security, "Unauthorized Page Get Attempt {SiteId} {Path}", siteid, path);
|
||||
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
|
||||
}
|
||||
else
|
||||
{
|
||||
HttpContext.Response.StatusCode = (int)HttpStatusCode.NotFound;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@ -177,64 +189,68 @@ namespace Oqtane.Controllers
|
||||
User user = _userPermissions.GetUser(User);
|
||||
if (parent != null && parent.SiteId == _alias.SiteId && parent.IsPersonalizable && user.UserId == int.Parse(userid))
|
||||
{
|
||||
page = new Page();
|
||||
page.SiteId = parent.SiteId;
|
||||
page.ParentId = parent.PageId;
|
||||
page.Name = user.Username;
|
||||
page.Path = parent.Path + "/" + page.Name;
|
||||
page.Title = parent.Name + " - " + page.Name;
|
||||
page.Order = 0;
|
||||
page.IsNavigation = false;
|
||||
page.Url = "";
|
||||
page.ThemeType = parent.ThemeType;
|
||||
page.DefaultContainerType = parent.DefaultContainerType;
|
||||
page.Icon = parent.Icon;
|
||||
page.PermissionList = new List<Permission>()
|
||||
page = _pages.GetPage(parent.Path + "/" + user.Username, parent.SiteId);
|
||||
if (page == null)
|
||||
{
|
||||
new Permission(PermissionNames.View, int.Parse(userid), true),
|
||||
new Permission(PermissionNames.View, RoleNames.Everyone, true),
|
||||
new Permission(PermissionNames.Edit, int.Parse(userid), true)
|
||||
};
|
||||
page.IsPersonalizable = false;
|
||||
page.UserId = int.Parse(userid);
|
||||
page = _pages.AddPage(page);
|
||||
|
||||
// copy modules
|
||||
List<PageModule> pagemodules = _pageModules.GetPageModules(page.SiteId).ToList();
|
||||
foreach (PageModule pm in pagemodules.Where(item => item.PageId == parent.PageId && !item.IsDeleted))
|
||||
{
|
||||
Module module = new Module();
|
||||
module.SiteId = page.SiteId;
|
||||
module.PageId = page.PageId;
|
||||
module.ModuleDefinitionName = pm.Module.ModuleDefinitionName;
|
||||
module.AllPages = false;
|
||||
module.PermissionList = new List<Permission>()
|
||||
page = new Page();
|
||||
page.SiteId = parent.SiteId;
|
||||
page.ParentId = parent.PageId;
|
||||
page.Name = (!string.IsNullOrEmpty(user.DisplayName)) ? user.DisplayName : user.Username;
|
||||
page.Path = parent.Path + "/" + user.Username;
|
||||
page.Title = page.Name + " - " + parent.Name;
|
||||
page.Order = 0;
|
||||
page.IsNavigation = false;
|
||||
page.Url = "";
|
||||
page.ThemeType = parent.ThemeType;
|
||||
page.DefaultContainerType = parent.DefaultContainerType;
|
||||
page.Icon = parent.Icon;
|
||||
page.PermissionList = new List<Permission>()
|
||||
{
|
||||
new Permission(PermissionNames.View, int.Parse(userid), true),
|
||||
new Permission(PermissionNames.View, RoleNames.Everyone, true),
|
||||
new Permission(PermissionNames.Edit, int.Parse(userid), true)
|
||||
};
|
||||
module = _modules.AddModule(module);
|
||||
page.IsPersonalizable = false;
|
||||
page.UserId = int.Parse(userid);
|
||||
page = _pages.AddPage(page);
|
||||
|
||||
string content = _modules.ExportModule(pm.ModuleId);
|
||||
if (content != "")
|
||||
// copy modules
|
||||
List<PageModule> pagemodules = _pageModules.GetPageModules(page.SiteId).ToList();
|
||||
foreach (PageModule pm in pagemodules.Where(item => item.PageId == parent.PageId && !item.IsDeleted))
|
||||
{
|
||||
_modules.ImportModule(module.ModuleId, content);
|
||||
Module module = new Module();
|
||||
module.SiteId = page.SiteId;
|
||||
module.PageId = page.PageId;
|
||||
module.ModuleDefinitionName = pm.Module.ModuleDefinitionName;
|
||||
module.AllPages = false;
|
||||
module.PermissionList = new List<Permission>()
|
||||
{
|
||||
new Permission(PermissionNames.View, int.Parse(userid), true),
|
||||
new Permission(PermissionNames.View, RoleNames.Everyone, true),
|
||||
new Permission(PermissionNames.Edit, int.Parse(userid), true)
|
||||
};
|
||||
module = _modules.AddModule(module);
|
||||
|
||||
string content = _modules.ExportModule(pm.ModuleId);
|
||||
if (content != "")
|
||||
{
|
||||
_modules.ImportModule(module.ModuleId, content);
|
||||
}
|
||||
|
||||
PageModule pagemodule = new PageModule();
|
||||
pagemodule.PageId = page.PageId;
|
||||
pagemodule.ModuleId = module.ModuleId;
|
||||
pagemodule.Title = pm.Title;
|
||||
pagemodule.Pane = pm.Pane;
|
||||
pagemodule.Order = pm.Order;
|
||||
pagemodule.ContainerType = pm.ContainerType;
|
||||
|
||||
_pageModules.AddPageModule(pagemodule);
|
||||
}
|
||||
|
||||
PageModule pagemodule = new PageModule();
|
||||
pagemodule.PageId = page.PageId;
|
||||
pagemodule.ModuleId = module.ModuleId;
|
||||
pagemodule.Title = pm.Title;
|
||||
pagemodule.Pane = pm.Pane;
|
||||
pagemodule.Order = pm.Order;
|
||||
pagemodule.ContainerType = pm.ContainerType;
|
||||
|
||||
_pageModules.AddPageModule(pagemodule);
|
||||
_syncManager.AddSyncEvent(_alias.TenantId, EntityNames.Page, page.PageId, SyncEventActions.Create);
|
||||
_syncManager.AddSyncEvent(_alias.TenantId, EntityNames.Site, page.SiteId, SyncEventActions.Refresh);
|
||||
}
|
||||
|
||||
_syncManager.AddSyncEvent(_alias.TenantId, EntityNames.Page, page.PageId, SyncEventActions.Create);
|
||||
_syncManager.AddSyncEvent(_alias.TenantId, EntityNames.Site, page.SiteId, SyncEventActions.Refresh);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -263,14 +279,18 @@ namespace Oqtane.Controllers
|
||||
// save url mapping if page path changed
|
||||
if (currentPage.Path != page.Path)
|
||||
{
|
||||
var urlMapping = new UrlMapping();
|
||||
urlMapping.SiteId = page.SiteId;
|
||||
urlMapping.Url = currentPage.Path;
|
||||
urlMapping.MappedUrl = page.Path;
|
||||
urlMapping.Requests = 0;
|
||||
urlMapping.CreatedOn = System.DateTime.UtcNow;
|
||||
urlMapping.RequestedOn = System.DateTime.UtcNow;
|
||||
_urlMappings.AddUrlMapping(urlMapping);
|
||||
var urlMapping = _urlMappings.GetUrlMapping(page.SiteId, currentPage.Path);
|
||||
if (urlMapping == null)
|
||||
{
|
||||
urlMapping = new UrlMapping();
|
||||
urlMapping.SiteId = page.SiteId;
|
||||
urlMapping.Url = currentPage.Path;
|
||||
urlMapping.MappedUrl = page.Path;
|
||||
urlMapping.Requests = 0;
|
||||
urlMapping.CreatedOn = System.DateTime.UtcNow;
|
||||
urlMapping.RequestedOn = System.DateTime.UtcNow;
|
||||
_urlMappings.AddUrlMapping(urlMapping);
|
||||
}
|
||||
}
|
||||
|
||||
// get differences between current and new page permissions
|
||||
@ -314,6 +334,16 @@ namespace Oqtane.Controllers
|
||||
}
|
||||
}
|
||||
|
||||
// update child paths
|
||||
if (page.ParentId != currentPage.ParentId)
|
||||
{
|
||||
foreach (Page _page in _pages.GetPages(page.SiteId).Where(item => item.Path.StartsWith(currentPage.Path)).ToList())
|
||||
{
|
||||
_page.Path = _page.Path.Replace(currentPage.Path, page.Path);
|
||||
_pages.UpdatePage(_page);
|
||||
}
|
||||
}
|
||||
|
||||
_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);
|
||||
|
@ -44,8 +44,15 @@ namespace Oqtane.Controllers
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized PageModule Get Attempt {PageModuleId}", id);
|
||||
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
|
||||
if (pagemodule != null)
|
||||
{
|
||||
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized PageModule Get Attempt {PageModuleId}", id);
|
||||
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
|
||||
}
|
||||
else
|
||||
{
|
||||
HttpContext.Response.StatusCode = (int)HttpStatusCode.NotFound;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@ -61,8 +68,15 @@ namespace Oqtane.Controllers
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized PageModule Get Attempt {PageId} {ModuleId}", pageid, moduleid);
|
||||
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
|
||||
if (pagemodule != null)
|
||||
{
|
||||
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized PageModule Get Attempt {PageId} {ModuleId}", pageid, moduleid);
|
||||
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
|
||||
}
|
||||
else
|
||||
{
|
||||
HttpContext.Response.StatusCode = (int)HttpStatusCode.NotFound;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
@ -56,8 +56,15 @@ namespace Oqtane.Controllers
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized Profile Get Attempt {ProfileId}", id);
|
||||
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
|
||||
if (profile != null)
|
||||
{
|
||||
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized Profile Get Attempt {ProfileId}", id);
|
||||
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
|
||||
}
|
||||
else
|
||||
{
|
||||
HttpContext.Response.StatusCode = (int)HttpStatusCode.NotFound;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
@ -59,9 +59,16 @@ namespace Oqtane.Controllers
|
||||
return role;
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized Role Get Attempt {RoleId}", id);
|
||||
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
|
||||
{
|
||||
if (role != null)
|
||||
{
|
||||
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized Role Get Attempt {RoleId}", id);
|
||||
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
|
||||
}
|
||||
else
|
||||
{
|
||||
HttpContext.Response.StatusCode = (int)HttpStatusCode.NotFound;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
@ -89,11 +89,15 @@ namespace Oqtane.Controllers
|
||||
}
|
||||
else
|
||||
{
|
||||
if (entityName != EntityNames.Visitor)
|
||||
if (setting != null && entityName != EntityNames.Visitor)
|
||||
{
|
||||
_logger.Log(LogLevel.Error, this, LogFunction.Read, "User Not Authorized To Access Setting {EntityName} {SettingId}", entityName, id);
|
||||
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
|
||||
}
|
||||
else
|
||||
{
|
||||
HttpContext.Response.StatusCode = (int)HttpStatusCode.NotFound;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
@ -79,7 +79,7 @@ namespace Oqtane.Controllers
|
||||
private Site GetSite(int siteid)
|
||||
{
|
||||
var site = _sites.GetSite(siteid);
|
||||
if (site.SiteId == _alias.SiteId)
|
||||
if (site != null && site.SiteId == _alias.SiteId)
|
||||
{
|
||||
// site settings
|
||||
site.Settings = _settings.GetSettings(EntityNames.Site, site.SiteId)
|
||||
@ -153,8 +153,15 @@ namespace Oqtane.Controllers
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized Site Get Attempt {SiteId}", siteid);
|
||||
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
|
||||
if (site != null)
|
||||
{
|
||||
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized Site Get Attempt {SiteId}", siteid);
|
||||
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
|
||||
}
|
||||
else
|
||||
{
|
||||
HttpContext.Response.StatusCode = (int)HttpStatusCode.NotFound;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@ -246,7 +253,7 @@ namespace Oqtane.Controllers
|
||||
foreach (Page child in children)
|
||||
{
|
||||
child.Level = level + 1;
|
||||
child.HasChildren = pages.Any(item => item.ParentId == child.PageId);
|
||||
child.HasChildren = pages.Any(item => item.ParentId == child.PageId && !item.IsDeleted && item.IsNavigation);
|
||||
hierarchy.Add(child);
|
||||
getPath(pageList, child);
|
||||
}
|
||||
|
@ -55,9 +55,16 @@ namespace Oqtane.Controllers
|
||||
return urlMapping;
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized UrlMapping Get Attempt {UrlMappingId}", id);
|
||||
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
|
||||
{
|
||||
if (urlMapping != null)
|
||||
{
|
||||
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized UrlMapping Get Attempt {UrlMappingId}", id);
|
||||
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
|
||||
}
|
||||
else
|
||||
{
|
||||
HttpContext.Response.StatusCode = (int)HttpStatusCode.NotFound;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@ -73,8 +80,15 @@ namespace Oqtane.Controllers
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized UrlMapping Get Attempt {SiteId} {Url}", siteid, url);
|
||||
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
|
||||
if (urlMapping != null)
|
||||
{
|
||||
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized UrlMapping Get Attempt {SiteId} {Url}", siteid, url);
|
||||
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
|
||||
}
|
||||
else
|
||||
{
|
||||
HttpContext.Response.StatusCode = (int)HttpStatusCode.NotFound;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,3 @@
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Authentication;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
@ -9,13 +8,13 @@ using System.Linq;
|
||||
using System.Security.Claims;
|
||||
using Oqtane.Shared;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Net;
|
||||
using Oqtane.Enums;
|
||||
using Oqtane.Infrastructure;
|
||||
using Oqtane.Repository;
|
||||
using Oqtane.Security;
|
||||
using Oqtane.Extensions;
|
||||
using Oqtane.Managers;
|
||||
|
||||
namespace Oqtane.Controllers
|
||||
{
|
||||
@ -23,31 +22,27 @@ namespace Oqtane.Controllers
|
||||
public class UserController : Controller
|
||||
{
|
||||
private readonly IUserRepository _users;
|
||||
private readonly IUserRoleRepository _userRoles;
|
||||
private readonly UserManager<IdentityUser> _identityUserManager;
|
||||
private readonly SignInManager<IdentityUser> _identitySignInManager;
|
||||
private readonly ITenantManager _tenantManager;
|
||||
private readonly INotificationRepository _notifications;
|
||||
private readonly IFolderRepository _folders;
|
||||
private readonly IUserManager _userManager;
|
||||
private readonly ISiteRepository _sites;
|
||||
private readonly IUserPermissions _userPermissions;
|
||||
private readonly IJwtManager _jwtManager;
|
||||
private readonly ISyncManager _syncManager;
|
||||
private readonly ILogManager _logger;
|
||||
|
||||
public UserController(IUserRepository users, IUserRoleRepository userRoles, UserManager<IdentityUser> identityUserManager, SignInManager<IdentityUser> identitySignInManager, ITenantManager tenantManager, INotificationRepository notifications, IFolderRepository folders, ISiteRepository sites, IUserPermissions userPermissions, IJwtManager jwtManager, ISyncManager syncManager, ILogManager logger)
|
||||
public UserController(IUserRepository users, UserManager<IdentityUser> identityUserManager, SignInManager<IdentityUser> identitySignInManager, ITenantManager tenantManager, INotificationRepository notifications, IUserManager userManager, ISiteRepository sites, IUserPermissions userPermissions, IJwtManager jwtManager, ILogManager logger)
|
||||
{
|
||||
_users = users;
|
||||
_userRoles = userRoles;
|
||||
_identityUserManager = identityUserManager;
|
||||
_identitySignInManager = identitySignInManager;
|
||||
_tenantManager = tenantManager;
|
||||
_notifications = notifications;
|
||||
_folders = folders;
|
||||
_userManager = userManager;
|
||||
_sites = sites;
|
||||
_userPermissions = userPermissions;
|
||||
_jwtManager = jwtManager;
|
||||
_syncManager = syncManager;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
@ -56,14 +51,12 @@ namespace Oqtane.Controllers
|
||||
[Authorize]
|
||||
public User Get(int id, string siteid)
|
||||
{
|
||||
int SiteId;
|
||||
if (int.TryParse(siteid, out SiteId) && SiteId == _tenantManager.GetAlias().SiteId)
|
||||
if (int.TryParse(siteid, out int SiteId) && SiteId == _tenantManager.GetAlias().SiteId)
|
||||
{
|
||||
User user = _users.GetUser(id);
|
||||
if (user != null)
|
||||
User user = _userManager.GetUser(id, SiteId);
|
||||
if (user == null)
|
||||
{
|
||||
user.SiteId = int.Parse(siteid);
|
||||
user.Roles = GetUserRoles(user.UserId, user.SiteId);
|
||||
HttpContext.Response.StatusCode = (int)HttpStatusCode.NotFound;
|
||||
}
|
||||
return Filter(user);
|
||||
}
|
||||
@ -79,14 +72,12 @@ namespace Oqtane.Controllers
|
||||
[HttpGet("name/{name}")]
|
||||
public User Get(string name, string siteid)
|
||||
{
|
||||
int SiteId;
|
||||
if (int.TryParse(siteid, out SiteId) && SiteId == _tenantManager.GetAlias().SiteId)
|
||||
if (int.TryParse(siteid, out int SiteId) && SiteId == _tenantManager.GetAlias().SiteId)
|
||||
{
|
||||
User user = _users.GetUser(name);
|
||||
if (user != null)
|
||||
User user = _userManager.GetUser(name, SiteId);
|
||||
if (user == null)
|
||||
{
|
||||
user.SiteId = int.Parse(siteid);
|
||||
user.Roles = GetUserRoles(user.UserId, user.SiteId);
|
||||
HttpContext.Response.StatusCode = (int)HttpStatusCode.NotFound;
|
||||
}
|
||||
return Filter(user);
|
||||
}
|
||||
@ -133,8 +124,28 @@ namespace Oqtane.Controllers
|
||||
{
|
||||
if (ModelState.IsValid && user.SiteId == _tenantManager.GetAlias().SiteId)
|
||||
{
|
||||
var User = await CreateUser(user);
|
||||
return User;
|
||||
bool allowregistration;
|
||||
if (_userPermissions.IsAuthorized(User, user.SiteId, EntityNames.User, -1, PermissionNames.Write, RoleNames.Admin))
|
||||
{
|
||||
user.EmailConfirmed = true;
|
||||
allowregistration = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
user.EmailConfirmed = false;
|
||||
allowregistration = _sites.GetSite(user.SiteId).AllowRegistration;
|
||||
}
|
||||
|
||||
if (allowregistration)
|
||||
{
|
||||
user = await _userManager.AddUser(user);
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.Log(user.SiteId, LogLevel.Error, this, LogFunction.Create, "User Registration Is Not Enabled For Site. User Was Not Added {User}", user);
|
||||
}
|
||||
|
||||
return user;
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -145,99 +156,6 @@ namespace Oqtane.Controllers
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<User> CreateUser(User user)
|
||||
{
|
||||
User newUser = null;
|
||||
|
||||
bool verified;
|
||||
bool allowregistration;
|
||||
if (_userPermissions.IsAuthorized(User, user.SiteId, EntityNames.User, -1, PermissionNames.Write, RoleNames.Admin))
|
||||
{
|
||||
verified = true;
|
||||
allowregistration = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
verified = false;
|
||||
allowregistration = _sites.GetSite(user.SiteId).AllowRegistration;
|
||||
}
|
||||
|
||||
if (allowregistration)
|
||||
{
|
||||
bool succeeded;
|
||||
string errors = "";
|
||||
IdentityUser identityuser = await _identityUserManager.FindByNameAsync(user.Username);
|
||||
if (identityuser == null)
|
||||
{
|
||||
identityuser = new IdentityUser();
|
||||
identityuser.UserName = user.Username;
|
||||
identityuser.Email = user.Email;
|
||||
identityuser.EmailConfirmed = verified;
|
||||
var result = await _identityUserManager.CreateAsync(identityuser, user.Password);
|
||||
succeeded = result.Succeeded;
|
||||
if (!succeeded)
|
||||
{
|
||||
errors = string.Join(", ", result.Errors.Select(e => e.Description));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var result = await _identitySignInManager.CheckPasswordSignInAsync(identityuser, user.Password, false);
|
||||
succeeded = result.Succeeded;
|
||||
if (!succeeded)
|
||||
{
|
||||
errors = "Password Not Valid For User";
|
||||
}
|
||||
verified = succeeded;
|
||||
}
|
||||
|
||||
if (succeeded)
|
||||
{
|
||||
user.LastLoginOn = null;
|
||||
user.LastIPAddress = "";
|
||||
newUser = _users.AddUser(user);
|
||||
_syncManager.AddSyncEvent(_tenantManager.GetAlias().TenantId, EntityNames.User, newUser.UserId, SyncEventActions.Create);
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.Log(user.SiteId, LogLevel.Error, this, LogFunction.Create, "Unable To Add User {Username} - {Errors}", user.Username, errors);
|
||||
}
|
||||
|
||||
if (newUser != null)
|
||||
{
|
||||
if (!verified)
|
||||
{
|
||||
string token = await _identityUserManager.GenerateEmailConfirmationTokenAsync(identityuser);
|
||||
string url = HttpContext.Request.Scheme + "://" + _tenantManager.GetAlias().Name + "/login?name=" + user.Username + "&token=" + WebUtility.UrlEncode(token);
|
||||
string body = "Dear " + user.DisplayName + ",\n\nIn Order To Complete The Registration Of Your User Account Please Click The Link Displayed Below:\n\n" + url + "\n\nThank You!";
|
||||
var notification = new Notification(user.SiteId, newUser, "User Account Verification", body);
|
||||
_notifications.AddNotification(notification);
|
||||
}
|
||||
else
|
||||
{
|
||||
string url = HttpContext.Request.Scheme + "://" + _tenantManager.GetAlias().Name;
|
||||
string body = "Dear " + user.DisplayName + ",\n\nA User Account Has Been Successfully Created For You. Please Use The Following Link To Access The Site:\n\n" + url + "\n\nThank You!";
|
||||
var notification = new Notification(user.SiteId, newUser, "User Account Notification", body);
|
||||
_notifications.AddNotification(notification);
|
||||
}
|
||||
|
||||
newUser.Password = ""; // remove sensitive information
|
||||
_logger.Log(user.SiteId, LogLevel.Information, this, LogFunction.Create, "User Added {User}", newUser);
|
||||
}
|
||||
else
|
||||
{
|
||||
user.Password = ""; // remove sensitive information
|
||||
_logger.Log(user.SiteId, LogLevel.Error, this, LogFunction.Create, "Unable To Add User {User}", user);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.Log(user.SiteId, LogLevel.Error, this, LogFunction.Create, "User Registration Is Not Enabled For Site. User Was Not Added {User}", user);
|
||||
}
|
||||
|
||||
return newUser;
|
||||
}
|
||||
|
||||
// PUT api/<controller>/5
|
||||
[HttpPut("{id}")]
|
||||
[Authorize]
|
||||
@ -246,37 +164,7 @@ namespace Oqtane.Controllers
|
||||
if (ModelState.IsValid && user.SiteId == _tenantManager.GetAlias().SiteId && _users.GetUser(user.UserId, false) != null
|
||||
&& (_userPermissions.IsAuthorized(User, user.SiteId, EntityNames.User, -1, PermissionNames.Write, RoleNames.Admin) || User.Identity.Name == user.Username))
|
||||
{
|
||||
IdentityUser identityuser = await _identityUserManager.FindByNameAsync(user.Username);
|
||||
if (identityuser != null)
|
||||
{
|
||||
identityuser.Email = user.Email;
|
||||
var valid = true;
|
||||
if (user.Password != "")
|
||||
{
|
||||
var validator = new PasswordValidator<IdentityUser>();
|
||||
var result = await validator.ValidateAsync(_identityUserManager, null, user.Password);
|
||||
valid = result.Succeeded;
|
||||
if (valid)
|
||||
{
|
||||
identityuser.PasswordHash = _identityUserManager.PasswordHasher.HashPassword(identityuser, user.Password);
|
||||
}
|
||||
}
|
||||
if (valid)
|
||||
{
|
||||
await _identityUserManager.UpdateAsync(identityuser);
|
||||
|
||||
user = _users.UpdateUser(user);
|
||||
_syncManager.AddSyncEvent(_tenantManager.GetAlias().TenantId, EntityNames.User, user.UserId, SyncEventActions.Update);
|
||||
_syncManager.AddSyncEvent(_tenantManager.GetAlias().TenantId, EntityNames.User, user.UserId, SyncEventActions.Refresh);
|
||||
user.Password = ""; // remove sensitive information
|
||||
_logger.Log(LogLevel.Information, this, LogFunction.Update, "User Updated {User}", user);
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.Log(user.SiteId, LogLevel.Error, this, LogFunction.Update, "Unable To Update User {Username}. Password Does Not Meet Complexity Requirements.", user.Username);
|
||||
user = null;
|
||||
}
|
||||
}
|
||||
user = await _userManager.UpdateUser(user);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -293,51 +181,10 @@ namespace Oqtane.Controllers
|
||||
[Authorize(Policy = $"{EntityNames.User}:{PermissionNames.Write}:{RoleNames.Admin}")]
|
||||
public async Task Delete(int id, string siteid)
|
||||
{
|
||||
int SiteId;
|
||||
User user = _users.GetUser(id);
|
||||
if (user != null && int.TryParse(siteid, out SiteId) && SiteId == _tenantManager.GetAlias().SiteId)
|
||||
User user = _users.GetUser(id, false);
|
||||
if (user != null && int.TryParse(siteid, out int SiteId) && SiteId == _tenantManager.GetAlias().SiteId)
|
||||
{
|
||||
// remove user roles for site
|
||||
foreach (UserRole userrole in _userRoles.GetUserRoles(user.UserId, SiteId).ToList())
|
||||
{
|
||||
_userRoles.DeleteUserRole(userrole.UserRoleId);
|
||||
_logger.Log(LogLevel.Information, this, LogFunction.Delete, "User Role Deleted {UserRole}", userrole);
|
||||
}
|
||||
|
||||
// remove user folder for site
|
||||
var folder = _folders.GetFolder(SiteId, $"Users{user.UserId}/");
|
||||
if (folder != null)
|
||||
{
|
||||
if (Directory.Exists(_folders.GetFolderPath(folder)))
|
||||
{
|
||||
Directory.Delete(_folders.GetFolderPath(folder), true);
|
||||
}
|
||||
_folders.DeleteFolder(folder.FolderId);
|
||||
_logger.Log(LogLevel.Information, this, LogFunction.Delete, "User Folder Deleted {Folder}", folder);
|
||||
}
|
||||
|
||||
// delete user if they are not a member of any other sites
|
||||
if (!_userRoles.GetUserRoles(user.UserId, -1).Any())
|
||||
{
|
||||
// get identity user
|
||||
IdentityUser identityuser = await _identityUserManager.FindByNameAsync(user.Username);
|
||||
if (identityuser != null)
|
||||
{
|
||||
// delete identity user
|
||||
var result = await _identityUserManager.DeleteAsync(identityuser);
|
||||
if (result != null)
|
||||
{
|
||||
// delete user
|
||||
_users.DeleteUser(user.UserId);
|
||||
_syncManager.AddSyncEvent(_tenantManager.GetAlias().TenantId, EntityNames.User, user.UserId, SyncEventActions.Delete);
|
||||
_logger.Log(LogLevel.Information, this, LogFunction.Delete, "User Deleted {UserId}", user.UserId, result.ToString());
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.Log(LogLevel.Error, this, LogFunction.Delete, "Error Deleting User {UserId}", user.UserId);
|
||||
}
|
||||
}
|
||||
}
|
||||
await _userManager.DeleteUser(id, SiteId);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -350,83 +197,15 @@ namespace Oqtane.Controllers
|
||||
[HttpPost("login")]
|
||||
public async Task<User> Login([FromBody] User user, bool setCookie, bool isPersistent)
|
||||
{
|
||||
User loginUser = new User { SiteId = user.SiteId, Username = user.Username, IsAuthenticated = false };
|
||||
|
||||
if (ModelState.IsValid)
|
||||
{
|
||||
IdentityUser identityuser = await _identityUserManager.FindByNameAsync(user.Username);
|
||||
if (identityuser != null)
|
||||
{
|
||||
var result = await _identitySignInManager.CheckPasswordSignInAsync(identityuser, user.Password, true);
|
||||
if (result.Succeeded)
|
||||
{
|
||||
var LastIPAddress = user.LastIPAddress ?? "";
|
||||
|
||||
user = _users.GetUser(user.Username);
|
||||
if (user.TwoFactorRequired)
|
||||
{
|
||||
var token = await _identityUserManager.GenerateTwoFactorTokenAsync(identityuser, "Email");
|
||||
user.TwoFactorCode = token;
|
||||
user.TwoFactorExpiry = DateTime.UtcNow.AddMinutes(10);
|
||||
_users.UpdateUser(user);
|
||||
|
||||
string body = "Dear " + user.DisplayName + ",\n\nYou requested a secure verification code to log in to your account. Please enter the secure verification code on the site:\n\n" + token +
|
||||
"\n\nPlease note that the code is only valid for 10 minutes so if you are unable to take action within that time period, you should initiate a new login on the site." +
|
||||
"\n\nThank You!";
|
||||
var notification = new Notification(loginUser.SiteId, user, "User Verification Code", body);
|
||||
_notifications.AddNotification(notification);
|
||||
|
||||
_logger.Log(LogLevel.Information, this, LogFunction.Security, "User Verification Notification Sent For {Username}", user.Username);
|
||||
loginUser.TwoFactorRequired = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
loginUser = _users.GetUser(identityuser.UserName);
|
||||
if (loginUser != null)
|
||||
{
|
||||
if (identityuser.EmailConfirmed)
|
||||
{
|
||||
loginUser.IsAuthenticated = true;
|
||||
loginUser.LastLoginOn = DateTime.UtcNow;
|
||||
loginUser.LastIPAddress = LastIPAddress;
|
||||
_users.UpdateUser(loginUser);
|
||||
_logger.Log(LogLevel.Information, this, LogFunction.Security, "User Login Successful {Username}", user.Username);
|
||||
|
||||
if (setCookie)
|
||||
{
|
||||
await _identitySignInManager.SignInAsync(identityuser, isPersistent);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.Log(LogLevel.Information, this, LogFunction.Security, "User Not Verified {Username}", user.Username);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (result.IsLockedOut)
|
||||
{
|
||||
user = _users.GetUser(user.Username);
|
||||
string token = await _identityUserManager.GeneratePasswordResetTokenAsync(identityuser);
|
||||
string url = HttpContext.Request.Scheme + "://" + _tenantManager.GetAlias().Name + "/reset?name=" + user.Username + "&token=" + WebUtility.UrlEncode(token);
|
||||
string body = "Dear " + user.DisplayName + ",\n\nYou attempted multiple times unsuccessfully to log in to your account and it is now locked out. Please wait a few minutes and then try again... or use the link below to reset your password:\n\n" + url +
|
||||
"\n\nPlease note that the link is only valid for 24 hours so if you are unable to take action within that time period, you should initiate another password reset on the site." +
|
||||
"\n\nThank You!";
|
||||
var notification = new Notification(loginUser.SiteId, user, "User Lockout", body);
|
||||
_notifications.AddNotification(notification);
|
||||
_logger.Log(LogLevel.Information, this, LogFunction.Security, "User Lockout Notification Sent For {Username}", user.Username);
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.Log(LogLevel.Information, this, LogFunction.Security, "User Login Failed {Username}", user.Username);
|
||||
}
|
||||
}
|
||||
}
|
||||
user = await _userManager.LoginUser(user, setCookie, isPersistent);
|
||||
}
|
||||
|
||||
return loginUser;
|
||||
else
|
||||
{
|
||||
user = new User { SiteId = user.SiteId, Username = user.Username, IsAuthenticated = false };
|
||||
}
|
||||
return user;
|
||||
}
|
||||
|
||||
// POST api/<controller>/logout
|
||||
@ -444,25 +223,7 @@ namespace Oqtane.Controllers
|
||||
{
|
||||
if (ModelState.IsValid)
|
||||
{
|
||||
IdentityUser identityuser = await _identityUserManager.FindByNameAsync(user.Username);
|
||||
if (identityuser != null && !string.IsNullOrEmpty(token))
|
||||
{
|
||||
var result = await _identityUserManager.ConfirmEmailAsync(identityuser, token);
|
||||
if (result.Succeeded)
|
||||
{
|
||||
_logger.Log(LogLevel.Information, this, LogFunction.Security, "Email Verified For {Username}", user.Username);
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Email Verification Failed For {Username} - Error {Error}", user.Username, string.Join(" ", result.Errors.ToList().Select(e => e.Description)));
|
||||
user = null;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Email Verification Failed For {Username}And Token {Token}", user.Username, token);
|
||||
user = null;
|
||||
}
|
||||
user = await _userManager.VerifyEmail(user, token);
|
||||
}
|
||||
return user;
|
||||
}
|
||||
@ -473,25 +234,7 @@ namespace Oqtane.Controllers
|
||||
{
|
||||
if (ModelState.IsValid)
|
||||
{
|
||||
IdentityUser identityuser = await _identityUserManager.FindByNameAsync(user.Username);
|
||||
if (identityuser != null)
|
||||
{
|
||||
user = _users.GetUser(user.Username);
|
||||
string token = await _identityUserManager.GeneratePasswordResetTokenAsync(identityuser);
|
||||
string url = HttpContext.Request.Scheme + "://" + _tenantManager.GetAlias().Name + "/reset?name=" + user.Username + "&token=" + WebUtility.UrlEncode(token);
|
||||
string body = "Dear " + user.DisplayName + ",\n\nYou recently requested to reset your password. Please use the link below to complete the process:\n\n" + url +
|
||||
"\n\nPlease note that the link is only valid for 24 hours so if you are unable to take action within that time period, you should initiate another password reset on the site." +
|
||||
"\n\nIf you did not request to reset your password you can safely ignore this message." +
|
||||
"\n\nThank You!";
|
||||
|
||||
var notification = new Notification(_tenantManager.GetAlias().SiteId, user, "User Password Reset", body);
|
||||
_notifications.AddNotification(notification);
|
||||
_logger.Log(LogLevel.Information, this, LogFunction.Security, "Password Reset Notification Sent For {Username}", user.Username);
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Password Reset Notification Failed For {Username}", user.Username);
|
||||
}
|
||||
await _userManager.ForgotPassword(user);
|
||||
}
|
||||
}
|
||||
|
||||
@ -501,26 +244,7 @@ namespace Oqtane.Controllers
|
||||
{
|
||||
if (ModelState.IsValid)
|
||||
{
|
||||
IdentityUser identityuser = await _identityUserManager.FindByNameAsync(user.Username);
|
||||
if (identityuser != null && !string.IsNullOrEmpty(token))
|
||||
{
|
||||
var result = await _identityUserManager.ResetPasswordAsync(identityuser, token, user.Password);
|
||||
if (result.Succeeded)
|
||||
{
|
||||
_logger.Log(LogLevel.Information, this, LogFunction.Security, "Password Reset For {Username}", user.Username);
|
||||
user.Password = "";
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.Log(LogLevel.Information, this, LogFunction.Security, "Password Reset Failed For {Username} - Error {Error}", user.Username, string.Join(" ", result.Errors.ToList().Select(e => e.Description)));
|
||||
user = null;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Password Reset Failed For {Username} And Token {Token}", user.Username, token);
|
||||
user = null;
|
||||
}
|
||||
user = await _userManager.ResetPassword(user, token);
|
||||
}
|
||||
return user;
|
||||
}
|
||||
@ -529,21 +253,16 @@ namespace Oqtane.Controllers
|
||||
[HttpPost("twofactor")]
|
||||
public User TwoFactor([FromBody] User user, string token)
|
||||
{
|
||||
User loginUser = new User { SiteId = user.SiteId, Username = user.Username, IsAuthenticated = false };
|
||||
|
||||
if (ModelState.IsValid && !string.IsNullOrEmpty(token))
|
||||
{
|
||||
user = _users.GetUser(user.Username);
|
||||
if (user != null)
|
||||
{
|
||||
if (user.TwoFactorRequired && user.TwoFactorCode == token && DateTime.UtcNow < user.TwoFactorExpiry)
|
||||
{
|
||||
loginUser.IsAuthenticated = true;
|
||||
}
|
||||
}
|
||||
user = _userManager.VerifyTwoFactor(user, token);
|
||||
}
|
||||
else
|
||||
{
|
||||
user.IsAuthenticated = false;
|
||||
}
|
||||
|
||||
return loginUser;
|
||||
return user;
|
||||
}
|
||||
|
||||
// POST api/<controller>/link
|
||||
@ -552,23 +271,7 @@ namespace Oqtane.Controllers
|
||||
{
|
||||
if (ModelState.IsValid)
|
||||
{
|
||||
IdentityUser identityuser = await _identityUserManager.FindByNameAsync(user.Username);
|
||||
if (identityuser != null && !string.IsNullOrEmpty(token))
|
||||
{
|
||||
var result = await _identityUserManager.ConfirmEmailAsync(identityuser, token);
|
||||
if (result.Succeeded)
|
||||
{
|
||||
// make LoginProvider multi-tenant aware
|
||||
type += ":" + user.SiteId.ToString();
|
||||
await _identityUserManager.AddLoginAsync(identityuser, new UserLoginInfo(type, key, name));
|
||||
_logger.Log(LogLevel.Information, this, LogFunction.Security, "External Login Linkage Successful For {Username} And Provider {Provider}", user.Username, type);
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.Log(LogLevel.Error, this, LogFunction.Security, "External Login Linkage Failed For {Username} - Error {Error}", user.Username, string.Join(" ", result.Errors.ToList().Select(e => e.Description)));
|
||||
user = null;
|
||||
}
|
||||
}
|
||||
user = await _userManager.LinkExternalAccount(user, token, type, key, name);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -582,9 +285,7 @@ namespace Oqtane.Controllers
|
||||
[HttpGet("validate/{password}")]
|
||||
public async Task<bool> Validate(string password)
|
||||
{
|
||||
var validator = new PasswordValidator<IdentityUser>();
|
||||
var result = await validator.ValidateAsync(_identityUserManager, null, password);
|
||||
return result.Succeeded;
|
||||
return await _userManager.ValidatePassword(password);
|
||||
}
|
||||
|
||||
// GET api/<controller>/token
|
||||
@ -640,21 +341,5 @@ namespace Oqtane.Controllers
|
||||
}
|
||||
return user;
|
||||
}
|
||||
|
||||
private string GetUserRoles(int userId, int siteId)
|
||||
{
|
||||
string roles = "";
|
||||
List<UserRole> userroles = _userRoles.GetUserRoles(userId, siteId).ToList();
|
||||
foreach (UserRole userrole in userroles)
|
||||
{
|
||||
roles += userrole.Role.Name + ";";
|
||||
if (userrole.Role.Name == RoleNames.Host && userroles.Where(item => item.Role.Name == RoleNames.Admin).FirstOrDefault() == null)
|
||||
{
|
||||
roles += RoleNames.Admin + ";";
|
||||
}
|
||||
}
|
||||
if (roles != "") roles = ";" + roles;
|
||||
return roles;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -79,8 +79,15 @@ namespace Oqtane.Controllers
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized User Role Get Attempt {UserRoleId}", id);
|
||||
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
|
||||
if (userrole != null)
|
||||
{
|
||||
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized User Role Get Attempt {UserRoleId}", id);
|
||||
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
|
||||
}
|
||||
else
|
||||
{
|
||||
HttpContext.Response.StatusCode = (int)HttpStatusCode.NotFound;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
@ -64,8 +64,15 @@ namespace Oqtane.Controllers
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized Visitor Get Attempt {VisitorId}", id);
|
||||
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
|
||||
if (visitor != null)
|
||||
{
|
||||
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized Visitor Get Attempt {VisitorId}", id);
|
||||
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
|
||||
}
|
||||
else
|
||||
{
|
||||
HttpContext.Response.StatusCode = (int)HttpStatusCode.NotFound;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
@ -19,6 +19,7 @@ using Microsoft.Extensions.Hosting;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.OpenApi.Models;
|
||||
using Oqtane.Infrastructure;
|
||||
using Oqtane.Managers;
|
||||
using Oqtane.Models;
|
||||
using Oqtane.Modules;
|
||||
using Oqtane.Repository;
|
||||
@ -73,13 +74,7 @@ namespace Microsoft.Extensions.DependencyInjection
|
||||
|
||||
internal static IServiceCollection AddOqtaneTransientServices(this IServiceCollection services)
|
||||
{
|
||||
services.AddTransient<IDBContextDependencies, DBContextDependencies>();
|
||||
services.AddTransient<ITenantManager, TenantManager>();
|
||||
services.AddTransient<IAliasAccessor, AliasAccessor>();
|
||||
services.AddTransient<IUserPermissions, UserPermissions>();
|
||||
services.AddTransient<ITenantResolver, TenantResolver>();
|
||||
services.AddTransient<IJwtManager, JwtManager>();
|
||||
|
||||
// repositories
|
||||
services.AddTransient<IModuleDefinitionRepository, ModuleDefinitionRepository>();
|
||||
services.AddTransient<IThemeRepository, ThemeRepository>();
|
||||
services.AddTransient<IAliasRepository, AliasRepository>();
|
||||
@ -95,7 +90,6 @@ namespace Microsoft.Extensions.DependencyInjection
|
||||
services.AddTransient<IPermissionRepository, PermissionRepository>();
|
||||
services.AddTransient<ISettingRepository, SettingRepository>();
|
||||
services.AddTransient<ILogRepository, LogRepository>();
|
||||
services.AddTransient<ILogManager, LogManager>();
|
||||
services.AddTransient<ILocalizationManager, LocalizationManager>();
|
||||
services.AddTransient<IJobRepository, JobRepository>();
|
||||
services.AddTransient<IJobLogRepository, JobLogRepository>();
|
||||
@ -104,11 +98,21 @@ namespace Microsoft.Extensions.DependencyInjection
|
||||
services.AddTransient<IFileRepository, FileRepository>();
|
||||
services.AddTransient<ISiteTemplateRepository, SiteTemplateRepository>();
|
||||
services.AddTransient<ISqlRepository, SqlRepository>();
|
||||
services.AddTransient<IUpgradeManager, UpgradeManager>();
|
||||
services.AddTransient<ILanguageRepository, LanguageRepository>();
|
||||
services.AddTransient<IVisitorRepository, VisitorRepository>();
|
||||
services.AddTransient<IUrlMappingRepository, UrlMappingRepository>();
|
||||
|
||||
// managers
|
||||
services.AddTransient<IDBContextDependencies, DBContextDependencies>();
|
||||
services.AddTransient<ITenantManager, TenantManager>();
|
||||
services.AddTransient<IAliasAccessor, AliasAccessor>();
|
||||
services.AddTransient<IUserPermissions, UserPermissions>();
|
||||
services.AddTransient<ITenantResolver, TenantResolver>();
|
||||
services.AddTransient<IJwtManager, JwtManager>();
|
||||
services.AddTransient<ILogManager, LogManager>();
|
||||
services.AddTransient<IUpgradeManager, UpgradeManager>();
|
||||
services.AddTransient<IUserManager, UserManager>();
|
||||
|
||||
// obsolete - replaced by ITenantManager
|
||||
services.AddTransient<ITenantResolver, TenantResolver>();
|
||||
|
||||
|
@ -201,7 +201,24 @@ namespace Oqtane.Infrastructure
|
||||
{
|
||||
Directory.CreateDirectory(Path.GetDirectoryName(filename));
|
||||
}
|
||||
entry.ExtractToFile(filename, true);
|
||||
if (Path.Exists(filename) && Path.GetExtension(filename).ToLower() == ".dll")
|
||||
{
|
||||
// ensure assembly version is equal to or greater than existing assembly
|
||||
var assembly = filename.Replace(Path.GetFileName(filename), "temp.dll");
|
||||
entry.ExtractToFile(assembly, true);
|
||||
if (Version.Parse(FileVersionInfo.GetVersionInfo(assembly).FileVersion).CompareTo(Version.Parse(FileVersionInfo.GetVersionInfo(filename).FileVersion)) >= 0)
|
||||
{
|
||||
File.Move(assembly, filename, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
File.Delete(assembly);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
entry.ExtractToFile(filename, true);
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
|
@ -42,7 +42,7 @@ namespace Oqtane.Infrastructure
|
||||
// get site settings
|
||||
List<Setting> sitesettings = settingRepository.GetSettings(EntityNames.Site, site.SiteId).ToList();
|
||||
Dictionary<string, string> settings = GetSettings(sitesettings);
|
||||
if (!settings.ContainsKey("SMTPEnabled") || settings["SMTPEnabled"] == "True")
|
||||
if (!site.IsDeleted && (!settings.ContainsKey("SMTPEnabled") || settings["SMTPEnabled"] == "True"))
|
||||
{
|
||||
if (settings.ContainsKey("SMTPHost") && settings["SMTPHost"] != "" &&
|
||||
settings.ContainsKey("SMTPPort") && settings["SMTPPort"] != "" &&
|
||||
@ -162,7 +162,7 @@ namespace Oqtane.Infrastructure
|
||||
}
|
||||
else
|
||||
{
|
||||
log += "SMTP Disabled In Site Settings" + "<br />";
|
||||
log += "Site Deleted Or SMTP Disabled In Site Settings" + "<br />";
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -210,7 +210,7 @@ namespace Oqtane.Infrastructure
|
||||
{
|
||||
var subject = $"{alias.Name} Site {log.Level} Notification";
|
||||
var url = $"{_accessor.HttpContext.Request.Scheme}://{alias.Name}/admin/log?id={log.LogId}";
|
||||
string body = $"Log Message: {log.Message}\n\nPlease visit {url} for more information";
|
||||
string body = $"Log Message: {log.Message}<br /><br />Please visit {url} for more information";
|
||||
var notification = new Notification(log.SiteId.Value, userrole.User, subject, body);
|
||||
_notifications.AddNotification(notification);
|
||||
}
|
||||
|
21
Oqtane.Server/Managers/Interfaces/IUserManager.cs
Normal file
21
Oqtane.Server/Managers/Interfaces/IUserManager.cs
Normal file
@ -0,0 +1,21 @@
|
||||
using System.Threading.Tasks;
|
||||
using Oqtane.Models;
|
||||
|
||||
namespace Oqtane.Managers
|
||||
{
|
||||
public interface IUserManager
|
||||
{
|
||||
User GetUser(int userid, int siteid);
|
||||
User GetUser(string username, int siteid);
|
||||
Task<User> AddUser(User user);
|
||||
Task<User> UpdateUser(User user);
|
||||
Task DeleteUser(int userid, int siteid);
|
||||
Task<User> LoginUser(User user, bool setCookie, bool isPersistent);
|
||||
Task<User> VerifyEmail(User user, string token);
|
||||
Task ForgotPassword(User user);
|
||||
Task<User> ResetPassword(User user, string token);
|
||||
User VerifyTwoFactor(User user, string token);
|
||||
Task<User> LinkExternalAccount(User user, string token, string type, string key, string name);
|
||||
Task<bool> ValidatePassword(string password);
|
||||
}
|
||||
}
|
434
Oqtane.Server/Managers/UserManager.cs
Normal file
434
Oqtane.Server/Managers/UserManager.cs
Normal file
@ -0,0 +1,434 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Oqtane.Enums;
|
||||
using Oqtane.Infrastructure;
|
||||
using Oqtane.Models;
|
||||
using Oqtane.Repository;
|
||||
using Oqtane.Shared;
|
||||
|
||||
namespace Oqtane.Managers
|
||||
{
|
||||
public class UserManager : IUserManager
|
||||
{
|
||||
private readonly IUserRepository _users;
|
||||
private readonly IUserRoleRepository _userRoles;
|
||||
private readonly UserManager<IdentityUser> _identityUserManager;
|
||||
private readonly SignInManager<IdentityUser> _identitySignInManager;
|
||||
private readonly ITenantManager _tenantManager;
|
||||
private readonly INotificationRepository _notifications;
|
||||
private readonly IFolderRepository _folders;
|
||||
private readonly ISyncManager _syncManager;
|
||||
private readonly ILogManager _logger;
|
||||
|
||||
public UserManager(IUserRepository users, IUserRoleRepository userRoles, UserManager<IdentityUser> identityUserManager, SignInManager<IdentityUser> identitySignInManager, ITenantManager tenantManager, INotificationRepository notifications, IFolderRepository folders, ISyncManager syncManager, ILogManager logger)
|
||||
{
|
||||
_users = users;
|
||||
_userRoles = userRoles;
|
||||
_identityUserManager = identityUserManager;
|
||||
_identitySignInManager = identitySignInManager;
|
||||
_tenantManager = tenantManager;
|
||||
_notifications = notifications;
|
||||
_folders = folders;
|
||||
_syncManager = syncManager;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public User GetUser(int userid, int siteid)
|
||||
{
|
||||
User user = _users.GetUser(userid);
|
||||
if (user != null)
|
||||
{
|
||||
user.SiteId = siteid;
|
||||
user.Roles = GetUserRoles(user.UserId, user.SiteId);
|
||||
}
|
||||
return user;
|
||||
}
|
||||
|
||||
public User GetUser(string username, int siteid)
|
||||
{
|
||||
User user = _users.GetUser(username);
|
||||
if (user != null)
|
||||
{
|
||||
user.SiteId = siteid;
|
||||
user.Roles = GetUserRoles(user.UserId, user.SiteId);
|
||||
}
|
||||
return user;
|
||||
}
|
||||
|
||||
private string GetUserRoles(int userId, int siteId)
|
||||
{
|
||||
string roles = "";
|
||||
List<UserRole> userroles = _userRoles.GetUserRoles(userId, siteId).ToList();
|
||||
foreach (UserRole userrole in userroles)
|
||||
{
|
||||
roles += userrole.Role.Name + ";";
|
||||
if (userrole.Role.Name == RoleNames.Host && !userroles.Any(item => item.Role.Name == RoleNames.Admin))
|
||||
{
|
||||
roles += RoleNames.Admin + ";";
|
||||
}
|
||||
if (userrole.Role.Name == RoleNames.Host && !userroles.Any(item => item.Role.Name == RoleNames.Registered))
|
||||
{
|
||||
roles += RoleNames.Registered + ";";
|
||||
}
|
||||
}
|
||||
if (roles != "") roles = ";" + roles;
|
||||
return roles;
|
||||
}
|
||||
|
||||
public async Task<User> AddUser(User user)
|
||||
{
|
||||
User User = null;
|
||||
var alias = _tenantManager.GetAlias();
|
||||
bool succeeded = false;
|
||||
string errors = "";
|
||||
|
||||
IdentityUser identityuser = await _identityUserManager.FindByNameAsync(user.Username);
|
||||
if (identityuser == null)
|
||||
{
|
||||
identityuser = new IdentityUser();
|
||||
identityuser.UserName = user.Username;
|
||||
identityuser.Email = user.Email;
|
||||
identityuser.EmailConfirmed = user.EmailConfirmed;
|
||||
var result = await _identityUserManager.CreateAsync(identityuser, user.Password);
|
||||
succeeded = result.Succeeded;
|
||||
if (!succeeded)
|
||||
{
|
||||
errors = string.Join(", ", result.Errors.Select(e => e.Description));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var result = await _identitySignInManager.CheckPasswordSignInAsync(identityuser, user.Password, false);
|
||||
succeeded = result.Succeeded;
|
||||
if (!succeeded)
|
||||
{
|
||||
errors = "Password Not Valid For User";
|
||||
}
|
||||
user.EmailConfirmed = succeeded;
|
||||
}
|
||||
|
||||
if (succeeded)
|
||||
{
|
||||
user.DisplayName = (user.DisplayName == null) ? user.Username : user.DisplayName;
|
||||
user.LastLoginOn = null;
|
||||
user.LastIPAddress = "";
|
||||
User = _users.AddUser(user);
|
||||
_syncManager.AddSyncEvent(alias.TenantId, EntityNames.User, User.UserId, SyncEventActions.Create);
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.Log(user.SiteId, LogLevel.Error, this, LogFunction.Create, "Unable To Add User {Username} - {Errors}", user.Username, errors);
|
||||
}
|
||||
|
||||
if (User != null)
|
||||
{
|
||||
if (!user.EmailConfirmed)
|
||||
{
|
||||
string token = await _identityUserManager.GenerateEmailConfirmationTokenAsync(identityuser);
|
||||
string url = alias.Protocol + "://" + alias.Name + "/login?name=" + user.Username + "&token=" + WebUtility.UrlEncode(token);
|
||||
string body = "Dear " + user.DisplayName + ",\n\nIn Order To Complete The Registration Of Your User Account Please Click The Link Displayed Below:\n\n" + url + "\n\nThank You!";
|
||||
var notification = new Notification(user.SiteId, User, "User Account Verification", body);
|
||||
_notifications.AddNotification(notification);
|
||||
}
|
||||
else
|
||||
{
|
||||
string url = alias.Protocol + "://" + alias.Name;
|
||||
string body = "Dear " + user.DisplayName + ",\n\nA User Account Has Been Successfully Created For You. Please Use The Following Link To Access The Site:\n\n" + url + "\n\nThank You!";
|
||||
var notification = new Notification(user.SiteId, User, "User Account Notification", body);
|
||||
_notifications.AddNotification(notification);
|
||||
}
|
||||
|
||||
User.Password = ""; // remove sensitive information
|
||||
_logger.Log(user.SiteId, LogLevel.Information, this, LogFunction.Create, "User Added {User}", User);
|
||||
}
|
||||
else
|
||||
{
|
||||
user.Password = ""; // remove sensitive information
|
||||
_logger.Log(user.SiteId, LogLevel.Error, this, LogFunction.Create, "Unable To Add User {User}", user);
|
||||
}
|
||||
|
||||
return User;
|
||||
}
|
||||
|
||||
public async Task<User> UpdateUser(User user)
|
||||
{
|
||||
IdentityUser identityuser = await _identityUserManager.FindByNameAsync(user.Username);
|
||||
if (identityuser != null)
|
||||
{
|
||||
identityuser.Email = user.Email;
|
||||
var valid = true;
|
||||
if (user.Password != "")
|
||||
{
|
||||
var validator = new PasswordValidator<IdentityUser>();
|
||||
var result = await validator.ValidateAsync(_identityUserManager, null, user.Password);
|
||||
valid = result.Succeeded;
|
||||
if (valid)
|
||||
{
|
||||
identityuser.PasswordHash = _identityUserManager.PasswordHasher.HashPassword(identityuser, user.Password);
|
||||
}
|
||||
}
|
||||
if (valid)
|
||||
{
|
||||
await _identityUserManager.UpdateAsync(identityuser);
|
||||
|
||||
user = _users.UpdateUser(user);
|
||||
_syncManager.AddSyncEvent(_tenantManager.GetAlias().TenantId, EntityNames.User, user.UserId, SyncEventActions.Update);
|
||||
_syncManager.AddSyncEvent(_tenantManager.GetAlias().TenantId, EntityNames.User, user.UserId, SyncEventActions.Refresh);
|
||||
user.Password = ""; // remove sensitive information
|
||||
_logger.Log(LogLevel.Information, this, LogFunction.Update, "User Updated {User}", user);
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.Log(user.SiteId, LogLevel.Error, this, LogFunction.Update, "Unable To Update User {Username}. Password Does Not Meet Complexity Requirements.", user.Username);
|
||||
user = null;
|
||||
}
|
||||
}
|
||||
|
||||
return user;
|
||||
}
|
||||
|
||||
public async Task DeleteUser(int userid, int siteid)
|
||||
{
|
||||
// remove user roles for site
|
||||
foreach (UserRole userrole in _userRoles.GetUserRoles(userid, siteid).ToList())
|
||||
{
|
||||
_userRoles.DeleteUserRole(userrole.UserRoleId);
|
||||
_logger.Log(LogLevel.Information, this, LogFunction.Delete, "User Role Deleted {UserRole}", userrole);
|
||||
}
|
||||
|
||||
// remove user folder for site
|
||||
var folder = _folders.GetFolder(siteid, $"Users/{userid}/");
|
||||
if (folder != null)
|
||||
{
|
||||
if (Directory.Exists(_folders.GetFolderPath(folder)))
|
||||
{
|
||||
Directory.Delete(_folders.GetFolderPath(folder), true);
|
||||
}
|
||||
_folders.DeleteFolder(folder.FolderId);
|
||||
_logger.Log(LogLevel.Information, this, LogFunction.Delete, "User Folder Deleted {Folder}", folder);
|
||||
}
|
||||
|
||||
// delete user if they are not a member of any other sites
|
||||
if (!_userRoles.GetUserRoles(userid, -1).Any())
|
||||
{
|
||||
// get identity user
|
||||
var user = _users.GetUser(userid, false);
|
||||
IdentityUser identityuser = await _identityUserManager.FindByNameAsync(user.Username);
|
||||
if (identityuser != null)
|
||||
{
|
||||
// delete identity user
|
||||
var result = await _identityUserManager.DeleteAsync(identityuser);
|
||||
if (result != null)
|
||||
{
|
||||
// delete user
|
||||
_users.DeleteUser(userid);
|
||||
_syncManager.AddSyncEvent(_tenantManager.GetAlias().TenantId, EntityNames.User, userid, SyncEventActions.Delete);
|
||||
_logger.Log(LogLevel.Information, this, LogFunction.Delete, "User Deleted {UserId}", userid, result.ToString());
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.Log(LogLevel.Error, this, LogFunction.Delete, "Error Deleting User {UserId}", userid);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<User> LoginUser(User user, bool setCookie, bool isPersistent)
|
||||
{
|
||||
user.IsAuthenticated = false;
|
||||
|
||||
IdentityUser identityuser = await _identityUserManager.FindByNameAsync(user.Username);
|
||||
if (identityuser != null)
|
||||
{
|
||||
var result = await _identitySignInManager.CheckPasswordSignInAsync(identityuser, user.Password, true);
|
||||
if (result.Succeeded)
|
||||
{
|
||||
var LastIPAddress = user.LastIPAddress ?? "";
|
||||
|
||||
user = _users.GetUser(user.Username);
|
||||
if (user.TwoFactorRequired)
|
||||
{
|
||||
var token = await _identityUserManager.GenerateTwoFactorTokenAsync(identityuser, "Email");
|
||||
user.TwoFactorCode = token;
|
||||
user.TwoFactorExpiry = DateTime.UtcNow.AddMinutes(10);
|
||||
_users.UpdateUser(user);
|
||||
|
||||
string body = "Dear " + user.DisplayName + ",\n\nYou requested a secure verification code to log in to your account. Please enter the secure verification code on the site:\n\n" + token +
|
||||
"\n\nPlease note that the code is only valid for 10 minutes so if you are unable to take action within that time period, you should initiate a new login on the site." +
|
||||
"\n\nThank You!";
|
||||
var notification = new Notification(user.SiteId, user, "User Verification Code", body);
|
||||
_notifications.AddNotification(notification);
|
||||
|
||||
_logger.Log(LogLevel.Information, this, LogFunction.Security, "User Verification Notification Sent For {Username}", user.Username);
|
||||
user.TwoFactorRequired = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
user = _users.GetUser(identityuser.UserName);
|
||||
if (user != null)
|
||||
{
|
||||
if (identityuser.EmailConfirmed)
|
||||
{
|
||||
user.IsAuthenticated = true;
|
||||
user.LastLoginOn = DateTime.UtcNow;
|
||||
user.LastIPAddress = LastIPAddress;
|
||||
_users.UpdateUser(user);
|
||||
_logger.Log(LogLevel.Information, this, LogFunction.Security, "User Login Successful {Username}", user.Username);
|
||||
|
||||
if (setCookie)
|
||||
{
|
||||
await _identitySignInManager.SignInAsync(identityuser, isPersistent);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.Log(LogLevel.Information, this, LogFunction.Security, "User Not Verified {Username}", user.Username);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (result.IsLockedOut)
|
||||
{
|
||||
var alias = _tenantManager.GetAlias();
|
||||
user = _users.GetUser(user.Username);
|
||||
string token = await _identityUserManager.GeneratePasswordResetTokenAsync(identityuser);
|
||||
string url = alias.Protocol + "://" + alias.Name + "/reset?name=" + user.Username + "&token=" + WebUtility.UrlEncode(token);
|
||||
string body = "Dear " + user.DisplayName + ",\n\nYou attempted multiple times unsuccessfully to log in to your account and it is now locked out. Please wait a few minutes and then try again... or use the link below to reset your password:\n\n" + url +
|
||||
"\n\nPlease note that the link is only valid for 24 hours so if you are unable to take action within that time period, you should initiate another password reset on the site." +
|
||||
"\n\nThank You!";
|
||||
var notification = new Notification(user.SiteId, user, "User Lockout", body);
|
||||
_notifications.AddNotification(notification);
|
||||
_logger.Log(LogLevel.Information, this, LogFunction.Security, "User Lockout Notification Sent For {Username}", user.Username);
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.Log(LogLevel.Information, this, LogFunction.Security, "User Login Failed {Username}", user.Username);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return user;
|
||||
}
|
||||
|
||||
public async Task<User> VerifyEmail(User user, string token)
|
||||
{
|
||||
IdentityUser identityuser = await _identityUserManager.FindByNameAsync(user.Username);
|
||||
if (identityuser != null && !string.IsNullOrEmpty(token))
|
||||
{
|
||||
var result = await _identityUserManager.ConfirmEmailAsync(identityuser, token);
|
||||
if (result.Succeeded)
|
||||
{
|
||||
_logger.Log(LogLevel.Information, this, LogFunction.Security, "Email Verified For {Username}", user.Username);
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Email Verification Failed For {Username} - Error {Error}", user.Username, string.Join(" ", result.Errors.ToList().Select(e => e.Description)));
|
||||
user = null;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Email Verification Failed For {Username}And Token {Token}", user.Username, token);
|
||||
user = null;
|
||||
}
|
||||
return user;
|
||||
}
|
||||
public async Task ForgotPassword(User user)
|
||||
{
|
||||
IdentityUser identityuser = await _identityUserManager.FindByNameAsync(user.Username);
|
||||
if (identityuser != null)
|
||||
{
|
||||
var alias = _tenantManager.GetAlias();
|
||||
user = _users.GetUser(user.Username);
|
||||
string token = await _identityUserManager.GeneratePasswordResetTokenAsync(identityuser);
|
||||
string url = alias.Protocol + "://" + alias.Name + "/reset?name=" + user.Username + "&token=" + WebUtility.UrlEncode(token);
|
||||
string body = "Dear " + user.DisplayName + ",\n\nYou recently requested to reset your password. Please use the link below to complete the process:\n\n" + url +
|
||||
"\n\nPlease note that the link is only valid for 24 hours so if you are unable to take action within that time period, you should initiate another password reset on the site." +
|
||||
"\n\nIf you did not request to reset your password you can safely ignore this message." +
|
||||
"\n\nThank You!";
|
||||
|
||||
var notification = new Notification(_tenantManager.GetAlias().SiteId, user, "User Password Reset", body);
|
||||
_notifications.AddNotification(notification);
|
||||
_logger.Log(LogLevel.Information, this, LogFunction.Security, "Password Reset Notification Sent For {Username}", user.Username);
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Password Reset Notification Failed For {Username}", user.Username);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<User> ResetPassword(User user, string token)
|
||||
{
|
||||
IdentityUser identityuser = await _identityUserManager.FindByNameAsync(user.Username);
|
||||
if (identityuser != null && !string.IsNullOrEmpty(token))
|
||||
{
|
||||
var result = await _identityUserManager.ResetPasswordAsync(identityuser, token, user.Password);
|
||||
if (result.Succeeded)
|
||||
{
|
||||
_logger.Log(LogLevel.Information, this, LogFunction.Security, "Password Reset For {Username}", user.Username);
|
||||
user.Password = "";
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.Log(LogLevel.Information, this, LogFunction.Security, "Password Reset Failed For {Username} - Error {Error}", user.Username, string.Join(" ", result.Errors.ToList().Select(e => e.Description)));
|
||||
user = null;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Password Reset Failed For {Username} And Token {Token}", user.Username, token);
|
||||
user = null;
|
||||
}
|
||||
return user;
|
||||
}
|
||||
|
||||
public User VerifyTwoFactor(User user, string token)
|
||||
{
|
||||
user = _users.GetUser(user.Username);
|
||||
if (user != null)
|
||||
{
|
||||
if (user.TwoFactorRequired && user.TwoFactorCode == token && DateTime.UtcNow < user.TwoFactorExpiry)
|
||||
{
|
||||
user.IsAuthenticated = true;
|
||||
}
|
||||
}
|
||||
return user;
|
||||
}
|
||||
|
||||
public async Task<User> LinkExternalAccount(User user, string token, string type, string key, string name)
|
||||
{
|
||||
IdentityUser identityuser = await _identityUserManager.FindByNameAsync(user.Username);
|
||||
if (identityuser != null && !string.IsNullOrEmpty(token))
|
||||
{
|
||||
var result = await _identityUserManager.ConfirmEmailAsync(identityuser, token);
|
||||
if (result.Succeeded)
|
||||
{
|
||||
// make LoginProvider multi-tenant aware
|
||||
type += ":" + user.SiteId.ToString();
|
||||
await _identityUserManager.AddLoginAsync(identityuser, new UserLoginInfo(type, key, name));
|
||||
_logger.Log(LogLevel.Information, this, LogFunction.Security, "External Login Linkage Successful For {Username} And Provider {Provider}", user.Username, type);
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.Log(LogLevel.Error, this, LogFunction.Security, "External Login Linkage Failed For {Username} - Error {Error}", user.Username, string.Join(" ", result.Errors.ToList().Select(e => e.Description)));
|
||||
user = null;
|
||||
}
|
||||
}
|
||||
return user;
|
||||
}
|
||||
|
||||
public async Task<bool> ValidatePassword(string password)
|
||||
{
|
||||
var validator = new PasswordValidator<IdentityUser>();
|
||||
var result = await validator.ValidateAsync(_identityUserManager, null, password);
|
||||
return result.Succeeded;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,35 @@
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Oqtane.Databases.Interfaces;
|
||||
using Oqtane.Migrations.EntityBuilders;
|
||||
using Oqtane.Repository;
|
||||
using Oqtane.Shared;
|
||||
|
||||
namespace Oqtane.Migrations.Tenant
|
||||
{
|
||||
[DbContext(typeof(TenantDBContext))]
|
||||
[Migration("Tenant.04.00.01.01")]
|
||||
public class AddNotificationIsRead : MultiDatabaseMigration
|
||||
{
|
||||
|
||||
public AddNotificationIsRead(IDatabase database) : base(database)
|
||||
{
|
||||
}
|
||||
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
var notificationEntityBuilder = new NotificationEntityBuilder(migrationBuilder, ActiveDatabase);
|
||||
notificationEntityBuilder.AddBooleanColumn("IsRead", true);
|
||||
notificationEntityBuilder.UpdateColumn("IsRead", "1", "bool", "");
|
||||
}
|
||||
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
var notificationEntityBuilder = new NotificationEntityBuilder(migrationBuilder, ActiveDatabase);
|
||||
notificationEntityBuilder.DropColumn("IsRead");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -3,7 +3,7 @@
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<Configurations>Debug;Release</Configurations>
|
||||
<Version>4.0.0</Version>
|
||||
<Version>4.0.1</Version>
|
||||
<Product>Oqtane</Product>
|
||||
<Authors>Shaun Walker</Authors>
|
||||
<Company>.NET Foundation</Company>
|
||||
@ -11,7 +11,7 @@
|
||||
<Copyright>.NET Foundation</Copyright>
|
||||
<PackageProjectUrl>https://www.oqtane.org</PackageProjectUrl>
|
||||
<PackageLicenseUrl>https://github.com/oqtane/oqtane.framework/blob/dev/LICENSE</PackageLicenseUrl>
|
||||
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v4.0.0</PackageReleaseNotes>
|
||||
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v4.0.1</PackageReleaseNotes>
|
||||
<RepositoryUrl>https://github.com/oqtane/oqtane.framework</RepositoryUrl>
|
||||
<RepositoryType>Git</RepositoryType>
|
||||
<RootNamespace>Oqtane</RootNamespace>
|
||||
|
@ -114,7 +114,7 @@ namespace Oqtane.Pages
|
||||
}
|
||||
|
||||
var site = _sites.InitializeSite(alias);
|
||||
if (site != null && !site.IsDeleted && site.Runtime != "Hybrid")
|
||||
if (site != null && (!site.IsDeleted || url.Contains("admin/site")) && site.Runtime != "Hybrid")
|
||||
{
|
||||
Route route = new Route(url, alias.Path);
|
||||
|
||||
|
@ -6,6 +6,8 @@ namespace Oqtane.Repository
|
||||
public interface INotificationRepository
|
||||
{
|
||||
IEnumerable<Notification> GetNotifications(int siteId, int fromUserId, int toUserId);
|
||||
IEnumerable<Notification> GetNotifications(int siteId, int fromUserId, int toUserId, int count, bool isRead);
|
||||
int GetNotificationCount(int siteId, int fromUserId, int toUserId, bool isRead);
|
||||
Notification AddNotification(Notification notification);
|
||||
Notification UpdateNotification(Notification notification);
|
||||
Notification GetNotification(int notificationId);
|
||||
|
@ -101,6 +101,7 @@ namespace Oqtane.Repository
|
||||
ModuleDefinition.IsPortable = moduleDefinition.IsPortable;
|
||||
ModuleDefinition.Resources = moduleDefinition.Resources;
|
||||
ModuleDefinition.IsEnabled = moduleDefinition.IsEnabled;
|
||||
ModuleDefinition.PackageName = moduleDefinition.PackageName;
|
||||
}
|
||||
|
||||
return ModuleDefinition;
|
||||
|
@ -33,6 +33,54 @@ namespace Oqtane.Repository
|
||||
.ToList();
|
||||
}
|
||||
|
||||
public IEnumerable<Notification> GetNotifications(int siteId, int fromUserId, int toUserId, int count, bool isRead)
|
||||
{
|
||||
if (toUserId == -1 && fromUserId == -1)
|
||||
{
|
||||
return _db.Notification
|
||||
.Where(item => item.SiteId == siteId)
|
||||
.Where(item => item.IsDelivered == false && item.IsDeleted == false)
|
||||
.Where(item => item.SendOn == null || item.SendOn < System.DateTime.UtcNow)
|
||||
.Where(item => item.IsRead == isRead)
|
||||
.OrderByDescending(item => item.CreatedOn)
|
||||
.ToList()
|
||||
.Take(count);
|
||||
}
|
||||
|
||||
return _db.Notification
|
||||
.Where(item => item.SiteId == siteId)
|
||||
.Where(item => item.ToUserId == toUserId || toUserId == -1)
|
||||
.Where(item => item.FromUserId == fromUserId || fromUserId == -1)
|
||||
.Where(item => item.IsRead == isRead)
|
||||
.OrderByDescending(item => item.CreatedOn)
|
||||
.ToList()
|
||||
.Take(count);
|
||||
}
|
||||
|
||||
public int GetNotificationCount(int siteId, int fromUserId, int toUserId, bool isRead)
|
||||
{
|
||||
if (toUserId == -1 && fromUserId == -1)
|
||||
{
|
||||
return _db.Notification
|
||||
.Where(item => item.SiteId == siteId)
|
||||
.Where(item => item.IsDelivered == false && item.IsDeleted == false)
|
||||
.Where(item => item.SendOn == null || item.SendOn < System.DateTime.UtcNow)
|
||||
.Where(item => item.IsRead == isRead)
|
||||
.ToList()
|
||||
.Count();
|
||||
|
||||
}
|
||||
|
||||
return _db.Notification
|
||||
.Where(item => item.SiteId == siteId)
|
||||
.Where(item => item.ToUserId == toUserId || toUserId == -1)
|
||||
.Where(item => item.FromUserId == fromUserId || fromUserId == -1)
|
||||
.Where(item => item.IsRead == isRead)
|
||||
.ToList()
|
||||
.Count();
|
||||
}
|
||||
|
||||
|
||||
public Notification AddNotification(Notification notification)
|
||||
{
|
||||
_db.Notification.Add(notification);
|
||||
|
@ -89,6 +89,7 @@ namespace Oqtane.Repository
|
||||
Theme.Containers = theme.Containers;
|
||||
Theme.ThemeSettingsType = theme.ThemeSettingsType;
|
||||
Theme.ContainerSettingsType = theme.ContainerSettingsType;
|
||||
Theme.PackageName = theme.PackageName;
|
||||
Themes.Add(Theme);
|
||||
}
|
||||
|
||||
|
@ -13,7 +13,7 @@
|
||||
</div>
|
||||
|
||||
@code {
|
||||
private string resourceType = "[Owner].[Module].Settings, [Owner].[Module].Client.Oqtane"; // for localization
|
||||
private string resourceType = "[Owner].Module.[Module].Settings, [Owner].Module.[Module].Client.Oqtane"; // for localization
|
||||
public override string Title => "[Module] Settings";
|
||||
|
||||
string _value;
|
||||
|
@ -2,7 +2,6 @@
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<RazorLangVersion>3.0</RazorLangVersion>
|
||||
<Version>1.0.0</Version>
|
||||
<Authors>[Owner]</Authors>
|
||||
<Company>[Owner]</Company>
|
||||
|
@ -1,7 +1,7 @@
|
||||
cp -f "../Client/bin/Debug/net7.0/[Owner].Module.[Module].Client.Oqtane.dll" "../../oqtane.framework/Oqtane.Server/bin/Debug/net7.0/"
|
||||
cp -f "../Client/bin/Debug/net7.0/[Owner].Module.[Module].Client.Oqtane.pdb" "../../oqtane.framework/Oqtane.Server/bin/Debug/net7.0/"
|
||||
cp -f "../Server/bin/Debug/net7.0/[Owner].Module.[Module].Server.Oqtane.dll" "../../oqtane.framework/Oqtane.Server/bin/Debug/net7.0/"
|
||||
cp -f "../Server/bin/Debug/net7.0/[Owner].Module.[Module].Server.Oqtane.pdb" "../../oqtane.framework/Oqtane.Server/bin/Debug/net7.0/"
|
||||
cp -f "../Shared/bin/Debug/net7.0/[Owner].Module.[Module].Shared.Oqtane.dll" "../../oqtane.framework/Oqtane.Server/bin/Debug/net7.0/"
|
||||
cp -f "../Shared/bin/Debug/net7.0/[Owner].Module.[Module].Shared.Oqtane.pdb" "../../oqtane.framework/Oqtane.Server/bin/Debug/net7.0/"
|
||||
cp -f "../Client/bin/Debug/net7.0/[Owner].Module.[Module].Client.Oqtane.dll" "../../oqtane.framework/Oqtane.Server/bin/Debug/net7.0/"
|
||||
cp -f "../Client/bin/Debug/net7.0/[Owner].Module.[Module].Client.Oqtane.pdb" "../../oqtane.framework/Oqtane.Server/bin/Debug/net7.0/"
|
||||
cp -f "../Server/bin/Debug/net7.0/[Owner].Module.[Module].Server.Oqtane.dll" "../../oqtane.framework/Oqtane.Server/bin/Debug/net7.0/"
|
||||
cp -f "../Server/bin/Debug/net7.0/[Owner].Module.[Module].Server.Oqtane.pdb" "../../oqtane.framework/Oqtane.Server/bin/Debug/net7.0/"
|
||||
cp -f "../Shared/bin/Debug/net7.0/[Owner].Module.[Module].Shared.Oqtane.dll" "../../oqtane.framework/Oqtane.Server/bin/Debug/net7.0/"
|
||||
cp -f "../Shared/bin/Debug/net7.0/[Owner].Module.[Module].Shared.Oqtane.pdb" "../../oqtane.framework/Oqtane.Server/bin/Debug/net7.0/"
|
||||
cp -rf "../Server/wwwroot/"* "../../oqtane.framework/Oqtane.Server/wwwroot/"
|
@ -1,2 +1,2 @@
|
||||
"..\..\oqtane.framework\oqtane.package\nuget.exe" pack [Owner].Module.[Module].nuspec
|
||||
"..\..\oqtane.framework\oqtane.package\nuget.exe" pack [Owner].Module.[Module].nuspec
|
||||
cp -f "*.nupkg" "..\..\oqtane.framework\Oqtane.Server\Packages\"
|
@ -20,9 +20,7 @@ Global
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{3AB6FCC9-EFEB-4C0E-A2CF-8103914C5196}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{3AB6FCC9-EFEB-4C0E-A2CF-8103914C5196}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{3AB6FCC9-EFEB-4C0E-A2CF-8103914C5196}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{3AB6FCC9-EFEB-4C0E-A2CF-8103914C5196}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{AA8E58A1-CD09-4208-BF66-A8BB341FD669}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{AA8E58A1-CD09-4208-BF66-A8BB341FD669}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{AA8E58A1-CD09-4208-BF66-A8BB341FD669}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user