commit
1b7c810eeb
2
.gitignore
vendored
2
.gitignore
vendored
@ -16,7 +16,7 @@ _ReSharper.Caches
|
|||||||
Oqtane.Server/appsettings.json
|
Oqtane.Server/appsettings.json
|
||||||
Oqtane.Server/Data
|
Oqtane.Server/Data
|
||||||
|
|
||||||
/Oqtane.Server/Properties/PublishProfiles/FolderProfile.pubxml
|
Oqtane.Server/Properties/PublishProfiles/FolderProfile.pubxml
|
||||||
Oqtane.Server/Content
|
Oqtane.Server/Content
|
||||||
Oqtane.Server/Packages
|
Oqtane.Server/Packages
|
||||||
Oqtane.Server/wwwroot/Content
|
Oqtane.Server/wwwroot/Content
|
||||||
|
@ -13,7 +13,7 @@
|
|||||||
{
|
{
|
||||||
string url = NavigateUrl(p.Path);
|
string url = NavigateUrl(p.Path);
|
||||||
<p class="col-md-2 mx-auto text-center mb-3">
|
<p class="col-md-2 mx-auto text-center mb-3">
|
||||||
<NavLink class="nav-link text-primary" href="@url" Match="NavLinkMatch.All">
|
<NavLink class="nav-link text-body" href="@url" Match="NavLinkMatch.All">
|
||||||
<h2><span class="@p.Icon" aria-hidden="true"></span></h2>
|
<h2><span class="@p.Icon" aria-hidden="true"></span></h2>
|
||||||
<p class="lead">@((MarkupString)SharedLocalizer[p.Name].ToString().Replace(" ", "<br />"))</p>
|
<p class="lead">@((MarkupString)SharedLocalizer[p.Name].ToString().Replace(" ", "<br />"))</p>
|
||||||
</NavLink>
|
</NavLink>
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
@inject NavigationManager NavigationManager
|
@inject NavigationManager NavigationManager
|
||||||
@inject IFileService FileService
|
@inject IFileService FileService
|
||||||
@inject IFolderService FolderService
|
@inject IFolderService FolderService
|
||||||
|
@inject ISettingService SettingService
|
||||||
@inject IStringLocalizer<Add> Localizer
|
@inject IStringLocalizer<Add> Localizer
|
||||||
@inject IStringLocalizer<SharedResources> SharedLocalizer
|
@inject IStringLocalizer<SharedResources> SharedLocalizer
|
||||||
|
|
||||||
@ -80,6 +81,7 @@
|
|||||||
{
|
{
|
||||||
validated = true;
|
validated = true;
|
||||||
var interop = new Interop(JSRuntime);
|
var interop = new Interop(JSRuntime);
|
||||||
|
|
||||||
if (await interop.FormValid(form))
|
if (await interop.FormValid(form))
|
||||||
{
|
{
|
||||||
if (_url == string.Empty || _folderId == -1)
|
if (_url == string.Empty || _folderId == -1)
|
||||||
@ -93,7 +95,7 @@
|
|||||||
_name = _url.Substring(_url.LastIndexOf("/", StringComparison.Ordinal) + 1);
|
_name = _url.Substring(_url.LastIndexOf("/", StringComparison.Ordinal) + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!Constants.UploadableFiles.Split(',').Contains(Path.GetExtension(_name).ToLower().Replace(".", "")))
|
if (!PageState.Site.UploadableFiles.Split(',').Contains(Path.GetExtension(_name).ToLower().Replace(".", "")))
|
||||||
{
|
{
|
||||||
AddModuleMessage(Localizer["Message.Download.InvalidExtension"], MessageType.Warning);
|
AddModuleMessage(Localizer["Message.Download.InvalidExtension"], MessageType.Warning);
|
||||||
return;
|
return;
|
||||||
|
@ -45,7 +45,7 @@
|
|||||||
<div class="row mb-1 align-items-center">
|
<div class="row mb-1 align-items-center">
|
||||||
<Label Class="col-sm-3" For="retention" HelpText="Number of log entries to retain for this job" ResourceKey="RetentionLog">Retention Log (Items): </Label>
|
<Label Class="col-sm-3" For="retention" HelpText="Number of log entries to retain for this job" ResourceKey="RetentionLog">Retention Log (Items): </Label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
<input id="retention" class="form-control" @bind="@_retentionHistory" maxlength="4" required />
|
<input id="retention" type="number" min="0" step ="1" class="form-control" @bind="@_retentionHistory" maxlength="4" required />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row mb-1 align-items-center">
|
<div class="row mb-1 align-items-center">
|
||||||
|
@ -131,8 +131,6 @@ else
|
|||||||
var interop = new Interop(JSRuntime);
|
var interop = new Interop(JSRuntime);
|
||||||
var localizationCookieValue = CookieRequestCultureProvider.MakeCookieValue(new RequestCulture(culture));
|
var localizationCookieValue = CookieRequestCultureProvider.MakeCookieValue(new RequestCulture(culture));
|
||||||
await interop.SetCookie(CookieRequestCultureProvider.DefaultCookieName, localizationCookieValue, 360);
|
await interop.SetCookie(CookieRequestCultureProvider.DefaultCookieName, localizationCookieValue, 360);
|
||||||
|
|
||||||
NavigationManager.NavigateTo(NavigationManager.Uri, true);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
110
Oqtane.Client/Modules/Admin/Languages/Edit.razor
Normal file
110
Oqtane.Client/Modules/Admin/Languages/Edit.razor
Normal file
@ -0,0 +1,110 @@
|
|||||||
|
@namespace Oqtane.Modules.Admin.Languages
|
||||||
|
@inherits ModuleBase
|
||||||
|
@using System.Globalization
|
||||||
|
@using Microsoft.AspNetCore.Localization
|
||||||
|
@inject NavigationManager NavigationManager
|
||||||
|
@inject ILocalizationService LocalizationService
|
||||||
|
@inject ILanguageService LanguageService
|
||||||
|
@inject IPackageService PackageService
|
||||||
|
@inject IStringLocalizer<Edit> Localizer
|
||||||
|
@inject IStringLocalizer<SharedResources> SharedLocalizer
|
||||||
|
|
||||||
|
@if (_code == null)
|
||||||
|
{
|
||||||
|
<p><em>@SharedLocalizer["Loading"]</em></p>
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<TabStrip>
|
||||||
|
<TabPanel Name="Manage" ResourceKey="Manage" Heading="Manage">
|
||||||
|
<form @ref="form" class="@(validated ? "was-validated" : "needs-validation")" novalidate>
|
||||||
|
<div class="container">
|
||||||
|
<div class="row mb-1 align-items-center">
|
||||||
|
<Label Class="col-sm-3" For="name" HelpText="Name Of The Language" ResourceKey="Name">Name:</Label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<input id="code" class="form-control" @bind="@_code" readonly/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row mb-1 align-items-center">
|
||||||
|
<Label Class="col-sm-3" For="default" HelpText="Indicates Whether Or Not This Language Is The Default For The Site" ResourceKey="IsDefault">Default?</Label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<select id="default" class="form-select" @bind="@_default" required>
|
||||||
|
<option value="True">@SharedLocalizer["Yes"]</option>
|
||||||
|
<option value="False">@SharedLocalizer["No"]</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<button type="button" class="btn btn-success" @onclick="SaveLanguage">@SharedLocalizer["Save"]</button>
|
||||||
|
<NavLink class="btn btn-secondary" href="@NavigateUrl()">@SharedLocalizer["Cancel"]</NavLink>
|
||||||
|
</form>
|
||||||
|
</TabPanel>
|
||||||
|
</TabStrip>
|
||||||
|
}
|
||||||
|
|
||||||
|
@code {
|
||||||
|
private ElementReference form;
|
||||||
|
private bool validated = false;
|
||||||
|
private int _languageId = -1;
|
||||||
|
private string _code = string.Empty;
|
||||||
|
private string _cultureName = string.Empty;
|
||||||
|
private string _default = "False";
|
||||||
|
private List<Language> _languages;
|
||||||
|
|
||||||
|
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Admin;
|
||||||
|
|
||||||
|
protected override async Task OnInitializedAsync()
|
||||||
|
{
|
||||||
|
_languageId = Int32.Parse(PageState.QueryString["id"]);
|
||||||
|
_languages = await LanguageService.GetLanguagesAsync(PageState.Site.SiteId, Constants.ClientId);
|
||||||
|
Language language = _languages.Where(x => x.LanguageId == _languageId).FirstOrDefault();
|
||||||
|
if (language != null)
|
||||||
|
{
|
||||||
|
_code = language.Code;
|
||||||
|
_cultureName = language.Name;
|
||||||
|
_default = language.IsDefault.ToString();
|
||||||
|
if (language.SiteId == null)
|
||||||
|
{
|
||||||
|
language.SiteId = PageState.Site.SiteId;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task SaveLanguage()
|
||||||
|
{
|
||||||
|
Language language = _languages.Where(x => x.LanguageId == _languageId).FirstOrDefault();
|
||||||
|
if (language != null)
|
||||||
|
{
|
||||||
|
language.IsDefault = Boolean.Parse(_default);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await LanguageService.EditLanguageAsync(language);
|
||||||
|
|
||||||
|
if (language.IsDefault)
|
||||||
|
{
|
||||||
|
await SetCultureAsync(language.Code);
|
||||||
|
}
|
||||||
|
|
||||||
|
await logger.LogInformation("Language Edited {Language}", language);
|
||||||
|
|
||||||
|
NavigationManager.NavigateTo(NavigateUrl());
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
await logger.LogError(ex, "Error Editing Language {Language} {Error}", language, ex.Message);
|
||||||
|
AddModuleMessage(Localizer["Error.Language.Edit"], MessageType.Error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task SetCultureAsync(string culture)
|
||||||
|
{
|
||||||
|
if (culture != CultureInfo.CurrentUICulture.Name)
|
||||||
|
{
|
||||||
|
var interop = new Interop(JSRuntime);
|
||||||
|
var localizationCookieValue = CookieRequestCultureProvider.MakeCookieValue(new RequestCulture(culture));
|
||||||
|
await interop.SetCookie(CookieRequestCultureProvider.DefaultCookieName, localizationCookieValue, 360);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -16,6 +16,7 @@ else
|
|||||||
|
|
||||||
<Pager Items="@_languages" SearchProperties="Name,Code">
|
<Pager Items="@_languages" SearchProperties="Name,Code">
|
||||||
<Header>
|
<Header>
|
||||||
|
<th style="width: 1px;"> </th>
|
||||||
<th style="width: 1px;"> </th>
|
<th style="width: 1px;"> </th>
|
||||||
<th>@SharedLocalizer["Name"]</th>
|
<th>@SharedLocalizer["Name"]</th>
|
||||||
<th>@Localizer["Code"]</th>
|
<th>@Localizer["Code"]</th>
|
||||||
@ -27,6 +28,12 @@ else
|
|||||||
}
|
}
|
||||||
</Header>
|
</Header>
|
||||||
<Row>
|
<Row>
|
||||||
|
<td>
|
||||||
|
@if (!context.IsDefault)
|
||||||
|
{
|
||||||
|
<ActionLink Action="Edit" Text="Edit" Parameters="@($"id=" + context.LanguageId.ToString())" ResourceKey="EditLanguage" />
|
||||||
|
}
|
||||||
|
</td>
|
||||||
<td><ActionDialog Header="Delete Language" Message="@string.Format(Localizer["Confirm.Language.Delete"], context.Name)" Action="Delete" Security="SecurityAccessLevel.Admin" Class="btn btn-danger" OnClick="@(async () => await DeleteLanguage(context))" Disabled="@((context.IsDefault && _languages.Count > 2) || context.Code == Constants.DefaultCulture)" ResourceKey="DeleteLanguage" /></td>
|
<td><ActionDialog Header="Delete Language" Message="@string.Format(Localizer["Confirm.Language.Delete"], context.Name)" Action="Delete" Security="SecurityAccessLevel.Admin" Class="btn btn-danger" OnClick="@(async () => await DeleteLanguage(context))" Disabled="@((context.IsDefault && _languages.Count > 2) || context.Code == Constants.DefaultCulture)" ResourceKey="DeleteLanguage" /></td>
|
||||||
<td>@context.Name</td>
|
<td>@context.Name</td>
|
||||||
<td>@context.Code</td>
|
<td>@context.Code</td>
|
||||||
|
@ -38,10 +38,10 @@
|
|||||||
<div class="form-group mt-2">
|
<div class="form-group mt-2">
|
||||||
@if (!_alwaysremember)
|
@if (!_alwaysremember)
|
||||||
{
|
{
|
||||||
<div class="form-check">
|
<div class="form-check">
|
||||||
<input id="remember" type="checkbox" class="form-check-input" @bind="@_remember" />
|
<input id="remember" type="checkbox" class="form-check-input" @bind="@_remember" />
|
||||||
<Label Class="control-label" For="remember" HelpText="Specify if you would like to be signed back in automatically the next time you visit this site" ResourceKey="Remember">Remember Me?</Label>
|
<Label Class="control-label" For="remember" HelpText="Specify if you would like to be signed back in automatically the next time you visit this site" ResourceKey="Remember">Remember Me?</Label>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
<button type="button" class="btn btn-primary" @onclick="Login">@SharedLocalizer["Login"]</button>
|
<button type="button" class="btn btn-primary" @onclick="Login">@SharedLocalizer["Login"]</button>
|
||||||
@ -99,6 +99,7 @@
|
|||||||
{
|
{
|
||||||
_allowexternallogin = (SettingService.GetSetting(PageState.Site.Settings, "ExternalLogin:ProviderType", "") != "") ? true : false;
|
_allowexternallogin = (SettingService.GetSetting(PageState.Site.Settings, "ExternalLogin:ProviderType", "") != "") ? true : false;
|
||||||
_allowsitelogin = bool.Parse(SettingService.GetSetting(PageState.Site.Settings, "LoginOptions:AllowSiteLogin", "true"));
|
_allowsitelogin = bool.Parse(SettingService.GetSetting(PageState.Site.Settings, "LoginOptions:AllowSiteLogin", "true"));
|
||||||
|
_alwaysremember = bool.Parse(SettingService.GetSetting(PageState.Site.Settings, "LoginOptions:AlwaysRemember", "false"));
|
||||||
|
|
||||||
_togglepassword = SharedLocalizer["ShowPassword"];
|
_togglepassword = SharedLocalizer["ShowPassword"];
|
||||||
|
|
||||||
@ -155,10 +156,6 @@
|
|||||||
AddModuleMessage(Localizer["ExternalLoginStatus." + PageState.QueryString["status"]], MessageType.Info);
|
AddModuleMessage(Localizer["ExternalLoginStatus." + PageState.QueryString["status"]], MessageType.Info);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (PageState.Site.Settings.TryGetValue("LoginOptions:AlwaysRemember", out string alwaysRememberStr))
|
|
||||||
{
|
|
||||||
_alwaysremember = Convert.ToBoolean(alwaysRememberStr);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
@ -194,13 +191,7 @@
|
|||||||
|
|
||||||
if (!twofactor)
|
if (!twofactor)
|
||||||
{
|
{
|
||||||
bool alwaysRemember = false;
|
_remember = _alwaysremember || _remember;
|
||||||
if (PageState.Site.Settings.TryGetValue("LoginOptions:AlwaysRemember", out string alwaysRememberStr))
|
|
||||||
{
|
|
||||||
alwaysRemember = Convert.ToBoolean(alwaysRememberStr);
|
|
||||||
}
|
|
||||||
bool remember = alwaysRemember || _remember;
|
|
||||||
_remember = remember;
|
|
||||||
user = await UserService.LoginUserAsync(user, hybrid, _remember);
|
user = await UserService.LoginUserAsync(user, hybrid, _remember);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -81,7 +81,7 @@ else
|
|||||||
<div class="row mb-1 align-items-center">
|
<div class="row mb-1 align-items-center">
|
||||||
<Label Class="col-sm-3" For="retention" HelpText="Number of days of events to retain" ResourceKey="Retention">Retention (Days): </Label>
|
<Label Class="col-sm-3" For="retention" HelpText="Number of days of events to retain" ResourceKey="Retention">Retention (Days): </Label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
<input id="retention" class="form-control" @bind="@_retention" />
|
<input id="retention" type="number" min="0" step="1" class="form-control" @bind="@_retention" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -97,7 +97,7 @@ else
|
|||||||
private string _rows = "10";
|
private string _rows = "10";
|
||||||
private int _page = 1;
|
private int _page = 1;
|
||||||
private List<Log> _logs;
|
private List<Log> _logs;
|
||||||
private string _retention = "";
|
private int _retention = 30;
|
||||||
|
|
||||||
public override string UrlParametersTemplate => "/{level}/{function}/{rows}/{page}";
|
public override string UrlParametersTemplate => "/{level}/{function}/{rows}/{page}";
|
||||||
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Host;
|
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Host;
|
||||||
@ -126,7 +126,7 @@ else
|
|||||||
await GetLogs();
|
await GetLogs();
|
||||||
|
|
||||||
var settings = await SettingService.GetSiteSettingsAsync(PageState.Site.SiteId);
|
var settings = await SettingService.GetSiteSettingsAsync(PageState.Site.SiteId);
|
||||||
_retention = SettingService.GetSetting(settings, "LogRetention", "30");
|
_retention = int.Parse( SettingService.GetSetting(settings, "LogRetention", "30"));
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
@ -218,7 +218,7 @@ else
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
var settings = await SettingService.GetSiteSettingsAsync(PageState.Site.SiteId);
|
var settings = await SettingService.GetSiteSettingsAsync(PageState.Site.SiteId);
|
||||||
settings = SettingService.SetSetting(settings, "LogRetention", _retention, true);
|
settings = SettingService.SetSetting(settings, "LogRetention", _retention.ToString(), true);
|
||||||
await SettingService.UpdateSiteSettingsAsync(settings, PageState.Site.SiteId);
|
await SettingService.UpdateSiteSettingsAsync(settings, PageState.Site.SiteId);
|
||||||
|
|
||||||
AddModuleMessage(Localizer["Success.SaveSiteSettings"], MessageType.Success);
|
AddModuleMessage(Localizer["Success.SaveSiteSettings"], MessageType.Success);
|
||||||
|
@ -8,6 +8,8 @@
|
|||||||
@inject NavigationManager NavigationManager
|
@inject NavigationManager NavigationManager
|
||||||
@inject IStringLocalizer<Edit> Localizer
|
@inject IStringLocalizer<Edit> Localizer
|
||||||
@inject IStringLocalizer<SharedResources> SharedLocalizer
|
@inject IStringLocalizer<SharedResources> SharedLocalizer
|
||||||
|
@inject IPageModuleService PageModuleService
|
||||||
|
@inject IModuleService ModuleService
|
||||||
|
|
||||||
@if (_initialized)
|
@if (_initialized)
|
||||||
{
|
{
|
||||||
@ -131,10 +133,22 @@
|
|||||||
<button type="button" class="btn btn-success" @onclick="SaveModuleDefinition">@SharedLocalizer["Save"]</button>
|
<button type="button" class="btn btn-success" @onclick="SaveModuleDefinition">@SharedLocalizer["Save"]</button>
|
||||||
<NavLink class="btn btn-secondary" href="@NavigateUrl()">@SharedLocalizer["Cancel"]</NavLink>
|
<NavLink class="btn btn-secondary" href="@NavigateUrl()">@SharedLocalizer["Cancel"]</NavLink>
|
||||||
</TabPanel>
|
</TabPanel>
|
||||||
|
<TabPanel Name="Pages" ResourceKey="Pages" Heading="Pages">
|
||||||
|
<Pager Items="@_pagesWithModules" RowClass="align-middle">
|
||||||
|
<Header>
|
||||||
|
<th style="width: 1px;"> </th>
|
||||||
|
<th>@SharedLocalizer["Name"]</th>
|
||||||
|
</Header>
|
||||||
|
<Row>
|
||||||
|
<td><button type="button" class="btn btn-secondary" @onclick="@(async () => NavigationManager.NavigateTo(Browse(context)))">@Localizer["Browse"]</button></td>
|
||||||
|
<td>@(string.IsNullOrEmpty(context.Title) ? @context.Name : @context.Title )</td>
|
||||||
|
</Row>
|
||||||
|
</Pager>
|
||||||
|
</TabPanel>
|
||||||
<TabPanel Name="Translations" ResourceKey="Translations" Heading="Translations">
|
<TabPanel Name="Translations" ResourceKey="Translations" Heading="Translations">
|
||||||
@if (_languages != null && _languages.Count > 0)
|
@if (_languages != null && _languages.Count > 0)
|
||||||
{
|
{
|
||||||
<Pager Items="@_languages">
|
<Pager Items="@_languages">
|
||||||
<Header>
|
<Header>
|
||||||
<th>@SharedLocalizer["Name"]</th>
|
<th>@SharedLocalizer["Name"]</th>
|
||||||
<th>@Localizer["Code"]</th>
|
<th>@Localizer["Code"]</th>
|
||||||
@ -240,6 +254,7 @@
|
|||||||
private DateTime _createdon;
|
private DateTime _createdon;
|
||||||
private string _modifiedby;
|
private string _modifiedby;
|
||||||
private DateTime _modifiedon;
|
private DateTime _modifiedon;
|
||||||
|
private List<Page> _pagesWithModules;
|
||||||
|
|
||||||
#pragma warning disable 649
|
#pragma warning disable 649
|
||||||
private PermissionGrid _permissionGrid;
|
private PermissionGrid _permissionGrid;
|
||||||
@ -291,6 +306,18 @@
|
|||||||
_languages = _languages.OrderBy(item => item.Name).ToList();
|
_languages = _languages.OrderBy(item => item.Name).ToList();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Group modules by PageId
|
||||||
|
// Get distinct PageIds where modules are present
|
||||||
|
var distinctPageIds = PageState.Modules
|
||||||
|
.Where(md => md.ModuleDefinition.ModuleDefinitionId == _moduleDefinitionId && md.IsDeleted == false)
|
||||||
|
.Select(md => md.PageId)
|
||||||
|
.Distinct();
|
||||||
|
|
||||||
|
// Filter and retrieve the corresponding pages
|
||||||
|
_pagesWithModules = PageState.Pages
|
||||||
|
.Where(pg => distinctPageIds.Contains(pg.PageId) && pg.IsDeleted == false)
|
||||||
|
.ToList();
|
||||||
|
|
||||||
_initialized = true;
|
_initialized = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -439,5 +466,5 @@
|
|||||||
AddModuleMessage(Localizer["Error.Validate"], MessageType.Error);
|
AddModuleMessage(Localizer["Error.Validate"], MessageType.Error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
private string Browse(Page page) => string.IsNullOrEmpty(page.Url) ? NavigateUrl(page.Path) : page.Url;
|
||||||
}
|
}
|
||||||
|
@ -306,13 +306,15 @@
|
|||||||
private void ThemeChanged(ChangeEventArgs e)
|
private void ThemeChanged(ChangeEventArgs e)
|
||||||
{
|
{
|
||||||
_themetype = (string)e.Value;
|
_themetype = (string)e.Value;
|
||||||
|
_containers = ThemeService.GetContainerControls(PageState.Site.Themes, _themetype);
|
||||||
|
_containertype = _containers.First().TypeName;
|
||||||
|
ThemeSettings();
|
||||||
|
StateHasChanged();
|
||||||
|
|
||||||
|
// if theme chosen is different than default site theme, display warning message to user
|
||||||
if (ThemeService.GetTheme(PageState.Site.Themes, _themetype)?.ThemeName != ThemeService.GetTheme(PageState.Site.Themes, PageState.Site.DefaultThemeType)?.ThemeName)
|
if (ThemeService.GetTheme(PageState.Site.Themes, _themetype)?.ThemeName != ThemeService.GetTheme(PageState.Site.Themes, PageState.Site.DefaultThemeType)?.ThemeName)
|
||||||
{
|
{
|
||||||
AddModuleMessage(Localizer["ThemeChanged.Message"], MessageType.Warning);
|
AddModuleMessage(Localizer["ThemeChanged.Message"], MessageType.Warning);
|
||||||
_containers = ThemeService.GetContainerControls(PageState.Site.Themes, _themetype);
|
|
||||||
_containertype = _containers.First().TypeName;
|
|
||||||
ThemeSettings();
|
|
||||||
StateHasChanged();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -447,13 +447,15 @@
|
|||||||
private void ThemeChanged(ChangeEventArgs e)
|
private void ThemeChanged(ChangeEventArgs e)
|
||||||
{
|
{
|
||||||
_themetype = (string)e.Value;
|
_themetype = (string)e.Value;
|
||||||
|
_containers = ThemeService.GetContainerControls(PageState.Site.Themes, _themetype);
|
||||||
|
_containertype = _containers.First().TypeName;
|
||||||
|
ThemeSettings();
|
||||||
|
StateHasChanged();
|
||||||
|
|
||||||
|
// if theme chosen is different than default site theme, display warning message to user
|
||||||
if (ThemeService.GetTheme(PageState.Site.Themes, _themetype)?.ThemeName != ThemeService.GetTheme(PageState.Site.Themes, PageState.Site.DefaultThemeType)?.ThemeName)
|
if (ThemeService.GetTheme(PageState.Site.Themes, _themetype)?.ThemeName != ThemeService.GetTheme(PageState.Site.Themes, PageState.Site.DefaultThemeType)?.ThemeName)
|
||||||
{
|
{
|
||||||
AddModuleMessage(Localizer["ThemeChanged.Message"], MessageType.Warning);
|
AddModuleMessage(Localizer["ThemeChanged.Message"], MessageType.Warning);
|
||||||
_containers = ThemeService.GetContainerControls(PageState.Site.Themes, _themetype);
|
|
||||||
_containertype = _containers.First().TypeName;
|
|
||||||
ThemeSettings();
|
|
||||||
StateHasChanged();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
@namespace Oqtane.Modules.Admin.Register
|
@namespace Oqtane.Modules.Admin.Register
|
||||||
|
@using System.Net
|
||||||
@inherits ModuleBase
|
@inherits ModuleBase
|
||||||
@inject NavigationManager NavigationManager
|
@inject NavigationManager NavigationManager
|
||||||
@inject IUserService UserService
|
@inject IUserService UserService
|
||||||
@ -88,9 +89,9 @@ else
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnParametersSet()
|
protected override void OnParametersSet()
|
||||||
{
|
{
|
||||||
_togglepassword = SharedLocalizer["ShowPassword"];
|
_togglepassword = SharedLocalizer["ShowPassword"];
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task Register()
|
private async Task Register()
|
||||||
{
|
{
|
||||||
@ -120,7 +121,14 @@ else
|
|||||||
if (user != null)
|
if (user != null)
|
||||||
{
|
{
|
||||||
await logger.LogInformation("User Created {Username} {Email}", _username, _email);
|
await logger.LogInformation("User Created {Username} {Email}", _username, _email);
|
||||||
AddModuleMessage(Localizer["Info.User.AccountCreate"], MessageType.Info);
|
if (PageState.QueryString.ContainsKey("returnurl"))
|
||||||
|
{
|
||||||
|
NavigationManager.NavigateTo(WebUtility.UrlDecode(PageState.QueryString["returnurl"]));
|
||||||
|
}
|
||||||
|
else // legacy behavior
|
||||||
|
{
|
||||||
|
AddModuleMessage(Localizer["Info.User.AccountCreate"], MessageType.Info);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -74,7 +74,7 @@
|
|||||||
<div class="row mb-1 align-items-center">
|
<div class="row mb-1 align-items-center">
|
||||||
<Label Class="col-sm-3" For="logo" HelpText="Specify a logo for the site" ResourceKey="Logo">Logo: </Label>
|
<Label Class="col-sm-3" For="logo" HelpText="Specify a logo for the site" ResourceKey="Logo">Logo: </Label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
<FileManager FileId="@_logofileid" Filter="@Constants.ImageFiles" @ref="_logofilemanager" />
|
<FileManager FileId="@_logofileid" Filter="@_ImageFiles" @ref="_logofilemanager" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row mb-1 align-items-center">
|
<div class="row mb-1 align-items-center">
|
||||||
@ -119,6 +119,22 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Section>
|
</Section>
|
||||||
|
<Section Name="FileExtensions" Heading="File Extensions" ResourceKey="FileExtensions">
|
||||||
|
<div class="container">
|
||||||
|
<div class="row mb-1 align-items-center">
|
||||||
|
<Label Class="col-sm-3" For="imageExt" HelpText="Enter a comma separated list of image file extensions" ResourceKey="ImageExtensions">Image Extensions: </Label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<input id="imageExt" spellcheck="false" class="form-control" @bind="@_ImageFiles" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row mb-1 align-items-center">
|
||||||
|
<Label Class="col-sm-3" For="uploadableFileExt" HelpText="Enter a comma separated list of uploadable file extensions" ResourceKey="UploadableFileExtensions">Uploadable File Extensions: </Label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<input id="uploadableFileExt" spellcheck="false" class="form-control" @bind="@_UploadableFiles" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Section>
|
||||||
<Section Name="PageContent" Heading="Page Content" ResourceKey="PageContent">
|
<Section Name="PageContent" Heading="Page Content" ResourceKey="PageContent">
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="row mb-1 align-items-center">
|
<div class="row mb-1 align-items-center">
|
||||||
@ -207,7 +223,7 @@
|
|||||||
<div class="row mb-1 align-items-center">
|
<div class="row mb-1 align-items-center">
|
||||||
<Label Class="col-sm-3" For="retention" HelpText="Number of days of notifications to retain" ResourceKey="Retention">Retention (Days): </Label>
|
<Label Class="col-sm-3" For="retention" HelpText="Number of days of notifications to retain" ResourceKey="Retention">Retention (Days): </Label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
<input id="retention" class="form-control" @bind="@_retention" />
|
<input id="retention" class="form-control" type="number" min="0" step="1" @bind="@_retention" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<button type="button" class="btn btn-secondary" @onclick="SendEmail">@Localizer["Smtp.TestConfig"]</button>
|
<button type="button" class="btn btn-secondary" @onclick="SendEmail">@Localizer["Smtp.TestConfig"]</button>
|
||||||
@ -295,12 +311,11 @@
|
|||||||
<Section Name="Hosting" Heading="Hosting Model" ResourceKey="Hosting">
|
<Section Name="Hosting" Heading="Hosting Model" ResourceKey="Hosting">
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="row mb-1 align-items-center">
|
<div class="row mb-1 align-items-center">
|
||||||
<Label Class="col-sm-3" For="runtime" HelpText="The Blazor runtime hosting model" ResourceKey="Runtime">Runtime: </Label>
|
<Label Class="col-sm-3" For="runtime" HelpText="The Blazor runtime hosting model for the site" ResourceKey="Runtime">Runtime: </Label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
<select id="runtime" class="form-select" @bind="@_runtime" required>
|
<select id="runtime" class="form-select" @bind="@_runtime" required>
|
||||||
<option value="Server">@SharedLocalizer["BlazorServer"]</option>
|
<option value="Server">@SharedLocalizer["BlazorServer"]</option>
|
||||||
<option value="WebAssembly">@SharedLocalizer["BlazorWebAssembly"]</option>
|
<option value="WebAssembly">@SharedLocalizer["BlazorWebAssembly"]</option>
|
||||||
<option value="Hybrid">@SharedLocalizer["BlazorHybrid"]</option>
|
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -313,6 +328,15 @@
|
|||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="row mb-1 align-items-center">
|
||||||
|
<Label Class="col-sm-3" For="hybridenabled" HelpText="Specifies if the site can be integrated with an external .NET MAUI hybrid application" ResourceKey="HybridEnabled">Hybrid Enabled? </Label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<select id="hybridenabled" class="form-select" @bind="@_hybridenabled" required>
|
||||||
|
<option value="True">@SharedLocalizer["Yes"]</option>
|
||||||
|
<option value="False">@SharedLocalizer["No"]</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Section>
|
</Section>
|
||||||
<Section Name="TenantInformation" Heading="Database" ResourceKey="TenantInformation">
|
<Section Name="TenantInformation" Heading="Database" ResourceKey="TenantInformation">
|
||||||
@ -378,7 +402,9 @@
|
|||||||
private string _smtpsender = string.Empty;
|
private string _smtpsender = string.Empty;
|
||||||
private string _smtprelay = "False";
|
private string _smtprelay = "False";
|
||||||
private string _smtpenabled = "True";
|
private string _smtpenabled = "True";
|
||||||
private string _retention = string.Empty;
|
private string _ImageFiles = string.Empty;
|
||||||
|
private string _UploadableFiles = string.Empty;
|
||||||
|
private int _retention = 30;
|
||||||
private string _pwaisenabled;
|
private string _pwaisenabled;
|
||||||
private int _pwaappiconfileid = -1;
|
private int _pwaappiconfileid = -1;
|
||||||
private FileManager _pwaappiconfilemanager;
|
private FileManager _pwaappiconfilemanager;
|
||||||
@ -390,6 +416,7 @@
|
|||||||
private string _defaultalias;
|
private string _defaultalias;
|
||||||
private string _runtime = "";
|
private string _runtime = "";
|
||||||
private string _prerender = "";
|
private string _prerender = "";
|
||||||
|
private string _hybridenabled = "";
|
||||||
private string _tenant = string.Empty;
|
private string _tenant = string.Empty;
|
||||||
private string _database = string.Empty;
|
private string _database = string.Empty;
|
||||||
private string _connectionstring = string.Empty;
|
private string _connectionstring = string.Empty;
|
||||||
@ -415,7 +442,7 @@
|
|||||||
_homepageid = site.HomePageId.Value.ToString();
|
_homepageid = site.HomePageId.Value.ToString();
|
||||||
}
|
}
|
||||||
_isdeleted = site.IsDeleted.ToString();
|
_isdeleted = site.IsDeleted.ToString();
|
||||||
_sitemap = PageState.Alias.Protocol + PageState.Alias.Name + "/pages/sitemap.xml";
|
_sitemap = PageState.Alias.Protocol + PageState.Alias.Name + "/sitemap.xml";
|
||||||
_siteguid = site.SiteGuid;
|
_siteguid = site.SiteGuid;
|
||||||
_version = site.Version;
|
_version = site.Version;
|
||||||
|
|
||||||
@ -461,7 +488,13 @@
|
|||||||
_smtpsender = SettingService.GetSetting(settings, "SMTPSender", string.Empty);
|
_smtpsender = SettingService.GetSetting(settings, "SMTPSender", string.Empty);
|
||||||
_smtprelay = SettingService.GetSetting(settings, "SMTPRelay", "False");
|
_smtprelay = SettingService.GetSetting(settings, "SMTPRelay", "False");
|
||||||
_smtpenabled = SettingService.GetSetting(settings, "SMTPEnabled", "True");
|
_smtpenabled = SettingService.GetSetting(settings, "SMTPEnabled", "True");
|
||||||
_retention = SettingService.GetSetting(settings, "NotificationRetention", "30");
|
_retention = int.Parse(SettingService.GetSetting(settings, "NotificationRetention", "30"));
|
||||||
|
|
||||||
|
// file extensions
|
||||||
|
_ImageFiles = SettingService.GetSetting(settings, "ImageFiles", Constants.ImageFiles);
|
||||||
|
_ImageFiles = (string.IsNullOrEmpty(_ImageFiles)) ? Constants.ImageFiles : _ImageFiles;
|
||||||
|
_UploadableFiles = SettingService.GetSetting(settings, "UploadableFiles", Constants.UploadableFiles);
|
||||||
|
_UploadableFiles = (string.IsNullOrEmpty(_UploadableFiles)) ? Constants.UploadableFiles : _UploadableFiles;
|
||||||
|
|
||||||
// aliases
|
// aliases
|
||||||
await GetAliases();
|
await GetAliases();
|
||||||
@ -469,6 +502,7 @@
|
|||||||
// hosting model
|
// hosting model
|
||||||
_runtime = site.Runtime;
|
_runtime = site.Runtime;
|
||||||
_prerender = site.RenderMode.Replace(_runtime, "");
|
_prerender = site.RenderMode.Replace(_runtime, "");
|
||||||
|
_hybridenabled = site.HybridEnabled.ToString();
|
||||||
|
|
||||||
// database
|
// database
|
||||||
if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host))
|
if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host))
|
||||||
@ -578,34 +612,35 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
// PWA
|
// PWA
|
||||||
if (site.PwaIsEnabled.ToString() != _pwaisenabled)
|
if (site.PwaIsEnabled.ToString() != _pwaisenabled)
|
||||||
{
|
{
|
||||||
site.PwaIsEnabled = Boolean.Parse(_pwaisenabled);
|
site.PwaIsEnabled = Boolean.Parse(_pwaisenabled);
|
||||||
reload = true; // needs to be reloaded on server
|
reload = true; // needs to be reloaded on server
|
||||||
}
|
}
|
||||||
int? pwaappiconfileid = _pwaappiconfilemanager.GetFileId();
|
int? pwaappiconfileid = _pwaappiconfilemanager.GetFileId();
|
||||||
if (pwaappiconfileid == -1) pwaappiconfileid = null;
|
if (pwaappiconfileid == -1) pwaappiconfileid = null;
|
||||||
if (site.PwaAppIconFileId != pwaappiconfileid)
|
if (site.PwaAppIconFileId != pwaappiconfileid)
|
||||||
{
|
{
|
||||||
site.PwaAppIconFileId = pwaappiconfileid;
|
site.PwaAppIconFileId = pwaappiconfileid;
|
||||||
reload = true; // needs to be reloaded on server
|
reload = true; // needs to be reloaded on server
|
||||||
}
|
}
|
||||||
int? pwasplashiconfileid = _pwasplashiconfilemanager.GetFileId();
|
int? pwasplashiconfileid = _pwasplashiconfilemanager.GetFileId();
|
||||||
if (pwasplashiconfileid == -1) pwasplashiconfileid = null;
|
if (pwasplashiconfileid == -1) pwasplashiconfileid = null;
|
||||||
if (site.PwaSplashIconFileId != pwasplashiconfileid)
|
if (site.PwaSplashIconFileId != pwasplashiconfileid)
|
||||||
{
|
{
|
||||||
site.PwaSplashIconFileId = pwasplashiconfileid;
|
site.PwaSplashIconFileId = pwasplashiconfileid;
|
||||||
reload = true; // needs to be reloaded on server
|
reload = true; // needs to be reloaded on server
|
||||||
}
|
}
|
||||||
|
|
||||||
// hosting model
|
// hosting model
|
||||||
if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host))
|
if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host))
|
||||||
{
|
{
|
||||||
if (site.Runtime != _runtime || site.RenderMode != _runtime + _prerender)
|
if (site.Runtime != _runtime || site.RenderMode != _runtime + _prerender || site.HybridEnabled != bool.Parse(_hybridenabled))
|
||||||
{
|
{
|
||||||
site.Runtime = _runtime;
|
site.Runtime = _runtime;
|
||||||
site.RenderMode = _runtime + _prerender;
|
site.RenderMode = _runtime + _prerender;
|
||||||
reload = true; // needs to be reloaded on server
|
site.HybridEnabled = bool.Parse(_hybridenabled);
|
||||||
|
reload = true; // needs to be reloaded on serve
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -622,7 +657,12 @@
|
|||||||
settings = SettingService.SetSetting(settings, "SMTPRelay", _smtprelay, true);
|
settings = SettingService.SetSetting(settings, "SMTPRelay", _smtprelay, true);
|
||||||
settings = SettingService.SetSetting(settings, "SMTPEnabled", _smtpenabled, true);
|
settings = SettingService.SetSetting(settings, "SMTPEnabled", _smtpenabled, true);
|
||||||
settings = SettingService.SetSetting(settings, "SiteGuid", _siteguid, true);
|
settings = SettingService.SetSetting(settings, "SiteGuid", _siteguid, true);
|
||||||
settings = SettingService.SetSetting(settings, "NotificationRetention", _retention, true);
|
settings = SettingService.SetSetting(settings, "NotificationRetention", _retention.ToString(), true);
|
||||||
|
|
||||||
|
// file extensions
|
||||||
|
settings = SettingService.SetSetting(settings, "ImageFiles", (_ImageFiles != Constants.ImageFiles) ? _ImageFiles.Replace(" ", "") : "", false);
|
||||||
|
settings = SettingService.SetSetting(settings, "UploadableFiles", (_UploadableFiles != Constants.UploadableFiles) ? _UploadableFiles.Replace(" ", "") : "", false);
|
||||||
|
|
||||||
await SettingService.UpdateSiteSettingsAsync(settings, site.SiteId);
|
await SettingService.UpdateSiteSettingsAsync(settings, site.SiteId);
|
||||||
|
|
||||||
await logger.LogInformation("Site Settings Saved {Site}", site);
|
await logger.LogInformation("Site Settings Saved {Site}", site);
|
||||||
|
@ -76,7 +76,6 @@ else
|
|||||||
<select id="runtime" class="form-select" @bind="@_runtime" required>
|
<select id="runtime" class="form-select" @bind="@_runtime" required>
|
||||||
<option value="Server">@SharedLocalizer["BlazorServer"]</option>
|
<option value="Server">@SharedLocalizer["BlazorServer"]</option>
|
||||||
<option value="WebAssembly">@SharedLocalizer["BlazorWebAssembly"]</option>
|
<option value="WebAssembly">@SharedLocalizer["BlazorWebAssembly"]</option>
|
||||||
<option value="Hybrid">@SharedLocalizer["BlazorHybrid"]</option>
|
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
@namespace Oqtane.Modules.Admin.UserProfile
|
@namespace Oqtane.Modules.Admin.UserProfile
|
||||||
|
@using System.Net
|
||||||
@using System.Text.RegularExpressions;
|
@using System.Text.RegularExpressions;
|
||||||
@inherits ModuleBase
|
@inherits ModuleBase
|
||||||
@inject NavigationManager NavigationManager
|
@inject NavigationManager NavigationManager
|
||||||
@ -76,7 +77,7 @@
|
|||||||
<div class="row mb-1 align-items-center">
|
<div class="row mb-1 align-items-center">
|
||||||
<Label Class="col-sm-3" For="@photofileid.ToString()" HelpText="A photo of yourself" ResourceKey="Photo"></Label>
|
<Label Class="col-sm-3" For="@photofileid.ToString()" HelpText="A photo of yourself" ResourceKey="Photo"></Label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
<FileManager FileId="@photofileid" Filter="@Constants.ImageFiles" ShowFolders="false" ShowFiles="true" UploadMultiple="false" FolderId="@folderid" @ref="filemanager" />
|
<FileManager FileId="@photofileid" Filter="@PageState.Site.ImageFiles" ShowFolders="false" ShowFiles="true" UploadMultiple="false" FolderId="@folderid" @ref="filemanager" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -310,7 +311,7 @@
|
|||||||
private int folderid = -1;
|
private int folderid = -1;
|
||||||
private int photofileid = -1;
|
private int photofileid = -1;
|
||||||
private File photo = null;
|
private File photo = null;
|
||||||
|
private string _ImageFiles = string.Empty;
|
||||||
private List<Profile> profiles;
|
private List<Profile> profiles;
|
||||||
private Dictionary<string, string> settings;
|
private Dictionary<string, string> settings;
|
||||||
private string category = string.Empty;
|
private string category = string.Empty;
|
||||||
@ -337,6 +338,11 @@
|
|||||||
email = PageState.User.Email;
|
email = PageState.User.Email;
|
||||||
displayname = PageState.User.DisplayName;
|
displayname = PageState.User.DisplayName;
|
||||||
|
|
||||||
|
if (string.IsNullOrEmpty(email))
|
||||||
|
{
|
||||||
|
AddModuleMessage(Localizer["Message.User.NoEmail"], MessageType.Warning);
|
||||||
|
}
|
||||||
|
|
||||||
// get user folder
|
// get user folder
|
||||||
var folder = await FolderService.GetFolderAsync(ModuleState.SiteId, PageState.User.FolderPath);
|
var folder = await FolderService.GetFolderAsync(ModuleState.SiteId, PageState.User.FolderPath);
|
||||||
if (folder != null)
|
if (folder != null)
|
||||||
@ -354,6 +360,8 @@
|
|||||||
photofileid = -1;
|
photofileid = -1;
|
||||||
photo = null;
|
photo = null;
|
||||||
}
|
}
|
||||||
|
var sitesettings = await SettingService.GetSiteSettingsAsync(SiteState.Alias.SiteId);
|
||||||
|
_ImageFiles = SettingService.GetSetting(settings, "ImageFiles", Constants.ImageFiles);
|
||||||
|
|
||||||
settings = await SettingService.GetUserSettingsAsync(PageState.User.UserId);
|
settings = await SettingService.GetUserSettingsAsync(PageState.User.UserId);
|
||||||
|
|
||||||
@ -427,8 +435,15 @@
|
|||||||
await SettingService.UpdateUserSettingsAsync(settings, PageState.User.UserId);
|
await SettingService.UpdateUserSettingsAsync(settings, PageState.User.UserId);
|
||||||
await logger.LogInformation("User Profile Saved");
|
await logger.LogInformation("User Profile Saved");
|
||||||
|
|
||||||
AddModuleMessage(Localizer["Success.Profile.Update"], MessageType.Success);
|
if (PageState.QueryString.ContainsKey("returnurl"))
|
||||||
StateHasChanged();
|
{
|
||||||
|
NavigationManager.NavigateTo(WebUtility.UrlDecode(PageState.QueryString["returnurl"]));
|
||||||
|
}
|
||||||
|
else // legacy behavior
|
||||||
|
{
|
||||||
|
AddModuleMessage(Localizer["Success.Profile.Update"], MessageType.Success);
|
||||||
|
StateHasChanged();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -195,7 +195,7 @@ else
|
|||||||
@if (_providertype != "")
|
@if (_providertype != "")
|
||||||
{
|
{
|
||||||
<div class="row mb-1 align-items-center">
|
<div class="row mb-1 align-items-center">
|
||||||
<Label Class="col-sm-3" For="providername" HelpText="The external login provider name which will be displayed on the login page" ResourceKey="ProviderName">Provider Name:</Label>
|
<Label Class="col-sm-3" For="providername" HelpText="Specify a friendly name for the external login provider which will be displayed on the Login page" ResourceKey="ProviderName">Provider Name:</Label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
<input id="providername" class="form-control" @bind="@_providername" />
|
<input id="providername" class="form-control" @bind="@_providername" />
|
||||||
</div>
|
</div>
|
||||||
@ -254,7 +254,25 @@ else
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row mb-1 align-items-center">
|
@if (_providertype == AuthenticationProviderTypes.OpenIDConnect)
|
||||||
|
{
|
||||||
|
<div class="row mb-1 align-items-center">
|
||||||
|
<Label Class="col-sm-3" For="authresponsetype" HelpText="Specify the authorization response type. The default is Authorization Code which is considered to be the most secure option based on the latest OAuth specification." ResourceKey="AuthResponseType">Authorization Response Type:</Label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<select id="authresponsetype" class="form-select" @bind="@_authresponsetype" required>
|
||||||
|
<option value="code">@Localizer["AuthFlow.Code"]</option>
|
||||||
|
<option value="code id_token">@Localizer["AuthFlow.CodeIdToken"]</option>
|
||||||
|
<option value="code id_token token">@Localizer["AuthFlow.CodeIdTokenToken"]</option>
|
||||||
|
<option value="code token">@Localizer["AuthFlow.CodeToken"]</option>
|
||||||
|
<option value="id_token">@Localizer["AuthFlow.IdToken"]</option>
|
||||||
|
<option value="id_token token">@Localizer["AuthFlow.IdTokenToken"]</option>
|
||||||
|
<option value="token">@Localizer["AuthFlow.Token"]</option>
|
||||||
|
<option value="none">@Localizer["AuthFlow.None"]</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
<div class="row mb-1 align-items-center">
|
||||||
<Label Class="col-sm-3" For="scopes" HelpText="A list of Scopes to request from the provider (separated by commas). If none are specified, standard Scopes will be used by default." ResourceKey="Scopes">Scopes:</Label>
|
<Label Class="col-sm-3" For="scopes" HelpText="A list of Scopes to request from the provider (separated by commas). If none are specified, standard Scopes will be used by default." ResourceKey="Scopes">Scopes:</Label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
<input id="scopes" class="form-control" @bind="@_scopes" />
|
<input id="scopes" class="form-control" @bind="@_scopes" />
|
||||||
@ -266,21 +284,6 @@ else
|
|||||||
<input id="parameters" class="form-control" @bind="@_parameters" />
|
<input id="parameters" class="form-control" @bind="@_parameters" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row mb-1 align-items-center">
|
|
||||||
<Label Class="col-sm-3" For="authresponsetype" HelpText="Specify the authorization response type" ResourceKey="AuthResponseType">Authorization Response Type</Label>
|
|
||||||
<div class="col-sm-9">
|
|
||||||
<select id="authresponsetype" class="form-select" @bind="@_authresponsetype" required>
|
|
||||||
<option value="code">@Localizer["AuthFlow.Code"]</option>
|
|
||||||
<option value="code id_token">@Localizer["AuthFlow.CodeIdToken"]</option>
|
|
||||||
<option value="code id_token token">@Localizer["AuthFlow.CodeIdTokenToken"]</option>
|
|
||||||
<option value="code token">@Localizer["AuthFlow.CodeToken"]</option>
|
|
||||||
<option value="id_token">@Localizer["AuthFlow.IdToken"]</option>
|
|
||||||
<option value="id_token token">@Localizer["AuthFlow.IdTokenToken"]</option>
|
|
||||||
<option value="token">@Localizer["AuthFlow.Token"]</option>
|
|
||||||
<option value="none">@Localizer["AuthFlow.None"]</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="row mb-1 align-items-center">
|
<div class="row mb-1 align-items-center">
|
||||||
<Label Class="col-sm-3" For="pkce" HelpText="Indicate if the provider supports Proof Key for Code Exchange (PKCE)" ResourceKey="PKCE">Use PKCE?</Label>
|
<Label Class="col-sm-3" For="pkce" HelpText="Indicate if the provider supports Proof Key for Code Exchange (PKCE)" ResourceKey="PKCE">Use PKCE?</Label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
@ -296,33 +299,51 @@ else
|
|||||||
<input id="redirecturl" class="form-control" @bind="@_redirecturl" readonly />
|
<input id="redirecturl" class="form-control" @bind="@_redirecturl" readonly />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row mb-1 align-items-center">
|
<div class="row mb-1 align-items-center">
|
||||||
<Label Class="col-sm-3" For="identifierclaimtype" HelpText="The name of the unique user identifier claim provided by the provider" ResourceKey="IdentifierClaimType">Identifier Claim:</Label>
|
<Label Class="col-sm-3" For="reviewclaims" HelpText="This option will record the full list of Claims returned by the Provider in the Event Log. It should only be used for testing purposes. External Login will be restricted when this option is enabled." ResourceKey="ReviewClaims">Review Claims?</Label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<div class="input-group">
|
||||||
|
<select id="reviewclaims" class="form-select" @bind="@_reviewclaims" required>
|
||||||
|
<option value="true">@SharedLocalizer["Yes"]</option>
|
||||||
|
<option value="false">@SharedLocalizer["No"]</option>
|
||||||
|
</select>
|
||||||
|
@if (_reviewclaims == "true")
|
||||||
|
{
|
||||||
|
<a href="@_externalloginurl" target="_blank" class="btn btn-secondary">@SharedLocalizer["Test"]</a>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row mb-1 align-items-center">
|
||||||
|
<Label Class="col-sm-3" For="identifierclaimtype" HelpText="Specify the type name of the unique user identifier claim provided by the provider. The default value is 'sub'." ResourceKey="IdentifierClaimType">Identifier Claim:</Label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
<input id="identifierclaimtype" class="form-control" @bind="@_identifierclaimtype" />
|
<input id="identifierclaimtype" class="form-control" @bind="@_identifierclaimtype" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row mb-1 align-items-center">
|
<div class="row mb-1 align-items-center">
|
||||||
<Label Class="col-sm-3" For="emailclaimtype" HelpText="The name of the email address claim provided by the provider" ResourceKey="EmailClaimType">Email Claim:</Label>
|
<Label Class="col-sm-3" For="nameclaimtype" HelpText="Optionally specify the type name of the user's name claim provided by the provider. The typical value is 'name'." ResourceKey="NameClaimType">Name Claim:</Label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<input id="nameclaimtype" class="form-control" @bind="@_nameclaimtype" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row mb-1 align-items-center">
|
||||||
|
<Label Class="col-sm-3" For="emailclaimtype" HelpText="Optionally specify the type name of the email address claim provided by the provider. The typical value is 'email'," ResourceKey="EmailClaimType">Email Claim:</Label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
<input id="emailclaimtype" class="form-control" @bind="@_emailclaimtype" />
|
<input id="emailclaimtype" class="form-control" @bind="@_emailclaimtype" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@if (_providertype == AuthenticationProviderTypes.OpenIDConnect)
|
<div class="row mb-1 align-items-center">
|
||||||
{
|
<Label Class="col-sm-3" For="roleclaimtype" HelpText="The name of the role claim provided by the provider" ResourceKey="RoleClaimType">Role Claim:</Label>
|
||||||
<div class="row mb-1 align-items-center">
|
<div class="col-sm-9">
|
||||||
<Label Class="col-sm-3" For="roleclaimtype" HelpText="The name of the role claim provided by the provider" ResourceKey="RoleClaimType">Role Claim:</Label>
|
<input id="roleclaimtype" class="form-control" @bind="@_roleclaimtype" />
|
||||||
<div class="col-sm-9">
|
|
||||||
<input id="roleclaimtype" class="form-control" @bind="@_roleclaimtype" />
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="row mb-1 align-items-center">
|
</div>
|
||||||
<Label Class="col-sm-3" For="profileclaimtypes" HelpText="A comma delimited list of user profile claims provided by the provider, as well as mappings to your user profile definition. For example if the provider includes a 'given_name' claim and you have a 'FirstName' user profile definition you should specify 'given_name:FirstName'." ResourceKey="ProfileClaimTypes">User Profile Claims:</Label>
|
<div class="row mb-1 align-items-center">
|
||||||
<div class="col-sm-9">
|
<Label Class="col-sm-3" For="profileclaimtypes" HelpText="A comma delimited list of user profile claims provided by the provider, as well as mappings to your user profile definition. For example if the provider includes a 'given_name' claim and you have a 'FirstName' user profile definition you should specify 'given_name:FirstName'." ResourceKey="ProfileClaimTypes">User Profile Claims:</Label>
|
||||||
<input id="profileclaimtypes" class="form-control" @bind="@_profileclaimtypes" />
|
<div class="col-sm-9">
|
||||||
</div>
|
<input id="profileclaimtypes" class="form-control" @bind="@_profileclaimtypes" />
|
||||||
</div>
|
</div>
|
||||||
}
|
</div>
|
||||||
<div class="row mb-1 align-items-center">
|
<div class="row mb-1 align-items-center">
|
||||||
<Label Class="col-sm-3" For="domainfilter" HelpText="Provide any email domain filter criteria (separated by commas). Domains to exclude should be prefixed with an exclamation point (!). For example 'microsoft.com,!hotmail.com' would include microsoft.com email addresses but not hotmail.com email addresses." ResourceKey="DomainFilter">Domain Filter:</Label>
|
<Label Class="col-sm-3" For="domainfilter" HelpText="Provide any email domain filter criteria (separated by commas). Domains to exclude should be prefixed with an exclamation point (!). For example 'microsoft.com,!hotmail.com' would include microsoft.com email addresses but not hotmail.com email addresses." ResourceKey="DomainFilter">Domain Filter:</Label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
@ -351,10 +372,10 @@ else
|
|||||||
</Section>
|
</Section>
|
||||||
<Section Name="Token" Heading="Token Settings" ResourceKey="TokenSettings">
|
<Section Name="Token" Heading="Token Settings" ResourceKey="TokenSettings">
|
||||||
<div class="row mb-1 align-items-center">
|
<div class="row mb-1 align-items-center">
|
||||||
<Label Class="col-sm-3" For="secret" HelpText="If you want to want to provide API access, please specify a secret which will be used to encrypt your tokens. The secret should be 16 characters or more to ensure optimal security. Please note that if you change this secret, all existing tokens will become invalid and will need to be regenerated." ResourceKey="Secret">Secret:</Label>
|
<Label Class="col-sm-3" For="jwtsecret" HelpText="If you want to want to provide API access, please specify a secret which will be used to encrypt your tokens. The secret should be 16 characters or more to ensure optimal security. Please note that if you change this secret, all existing tokens will become invalid and will need to be regenerated." ResourceKey="Secret">Secret:</Label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
<div class="input-group">
|
<div class="input-group">
|
||||||
<input type="@_secrettype" id="secret" class="form-control" @bind="@_secret" />
|
<input type="@_secrettype" id="jwtsecret" class="form-control" @bind="@_secret" />
|
||||||
<button type="button" class="btn btn-secondary" @onclick="@ToggleSecret">@_togglesecret</button>
|
<button type="button" class="btn btn-secondary" @onclick="@ToggleSecret">@_togglesecret</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -425,12 +446,15 @@ else
|
|||||||
private string _clientsecret;
|
private string _clientsecret;
|
||||||
private string _clientsecrettype = "password";
|
private string _clientsecrettype = "password";
|
||||||
private string _toggleclientsecret = string.Empty;
|
private string _toggleclientsecret = string.Empty;
|
||||||
|
private string _authresponsetype;
|
||||||
private string _scopes;
|
private string _scopes;
|
||||||
private string _parameters;
|
private string _parameters;
|
||||||
private string _pkce;
|
private string _pkce;
|
||||||
private string _authresponsetype;
|
|
||||||
private string _redirecturl;
|
private string _redirecturl;
|
||||||
|
private string _reviewclaims;
|
||||||
|
private string _externalloginurl;
|
||||||
private string _identifierclaimtype;
|
private string _identifierclaimtype;
|
||||||
|
private string _nameclaimtype;
|
||||||
private string _emailclaimtype;
|
private string _emailclaimtype;
|
||||||
private string _roleclaimtype;
|
private string _roleclaimtype;
|
||||||
private string _profileclaimtypes;
|
private string _profileclaimtypes;
|
||||||
@ -486,12 +510,15 @@ else
|
|||||||
_clientid = SettingService.GetSetting(settings, "ExternalLogin:ClientId", "");
|
_clientid = SettingService.GetSetting(settings, "ExternalLogin:ClientId", "");
|
||||||
_clientsecret = SettingService.GetSetting(settings, "ExternalLogin:ClientSecret", "");
|
_clientsecret = SettingService.GetSetting(settings, "ExternalLogin:ClientSecret", "");
|
||||||
_toggleclientsecret = SharedLocalizer["ShowPassword"];
|
_toggleclientsecret = SharedLocalizer["ShowPassword"];
|
||||||
|
_authresponsetype = SettingService.GetSetting(settings, "ExternalLogin:AuthResponseType", "code");
|
||||||
_scopes = SettingService.GetSetting(settings, "ExternalLogin:Scopes", "");
|
_scopes = SettingService.GetSetting(settings, "ExternalLogin:Scopes", "");
|
||||||
_parameters = SettingService.GetSetting(settings, "ExternalLogin:Parameters", "");
|
_parameters = SettingService.GetSetting(settings, "ExternalLogin:Parameters", "");
|
||||||
_pkce = SettingService.GetSetting(settings, "ExternalLogin:PKCE", "false");
|
_pkce = SettingService.GetSetting(settings, "ExternalLogin:PKCE", "false");
|
||||||
_authresponsetype = SettingService.GetSetting(settings, "ExternalLogin:AuthResponseType", "code");
|
|
||||||
_redirecturl = PageState.Uri.Scheme + "://" + PageState.Alias.Name + "/signin-" + _providertype;
|
_redirecturl = PageState.Uri.Scheme + "://" + PageState.Alias.Name + "/signin-" + _providertype;
|
||||||
|
_reviewclaims = SettingService.GetSetting(settings, "ExternalLogin:ReviewClaims", "false");
|
||||||
|
_externalloginurl = Utilities.TenantUrl(PageState.Alias, "/pages/external");
|
||||||
_identifierclaimtype = SettingService.GetSetting(settings, "ExternalLogin:IdentifierClaimType", "sub");
|
_identifierclaimtype = SettingService.GetSetting(settings, "ExternalLogin:IdentifierClaimType", "sub");
|
||||||
|
_nameclaimtype = SettingService.GetSetting(settings, "ExternalLogin:NameClaimType", "name");
|
||||||
_emailclaimtype = SettingService.GetSetting(settings, "ExternalLogin:EmailClaimType", "email");
|
_emailclaimtype = SettingService.GetSetting(settings, "ExternalLogin:EmailClaimType", "email");
|
||||||
_roleclaimtype = SettingService.GetSetting(settings, "ExternalLogin:RoleClaimType", "");
|
_roleclaimtype = SettingService.GetSetting(settings, "ExternalLogin:RoleClaimType", "");
|
||||||
_profileclaimtypes = SettingService.GetSetting(settings, "ExternalLogin:ProfileClaimTypes", "");
|
_profileclaimtypes = SettingService.GetSetting(settings, "ExternalLogin:ProfileClaimTypes", "");
|
||||||
@ -578,19 +605,20 @@ else
|
|||||||
settings = SettingService.SetSetting(settings, "ExternalLogin:UserInfoUrl", _userinfourl, true);
|
settings = SettingService.SetSetting(settings, "ExternalLogin:UserInfoUrl", _userinfourl, true);
|
||||||
settings = SettingService.SetSetting(settings, "ExternalLogin:ClientId", _clientid, true);
|
settings = SettingService.SetSetting(settings, "ExternalLogin:ClientId", _clientid, true);
|
||||||
settings = SettingService.SetSetting(settings, "ExternalLogin:ClientSecret", _clientsecret, true);
|
settings = SettingService.SetSetting(settings, "ExternalLogin:ClientSecret", _clientsecret, true);
|
||||||
settings = SettingService.SetSetting(settings, "ExternalLogin:Scopes", _scopes, true);
|
settings = SettingService.SetSetting(settings, "ExternalLogin:AuthResponseType", _authresponsetype, true);
|
||||||
|
settings = SettingService.SetSetting(settings, "ExternalLogin:Scopes", _scopes, true);
|
||||||
settings = SettingService.SetSetting(settings, "ExternalLogin:Parameters", _parameters, true);
|
settings = SettingService.SetSetting(settings, "ExternalLogin:Parameters", _parameters, true);
|
||||||
settings = SettingService.SetSetting(settings, "ExternalLogin:PKCE", _pkce, true);
|
settings = SettingService.SetSetting(settings, "ExternalLogin:PKCE", _pkce, true);
|
||||||
settings = SettingService.SetSetting(settings, "ExternalLogin:AuthResponseType", _authresponsetype, true);
|
settings = SettingService.SetSetting(settings, "ExternalLogin:ReviewClaims", _reviewclaims, true);
|
||||||
settings = SettingService.SetSetting(settings, "ExternalLogin:IdentifierClaimType", _identifierclaimtype, true);
|
settings = SettingService.SetSetting(settings, "ExternalLogin:IdentifierClaimType", _identifierclaimtype, true);
|
||||||
settings = SettingService.SetSetting(settings, "ExternalLogin:EmailClaimType", _emailclaimtype, true);
|
settings = SettingService.SetSetting(settings, "ExternalLogin:NameClaimType", _nameclaimtype, true);
|
||||||
|
settings = SettingService.SetSetting(settings, "ExternalLogin:EmailClaimType", _emailclaimtype, true);
|
||||||
settings = SettingService.SetSetting(settings, "ExternalLogin:RoleClaimType", _roleclaimtype, true);
|
settings = SettingService.SetSetting(settings, "ExternalLogin:RoleClaimType", _roleclaimtype, true);
|
||||||
settings = SettingService.SetSetting(settings, "ExternalLogin:ProfileClaimTypes", _profileclaimtypes, true);
|
settings = SettingService.SetSetting(settings, "ExternalLogin:ProfileClaimTypes", _profileclaimtypes, true);
|
||||||
settings = SettingService.SetSetting(settings, "ExternalLogin:DomainFilter", _domainfilter, true);
|
settings = SettingService.SetSetting(settings, "ExternalLogin:DomainFilter", _domainfilter, true);
|
||||||
settings = SettingService.SetSetting(settings, "ExternalLogin:CreateUsers", _createusers, true);
|
settings = SettingService.SetSetting(settings, "ExternalLogin:CreateUsers", _createusers, true);
|
||||||
settings = SettingService.SetSetting(settings, "ExternalLogin:VerifyUsers", _verifyusers, true);
|
settings = SettingService.SetSetting(settings, "ExternalLogin:VerifyUsers", _verifyusers, true);
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(_secret) && _secret.Length < 16) _secret = (_secret + "????????????????").Substring(0, 16);
|
|
||||||
settings = SettingService.SetSetting(settings, "JwtOptions:Secret", _secret, true);
|
settings = SettingService.SetSetting(settings, "JwtOptions:Secret", _secret, true);
|
||||||
settings = SettingService.SetSetting(settings, "JwtOptions:Issuer", _issuer, true);
|
settings = SettingService.SetSetting(settings, "JwtOptions:Issuer", _issuer, true);
|
||||||
settings = SettingService.SetSetting(settings, "JwtOptions:Audience", _audience, true);
|
settings = SettingService.SetSetting(settings, "JwtOptions:Audience", _audience, true);
|
||||||
|
@ -78,7 +78,7 @@ else
|
|||||||
<div class="row mb-1 align-items-center">
|
<div class="row mb-1 align-items-center">
|
||||||
<Label Class="col-sm-3" For="retention" HelpText="Number of days of visitor activity to retain" ResourceKey="Retention">Retention (Days): </Label>
|
<Label Class="col-sm-3" For="retention" HelpText="Number of days of visitor activity to retain" ResourceKey="Retention">Retention (Days): </Label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
<input id="retention" class="form-control" @bind="@_retention" />
|
<input id="retention" class="form-control" type="number" min="0" step="1" @bind="@_retention" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row mb-1 align-items-center">
|
<div class="row mb-1 align-items-center">
|
||||||
@ -104,7 +104,7 @@ else
|
|||||||
private List<Visitor> _visitors;
|
private List<Visitor> _visitors;
|
||||||
private string _tracking;
|
private string _tracking;
|
||||||
private string _filter = "";
|
private string _filter = "";
|
||||||
private string _retention = "";
|
private int _retention = 30;
|
||||||
private string _correlation = "true";
|
private string _correlation = "true";
|
||||||
|
|
||||||
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Admin;
|
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Admin;
|
||||||
@ -129,7 +129,7 @@ else
|
|||||||
_tracking = PageState.Site.VisitorTracking.ToString();
|
_tracking = PageState.Site.VisitorTracking.ToString();
|
||||||
var settings = await SettingService.GetSiteSettingsAsync(PageState.Site.SiteId);
|
var settings = await SettingService.GetSiteSettingsAsync(PageState.Site.SiteId);
|
||||||
_filter = SettingService.GetSetting(settings, "VisitorFilter", Constants.DefaultVisitorFilter);
|
_filter = SettingService.GetSetting(settings, "VisitorFilter", Constants.DefaultVisitorFilter);
|
||||||
_retention = SettingService.GetSetting(settings, "VisitorRetention", "30");
|
_retention = int.Parse(SettingService.GetSetting(settings, "VisitorRetention", "30"));
|
||||||
_correlation = SettingService.GetSetting(settings, "VisitorCorrelation", "true");
|
_correlation = SettingService.GetSetting(settings, "VisitorCorrelation", "true");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -180,7 +180,7 @@ else
|
|||||||
|
|
||||||
var settings = await SettingService.GetSiteSettingsAsync(PageState.Site.SiteId);
|
var settings = await SettingService.GetSiteSettingsAsync(PageState.Site.SiteId);
|
||||||
settings = SettingService.SetSetting(settings, "VisitorFilter", _filter, true);
|
settings = SettingService.SetSetting(settings, "VisitorFilter", _filter, true);
|
||||||
settings = SettingService.SetSetting(settings, "VisitorRetention", _retention, true);
|
settings = SettingService.SetSetting(settings, "VisitorRetention", _retention.ToString(), true);
|
||||||
settings = SettingService.SetSetting(settings, "VisitorCorrelation", _correlation, true);
|
settings = SettingService.SetSetting(settings, "VisitorCorrelation", _correlation, true);
|
||||||
await SettingService.UpdateSiteSettingsAsync(settings, PageState.Site.SiteId);
|
await SettingService.UpdateSiteSettingsAsync(settings, PageState.Site.SiteId);
|
||||||
|
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
@inherits ModuleControlBase
|
@inherits ModuleControlBase
|
||||||
@inject IFolderService FolderService
|
@inject IFolderService FolderService
|
||||||
@inject IFileService FileService
|
@inject IFileService FileService
|
||||||
|
@inject ISettingService SettingService
|
||||||
@inject IStringLocalizer<FileManager> Localizer
|
@inject IStringLocalizer<FileManager> Localizer
|
||||||
@inject IStringLocalizer<SharedResources> SharedLocalizer
|
@inject IStringLocalizer<SharedResources> SharedLocalizer
|
||||||
|
|
||||||
@ -83,6 +84,14 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@if (!string.IsNullOrEmpty(_message))
|
||||||
|
{
|
||||||
|
<div class="row mt-1">
|
||||||
|
<div class="col">
|
||||||
|
<ModuleMessage Message="@_message" Type="@_messagetype" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@if (_image != string.Empty)
|
@if (_image != string.Empty)
|
||||||
@ -92,14 +101,6 @@
|
|||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
@if (!string.IsNullOrEmpty(_message))
|
|
||||||
{
|
|
||||||
<div class="row mt-1">
|
|
||||||
<div class="col">
|
|
||||||
<ModuleMessage Message="@_message" Type="@_messagetype" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -343,6 +344,7 @@
|
|||||||
_message = string.Empty;
|
_message = string.Empty;
|
||||||
var interop = new Interop(JSRuntime);
|
var interop = new Interop(JSRuntime);
|
||||||
var uploads = await interop.GetFiles(_fileinputid);
|
var uploads = await interop.GetFiles(_fileinputid);
|
||||||
|
|
||||||
if (uploads.Length > 0)
|
if (uploads.Length > 0)
|
||||||
{
|
{
|
||||||
string restricted = "";
|
string restricted = "";
|
||||||
@ -350,7 +352,7 @@
|
|||||||
{
|
{
|
||||||
var filename = upload.Split(':')[0];
|
var filename = upload.Split(':')[0];
|
||||||
var extension = (filename.LastIndexOf(".") != -1) ? filename.Substring(filename.LastIndexOf(".") + 1) : "";
|
var extension = (filename.LastIndexOf(".") != -1) ? filename.Substring(filename.LastIndexOf(".") + 1) : "";
|
||||||
if (!Constants.UploadableFiles.Split(',').Contains(extension.ToLower()))
|
if (!PageState.Site.UploadableFiles.Split(',').Contains(extension.ToLower()))
|
||||||
{
|
{
|
||||||
restricted += (restricted == "" ? "" : ",") + extension;
|
restricted += (restricted == "" ? "" : ",") + extension;
|
||||||
}
|
}
|
||||||
|
@ -1,79 +1,86 @@
|
|||||||
|
@using System.Text.RegularExpressions
|
||||||
@namespace Oqtane.Modules.Controls
|
@namespace Oqtane.Modules.Controls
|
||||||
@inherits ModuleControlBase
|
@inherits ModuleControlBase
|
||||||
|
@inject ISettingService SettingService
|
||||||
@inject IStringLocalizer<RichTextEditor> Localizer
|
@inject IStringLocalizer<RichTextEditor> Localizer
|
||||||
|
|
||||||
<div class="row" style="margin-bottom: 50px;">
|
<div class="row" style="margin-bottom: 50px;">
|
||||||
<div class="col">
|
<div class="col">
|
||||||
<TabStrip>
|
<TabStrip>
|
||||||
<TabPanel Name="Rich" Heading="Rich Text Editor" ResourceKey="RichTextEditor">
|
@if (AllowRichText)
|
||||||
@if (_richfilemanager)
|
{
|
||||||
{
|
<TabPanel Name="Rich" Heading="Rich Text Editor" ResourceKey="RichTextEditor">
|
||||||
<FileManager @ref="_fileManager" Filter="@Constants.ImageFiles" />
|
@if (_richfilemanager)
|
||||||
<ModuleMessage Message="@_message" Type="MessageType.Warning"></ModuleMessage>
|
{
|
||||||
<br />
|
<FileManager @ref="_fileManager" Filter="@PageState.Site.ImageFiles" />
|
||||||
}
|
<ModuleMessage Message="@_message" Type="MessageType.Warning"></ModuleMessage>
|
||||||
<div class="d-flex justify-content-center mb-2">
|
<br />
|
||||||
@if (AllowRawHtml)
|
}
|
||||||
{
|
<div class="d-flex justify-content-center mb-2">
|
||||||
<button type="button" class="btn btn-secondary" @onclick="RefreshRichText">@Localizer["SynchronizeContent"]</button>@((MarkupString)" ")
|
@if (AllowRawHtml)
|
||||||
}
|
{
|
||||||
@if (AllowFileManagement)
|
<button type="button" class="btn btn-secondary" @onclick="RefreshRichText">@Localizer["SynchronizeContent"]</button>
|
||||||
{
|
|
||||||
<button type="button" class="btn btn-primary" @onclick="InsertRichImage">@Localizer["InsertImage"]</button>
|
@((MarkupString)" ")
|
||||||
}
|
}
|
||||||
@if (_richfilemanager)
|
@if (AllowFileManagement)
|
||||||
{
|
{
|
||||||
@((MarkupString)" ")
|
<button type="button" class="btn btn-primary" @onclick="InsertRichImage">@Localizer["InsertImage"]</button>
|
||||||
<button type="button" class="btn btn-secondary" @onclick="CloseRichFileManager">@Localizer["Close"]</button>
|
}
|
||||||
}
|
@if (_richfilemanager)
|
||||||
</div>
|
{
|
||||||
<div class="row">
|
@((MarkupString)" ")
|
||||||
<div class="col">
|
<button type="button" class="btn btn-secondary" @onclick="CloseRichFileManager">@Localizer["Close"]</button>
|
||||||
<div @ref="@_toolBar">
|
}
|
||||||
@if (ToolbarContent != null)
|
</div>
|
||||||
{
|
<div class="row">
|
||||||
@ToolbarContent
|
<div class="col">
|
||||||
}
|
<div @ref="@_toolBar">
|
||||||
else
|
@if (ToolbarContent != null)
|
||||||
{
|
{
|
||||||
<select class="ql-header">
|
@ToolbarContent
|
||||||
<option selected=""></option>
|
}
|
||||||
<option value="1"></option>
|
else
|
||||||
<option value="2"></option>
|
{
|
||||||
<option value="3"></option>
|
<select class="ql-header">
|
||||||
<option value="4"></option>
|
<option selected=""></option>
|
||||||
<option value="5"></option>
|
<option value="1"></option>
|
||||||
</select>
|
<option value="2"></option>
|
||||||
<span class="ql-formats">
|
<option value="3"></option>
|
||||||
<button class="ql-bold"></button>
|
<option value="4"></option>
|
||||||
<button class="ql-italic"></button>
|
<option value="5"></option>
|
||||||
<button class="ql-underline"></button>
|
</select>
|
||||||
<button class="ql-strike"></button>
|
<span class="ql-formats">
|
||||||
</span>
|
<button class="ql-bold"></button>
|
||||||
<span class="ql-formats">
|
<button class="ql-italic"></button>
|
||||||
<select class="ql-color"></select>
|
<button class="ql-underline"></button>
|
||||||
<select class="ql-background"></select>
|
<button class="ql-strike"></button>
|
||||||
</span>
|
</span>
|
||||||
<span class="ql-formats">
|
<span class="ql-formats">
|
||||||
<button class="ql-list" value="ordered"></button>
|
<select class="ql-color"></select>
|
||||||
<button class="ql-list" value="bullet"></button>
|
<select class="ql-background"></select>
|
||||||
</span>
|
</span>
|
||||||
<span class="ql-formats">
|
<span class="ql-formats">
|
||||||
<button class="ql-link"></button>
|
<button class="ql-list" value="ordered"></button>
|
||||||
</span>
|
<button class="ql-list" value="bullet"></button>
|
||||||
}
|
</span>
|
||||||
</div>
|
<span class="ql-formats">
|
||||||
<div @ref="@_editorElement">
|
<button class="ql-link"></button>
|
||||||
</div>
|
</span>
|
||||||
</div>
|
}
|
||||||
</div>
|
</div>
|
||||||
</TabPanel>
|
<div @ref="@_editorElement">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</TabPanel>
|
||||||
|
}
|
||||||
@if (AllowRawHtml)
|
@if (AllowRawHtml)
|
||||||
{
|
{
|
||||||
<TabPanel Name="Raw" Heading="Raw HTML Editor" ResourceKey="HtmlEditor">
|
<TabPanel Name="Raw" Heading="Raw HTML Editor" ResourceKey="HtmlEditor">
|
||||||
@if (_rawfilemanager)
|
@if (_rawfilemanager)
|
||||||
{
|
{
|
||||||
<FileManager @ref="_fileManager" Filter="@Constants.ImageFiles" />
|
<FileManager @ref="_fileManager" Filter="@PageState.Site.ImageFiles" />
|
||||||
<ModuleMessage Message="@_message" Type="MessageType.Warning"></ModuleMessage>
|
<ModuleMessage Message="@_message" Type="MessageType.Warning"></ModuleMessage>
|
||||||
<br />
|
<br />
|
||||||
}
|
}
|
||||||
@ -104,119 +111,130 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
@code {
|
@code {
|
||||||
private ElementReference _editorElement;
|
private ElementReference _editorElement;
|
||||||
private ElementReference _toolBar;
|
private ElementReference _toolBar;
|
||||||
private bool _richfilemanager = false;
|
private bool _richfilemanager = false;
|
||||||
private FileManager _fileManager;
|
private FileManager _fileManager;
|
||||||
private string _richhtml = string.Empty;
|
private string _richhtml = string.Empty;
|
||||||
private string _originalrichhtml = string.Empty;
|
private string _originalrichhtml = string.Empty;
|
||||||
private bool _rawfilemanager = false;
|
private bool _rawfilemanager = false;
|
||||||
private string _rawhtml = string.Empty;
|
private string _rawhtml = string.Empty;
|
||||||
private string _originalrawhtml = string.Empty;
|
private string _originalrawhtml = string.Empty;
|
||||||
private string _message = string.Empty;
|
private string _message = string.Empty;
|
||||||
|
|
||||||
[Parameter]
|
[Parameter]
|
||||||
public string Content { get; set; }
|
public string Content { get; set; }
|
||||||
|
|
||||||
[Parameter]
|
[Parameter]
|
||||||
public bool ReadOnly { get; set; } = false;
|
public bool ReadOnly { get; set; } = false;
|
||||||
|
|
||||||
[Parameter]
|
[Parameter]
|
||||||
public string Placeholder { get; set; } = "Enter Your Content...";
|
public string Placeholder { get; set; } = "Enter Your Content...";
|
||||||
|
|
||||||
[Parameter]
|
[Parameter]
|
||||||
public bool AllowFileManagement { get; set; } = true;
|
public bool AllowFileManagement { get; set; } = true;
|
||||||
|
|
||||||
[Parameter]
|
[Parameter]
|
||||||
public bool AllowRawHtml { get; set; } = true;
|
public bool AllowRichText { get; set; } = true;
|
||||||
|
|
||||||
// parameters only applicable to rich text editor
|
|
||||||
[Parameter]
|
|
||||||
public RenderFragment ToolbarContent { get; set; }
|
|
||||||
|
|
||||||
[Parameter]
|
[Parameter]
|
||||||
public string Theme { get; set; } = "snow";
|
public bool AllowRawHtml { get; set; } = true;
|
||||||
|
|
||||||
[Parameter]
|
// parameters only applicable to rich text editor
|
||||||
public string DebugLevel { get; set; } = "info";
|
[Parameter]
|
||||||
|
public RenderFragment ToolbarContent { get; set; }
|
||||||
|
|
||||||
public override List<Resource> Resources => new List<Resource>()
|
[Parameter]
|
||||||
|
public string Theme { get; set; } = "snow";
|
||||||
|
|
||||||
|
[Parameter]
|
||||||
|
public string DebugLevel { get; set; } = "info";
|
||||||
|
|
||||||
|
public override List<Resource> Resources => new List<Resource>()
|
||||||
{
|
{
|
||||||
new Resource { ResourceType = ResourceType.Script, Bundle = "Quill", Url = "js/quill.min.js" },
|
new Resource { ResourceType = ResourceType.Script, Bundle = "Quill", Url = "js/quill.min.js" },
|
||||||
new Resource { ResourceType = ResourceType.Script, Bundle = "Quill", Url = "js/quill-blot-formatter.min.js" },
|
new Resource { ResourceType = ResourceType.Script, Bundle = "Quill", Url = "js/quill-blot-formatter.min.js" },
|
||||||
new Resource { ResourceType = ResourceType.Script, Bundle = "Quill", Url = "js/quill-interop.js" }
|
new Resource { ResourceType = ResourceType.Script, Bundle = "Quill", Url = "js/quill-interop.js" }
|
||||||
};
|
};
|
||||||
|
|
||||||
protected override void OnParametersSet()
|
protected override void OnParametersSet()
|
||||||
{
|
{
|
||||||
_richhtml = Content;
|
_richhtml = Content;
|
||||||
_rawhtml = Content;
|
_rawhtml = Content;
|
||||||
_originalrawhtml = _rawhtml; // preserve for comparison later
|
_originalrawhtml = _rawhtml; // preserve for comparison later
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override async Task OnAfterRenderAsync(bool firstRender)
|
protected override async Task OnAfterRenderAsync(bool firstRender)
|
||||||
{
|
{
|
||||||
await base.OnAfterRenderAsync(firstRender);
|
await base.OnAfterRenderAsync(firstRender);
|
||||||
|
|
||||||
var interop = new RichTextEditorInterop(JSRuntime);
|
var interop = new RichTextEditorInterop(JSRuntime);
|
||||||
|
|
||||||
if (firstRender)
|
if (firstRender)
|
||||||
{
|
{
|
||||||
await interop.CreateEditor(
|
await interop.CreateEditor(
|
||||||
_editorElement,
|
_editorElement,
|
||||||
_toolBar,
|
_toolBar,
|
||||||
ReadOnly,
|
ReadOnly,
|
||||||
Placeholder,
|
Placeholder,
|
||||||
Theme,
|
Theme,
|
||||||
DebugLevel);
|
DebugLevel);
|
||||||
|
|
||||||
await interop.LoadEditorContent(_editorElement, _richhtml);
|
await interop.LoadEditorContent(_editorElement, _richhtml);
|
||||||
|
|
||||||
// preserve a copy of the rich text content (Quill sanitizes content so we need to retrieve it from the editor)
|
if (AllowRichText)
|
||||||
_originalrichhtml = await interop.GetHtml(_editorElement);
|
{
|
||||||
}
|
// preserve a copy of the rich text content (Quill sanitizes content so we need to retrieve it from the editor)
|
||||||
}
|
_originalrichhtml = await interop.GetHtml(_editorElement);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void CloseRichFileManager()
|
public void CloseRichFileManager()
|
||||||
{
|
{
|
||||||
_richfilemanager = false;
|
_richfilemanager = false;
|
||||||
_message = string.Empty;
|
_message = string.Empty;
|
||||||
StateHasChanged();
|
StateHasChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void CloseRawFileManager()
|
public void CloseRawFileManager()
|
||||||
{
|
{
|
||||||
_rawfilemanager = false;
|
_rawfilemanager = false;
|
||||||
_message = string.Empty;
|
_message = string.Empty;
|
||||||
StateHasChanged();
|
StateHasChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void RefreshRichText()
|
public void RefreshRichText()
|
||||||
{
|
{
|
||||||
_richhtml = _rawhtml;
|
_richhtml = _rawhtml;
|
||||||
StateHasChanged();
|
StateHasChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task RefreshRawHtml()
|
public async Task RefreshRawHtml()
|
||||||
{
|
{
|
||||||
var interop = new RichTextEditorInterop(JSRuntime);
|
var interop = new RichTextEditorInterop(JSRuntime);
|
||||||
_rawhtml = await interop.GetHtml(_editorElement);
|
_rawhtml = await interop.GetHtml(_editorElement);
|
||||||
StateHasChanged();
|
StateHasChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<string> GetHtml()
|
public async Task<string> GetHtml()
|
||||||
{
|
{
|
||||||
// evaluate raw html content as first priority
|
// evaluate raw html content as first priority
|
||||||
if (_rawhtml != _originalrawhtml)
|
if (_rawhtml != _originalrawhtml)
|
||||||
{
|
{
|
||||||
return _rawhtml;
|
return _rawhtml;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// return rich text content if it has changed
|
var richhtml = "";
|
||||||
var interop = new RichTextEditorInterop(JSRuntime);
|
if (AllowRichText)
|
||||||
var richhtml = await interop.GetHtml(_editorElement);
|
{
|
||||||
if (richhtml != _originalrichhtml)
|
// return rich text content if it has changed
|
||||||
|
var interop = new RichTextEditorInterop(JSRuntime);
|
||||||
|
richhtml = await interop.GetHtml(_editorElement);
|
||||||
|
}
|
||||||
|
// rich text value will only be blank if AllowRichText is disabled or the JS Interop method failed
|
||||||
|
if (richhtml != _originalrichhtml && !string.IsNullOrEmpty(richhtml) && !string.IsNullOrEmpty(_originalrichhtml))
|
||||||
{
|
{
|
||||||
return richhtml;
|
return richhtml;
|
||||||
}
|
}
|
||||||
|
@ -1,27 +1,30 @@
|
|||||||
@namespace Oqtane.Modules.Controls
|
@namespace Oqtane.Modules.Controls
|
||||||
@inherits LocalizableComponent
|
@inherits LocalizableComponent
|
||||||
|
|
||||||
<div class="d-flex mt-2">
|
@if (IsVisible)
|
||||||
<div>
|
{
|
||||||
<a data-bs-toggle="collapse" class="app-link-unstyled" href="#@Name" aria-expanded="@_expanded" aria-controls="@Name" @onclick:preventDefault="true">
|
<div class="d-flex mt-2">
|
||||||
<h5>@_heading</h5>
|
<div>
|
||||||
</a>
|
<a data-bs-toggle="collapse" class="app-link-unstyled" href="#@Name" aria-expanded="@_expanded" aria-controls="@Name" @onclick:preventDefault="true">
|
||||||
|
<h5>@_heading</h5>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<div class="ms-auto">
|
||||||
|
<a data-bs-toggle="collapse" class="app-link-unstyled float-right" href="#@Name" aria-expanded="@_expanded" aria-controls="@Name" @onclick:preventDefault="true">
|
||||||
|
<i class="oi oi-chevron-bottom"></i>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="ms-auto">
|
<div class="d-flex">
|
||||||
<a data-bs-toggle="collapse" class="app-link-unstyled float-right" href="#@Name" aria-expanded="@_expanded" aria-controls="@Name" @onclick:preventDefault="true">
|
<hr class="app-rule" />
|
||||||
<i class="oi oi-chevron-bottom"></i>
|
|
||||||
</a>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<div class="collapse @_show" id="@Name">
|
||||||
<div class="d-flex">
|
@if (ChildContent != null)
|
||||||
<hr class="app-rule" />
|
{
|
||||||
</div>
|
@ChildContent
|
||||||
<div class="collapse @_show" id="@Name">
|
}
|
||||||
@if (ChildContent != null)
|
</div>
|
||||||
{
|
}
|
||||||
@ChildContent
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
@code {
|
@code {
|
||||||
private string _heading = string.Empty;
|
private string _heading = string.Empty;
|
||||||
@ -40,6 +43,9 @@
|
|||||||
[Parameter]
|
[Parameter]
|
||||||
public string Expanded { get; set; } // optional - will default to false if not provided
|
public string Expanded { get; set; } // optional - will default to false if not provided
|
||||||
|
|
||||||
|
[Parameter]
|
||||||
|
public bool IsVisible { get; set; } = true;
|
||||||
|
|
||||||
protected override void OnParametersSet()
|
protected override void OnParametersSet()
|
||||||
{
|
{
|
||||||
base.OnParametersSet(); // must be included to call method in LocalizableComponent
|
base.OnParametersSet(); // must be included to call method in LocalizableComponent
|
||||||
|
@ -261,7 +261,12 @@ namespace Oqtane.Modules
|
|||||||
// UI methods
|
// UI methods
|
||||||
public void AddModuleMessage(string message, MessageType type)
|
public void AddModuleMessage(string message, MessageType type)
|
||||||
{
|
{
|
||||||
ModuleInstance.AddModuleMessage(message, type);
|
AddModuleMessage(message, type, "top");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void AddModuleMessage(string message, MessageType type, string position)
|
||||||
|
{
|
||||||
|
ModuleInstance.AddModuleMessage(message, type, position);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ClearModuleMessage()
|
public void ClearModuleMessage()
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
<TargetFramework>net8.0</TargetFramework>
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
<OutputType>Exe</OutputType>
|
<OutputType>Exe</OutputType>
|
||||||
<Configurations>Debug;Release</Configurations>
|
<Configurations>Debug;Release</Configurations>
|
||||||
<Version>5.0.0</Version>
|
<Version>5.0.1</Version>
|
||||||
<Product>Oqtane</Product>
|
<Product>Oqtane</Product>
|
||||||
<Authors>Shaun Walker</Authors>
|
<Authors>Shaun Walker</Authors>
|
||||||
<Company>.NET Foundation</Company>
|
<Company>.NET Foundation</Company>
|
||||||
@ -12,7 +12,7 @@
|
|||||||
<Copyright>.NET Foundation</Copyright>
|
<Copyright>.NET Foundation</Copyright>
|
||||||
<PackageProjectUrl>https://www.oqtane.org</PackageProjectUrl>
|
<PackageProjectUrl>https://www.oqtane.org</PackageProjectUrl>
|
||||||
<PackageLicenseUrl>https://github.com/oqtane/oqtane.framework/blob/dev/LICENSE</PackageLicenseUrl>
|
<PackageLicenseUrl>https://github.com/oqtane/oqtane.framework/blob/dev/LICENSE</PackageLicenseUrl>
|
||||||
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v5.0.0</PackageReleaseNotes>
|
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v5.0.1</PackageReleaseNotes>
|
||||||
<RepositoryUrl>https://github.com/oqtane/oqtane.framework</RepositoryUrl>
|
<RepositoryUrl>https://github.com/oqtane/oqtane.framework</RepositoryUrl>
|
||||||
<RepositoryType>Git</RepositoryType>
|
<RepositoryType>Git</RepositoryType>
|
||||||
<RootNamespace>Oqtane</RootNamespace>
|
<RootNamespace>Oqtane</RootNamespace>
|
||||||
|
153
Oqtane.Client/Resources/Modules/Admin/Languages/Edit.resx
Normal file
153
Oqtane.Client/Resources/Modules/Admin/Languages/Edit.resx
Normal file
@ -0,0 +1,153 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<root>
|
||||||
|
<!--
|
||||||
|
Microsoft ResX Schema
|
||||||
|
|
||||||
|
Version 2.0
|
||||||
|
|
||||||
|
The primary goals of this format is to allow a simple XML format
|
||||||
|
that is mostly human readable. The generation and parsing of the
|
||||||
|
various data types are done through the TypeConverter classes
|
||||||
|
associated with the data types.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
... ado.net/XML headers & schema ...
|
||||||
|
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||||
|
<resheader name="version">2.0</resheader>
|
||||||
|
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
|
||||||
|
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
|
||||||
|
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
|
||||||
|
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
|
||||||
|
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
|
||||||
|
<value>[base64 mime encoded serialized .NET Framework object]</value>
|
||||||
|
</data>
|
||||||
|
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||||
|
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||||
|
<comment>This is a comment</comment>
|
||||||
|
</data>
|
||||||
|
|
||||||
|
There are any number of "resheader" rows that contain simple
|
||||||
|
name/value pairs.
|
||||||
|
|
||||||
|
Each data row contains a name, and value. The row also contains a
|
||||||
|
type or mimetype. Type corresponds to a .NET class that support
|
||||||
|
text/value conversion through the TypeConverter architecture.
|
||||||
|
Classes that don't support this are serialized and stored with the
|
||||||
|
mimetype set.
|
||||||
|
|
||||||
|
The mimetype is used for serialized objects, and tells the
|
||||||
|
ResXResourceReader how to depersist the object. This is currently not
|
||||||
|
extensible. For a given mimetype the value must be set accordingly:
|
||||||
|
|
||||||
|
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||||
|
that the ResXResourceWriter will generate, however the reader can
|
||||||
|
read any of the formats listed below.
|
||||||
|
|
||||||
|
mimetype: application/x-microsoft.net.object.binary.base64
|
||||||
|
value : The object must be serialized with
|
||||||
|
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
||||||
|
: and then encoded with base64 encoding.
|
||||||
|
|
||||||
|
mimetype: application/x-microsoft.net.object.soap.base64
|
||||||
|
value : The object must be serialized with
|
||||||
|
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||||
|
: and then encoded with base64 encoding.
|
||||||
|
|
||||||
|
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||||
|
value : The object must be serialized into a byte array
|
||||||
|
: using a System.ComponentModel.TypeConverter
|
||||||
|
: and then encoded with base64 encoding.
|
||||||
|
-->
|
||||||
|
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||||
|
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||||
|
<xsd:element name="root" msdata:IsDataSet="true">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:choice maxOccurs="unbounded">
|
||||||
|
<xsd:element name="metadata">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||||
|
</xsd:sequence>
|
||||||
|
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||||
|
<xsd:attribute name="type" type="xsd:string" />
|
||||||
|
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||||
|
<xsd:attribute ref="xml:space" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
<xsd:element name="assembly">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:attribute name="alias" type="xsd:string" />
|
||||||
|
<xsd:attribute name="name" type="xsd:string" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
<xsd:element name="data">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||||
|
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||||
|
</xsd:sequence>
|
||||||
|
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
||||||
|
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||||
|
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||||
|
<xsd:attribute ref="xml:space" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
<xsd:element name="resheader">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||||
|
</xsd:sequence>
|
||||||
|
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
</xsd:choice>
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
</xsd:schema>
|
||||||
|
<resheader name="resmimetype">
|
||||||
|
<value>text/microsoft-resx</value>
|
||||||
|
</resheader>
|
||||||
|
<resheader name="version">
|
||||||
|
<value>2.0</value>
|
||||||
|
</resheader>
|
||||||
|
<resheader name="reader">
|
||||||
|
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||||
|
</resheader>
|
||||||
|
<resheader name="writer">
|
||||||
|
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||||
|
</resheader>
|
||||||
|
<data name="Error.Language.Edit" xml:space="preserve">
|
||||||
|
<value>Error Updating Language</value>
|
||||||
|
</data>
|
||||||
|
<data name="Name.HelpText" xml:space="preserve">
|
||||||
|
<value>Name Of The Langauage</value>
|
||||||
|
</data>
|
||||||
|
<data name="IsDefault.HelpText" xml:space="preserve">
|
||||||
|
<value>Indicates Whether Or Not This Language Is The Default For The Site</value>
|
||||||
|
</data>
|
||||||
|
<data name="Name.Text" xml:space="preserve">
|
||||||
|
<value>Name:</value>
|
||||||
|
</data>
|
||||||
|
<data name="IsDefault.Text" xml:space="preserve">
|
||||||
|
<value>Default?</value>
|
||||||
|
</data>
|
||||||
|
<data name="Success.Language.Download" xml:space="preserve">
|
||||||
|
<value>Translation Package Saved Successfully. You Must <a href={0}>Restart</a> To Complete The Installation.</value>
|
||||||
|
</data>
|
||||||
|
<data name="LanguageUpload.HelpText" xml:space="preserve">
|
||||||
|
<value>Upload one or more translation packages.</value>
|
||||||
|
</data>
|
||||||
|
<data name="LanguageUpload.Text" xml:space="preserve">
|
||||||
|
<value>Translation</value>
|
||||||
|
</data>
|
||||||
|
<data name="Manage.Heading" xml:space="preserve">
|
||||||
|
<value>Manage</value>
|
||||||
|
</data>
|
||||||
|
<data name="Upload.Heading" xml:space="preserve">
|
||||||
|
<value>Upload</value>
|
||||||
|
</data>
|
||||||
|
<data name="Error.Language.Load" xml:space="preserve">
|
||||||
|
<value>Error Loading Language</value>
|
||||||
|
</data>
|
||||||
|
</root>
|
@ -204,8 +204,8 @@
|
|||||||
<data name="ExternalLoginStatus.DuplicateEmail" xml:space="preserve">
|
<data name="ExternalLoginStatus.DuplicateEmail" xml:space="preserve">
|
||||||
<value>Multiple User Accounts Already Exist With The Email Address Of Your External Login. Please Contact Your Administrator For Further Instructions.</value>
|
<value>Multiple User Accounts Already Exist With The Email Address Of Your External Login. Please Contact Your Administrator For Further Instructions.</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="ExternalLoginStatus.InvalidEmail" xml:space="preserve">
|
<data name="ExternalLoginStatus.MissingClaims" xml:space="preserve">
|
||||||
<value>The External Login Provider Did Not Provide A Valid Email Address For Your Account. Please Contact Your Administrator For Further Instructions.</value>
|
<value>The External Login Provider Did Not Provide All Of The Required Information. Please Contact Your Administrator For Further Instructions.</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="ExternalLoginStatus.ProviderKeyMismatch" xml:space="preserve">
|
<data name="ExternalLoginStatus.ProviderKeyMismatch" xml:space="preserve">
|
||||||
<value>An Error Occurred Verifying Your External Login. Please Contact Your Administrator For Further Instructions.</value>
|
<value>An Error Occurred Verifying Your External Login. Please Contact Your Administrator For Further Instructions.</value>
|
||||||
@ -225,4 +225,7 @@
|
|||||||
<data name="ExternalLoginStatus.RemoteFailure" xml:space="preserve">
|
<data name="ExternalLoginStatus.RemoteFailure" xml:space="preserve">
|
||||||
<value>Your External Login Failed. Please Contact Your Administrator For Further Instructions.</value>
|
<value>Your External Login Failed. Please Contact Your Administrator For Further Instructions.</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="ExternalLoginStatus.ReviewClaims" xml:space="preserve">
|
||||||
|
<value>The Review Claims Option Was Enabled In External Login Settings. Please Visit The Event Log To View The Claims Returned By The Provider.</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
@ -240,4 +240,10 @@
|
|||||||
<data name="Validate" xml:space="preserve">
|
<data name="Validate" xml:space="preserve">
|
||||||
<value>Validate</value>
|
<value>Validate</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="Browse" xml:space="preserve">
|
||||||
|
<value>Browse</value>
|
||||||
|
</data>
|
||||||
|
<data name="Pages.Heading" xml:space="preserve">
|
||||||
|
<value>Pages</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
@ -127,7 +127,7 @@
|
|||||||
<value>Error Downloading Module</value>
|
<value>Error Downloading Module</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="Confirm.Module.Delete" xml:space="preserve">
|
<data name="Confirm.Module.Delete" xml:space="preserve">
|
||||||
<value>Are You Sure You Wish To Delete The {0} Module?</value>
|
<value>Are You Sure You Wish To Uninstall The {0} Module?</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="Error.Module.Load" xml:space="preserve">
|
<data name="Error.Module.Load" xml:space="preserve">
|
||||||
<value>Error Loading Modules</value>
|
<value>Error Loading Modules</value>
|
||||||
@ -142,10 +142,10 @@
|
|||||||
<value>Install Module</value>
|
<value>Install Module</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="DeleteModule.Header" xml:space="preserve">
|
<data name="DeleteModule.Header" xml:space="preserve">
|
||||||
<value>Delete Module</value>
|
<value>Uninstall Module</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="DeleteModule.Text" xml:space="preserve">
|
<data name="DeleteModule.Text" xml:space="preserve">
|
||||||
<value>Delete</value>
|
<value>Uninstall</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="InUse" xml:space="preserve">
|
<data name="InUse" xml:space="preserve">
|
||||||
<value>In Use?</value>
|
<value>In Use?</value>
|
||||||
|
@ -283,7 +283,7 @@
|
|||||||
<value>Prerender? </value>
|
<value>Prerender? </value>
|
||||||
</data>
|
</data>
|
||||||
<data name="Runtime.HelpText" xml:space="preserve">
|
<data name="Runtime.HelpText" xml:space="preserve">
|
||||||
<value>The Blazor runtime hosting model</value>
|
<value>The Blazor runtime hosting model for the site</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="Runtime.Text" xml:space="preserve">
|
<data name="Runtime.Text" xml:space="preserve">
|
||||||
<value>Runtime: </value>
|
<value>Runtime: </value>
|
||||||
@ -402,4 +402,25 @@
|
|||||||
<data name="Retention.Text" xml:space="preserve">
|
<data name="Retention.Text" xml:space="preserve">
|
||||||
<value>Retention (Days):</value>
|
<value>Retention (Days):</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="FileExtensions.Heading" xml:space="preserve">
|
||||||
|
<value>File Extensions</value>
|
||||||
|
</data>
|
||||||
|
<data name="ImageExtensions.HelpText" xml:space="preserve">
|
||||||
|
<value>Enter a comma separated list of image file extensions</value>
|
||||||
|
</data>
|
||||||
|
<data name="ImageExtensions.Text" xml:space="preserve">
|
||||||
|
<value>Image Extensions:</value>
|
||||||
|
</data>
|
||||||
|
<data name="UploadableFileExtensions.HelpText" xml:space="preserve">
|
||||||
|
<value>Enter a comma separated list of uploadable file extensions</value>
|
||||||
|
</data>
|
||||||
|
<data name="UploadableFileExtensions.Text" xml:space="preserve">
|
||||||
|
<value>Uploadable File Extensions:</value>
|
||||||
|
</data>
|
||||||
|
<data name="HybridEnabled.HelpText" xml:space="preserve">
|
||||||
|
<value>Specifies if the site can be integrated with an external .NET MAUI hybrid application</value>
|
||||||
|
</data>
|
||||||
|
<data name="HybridEnabled.Text" xml:space="preserve">
|
||||||
|
<value>Hybrid Enabled?</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
@ -124,7 +124,7 @@
|
|||||||
<value>Error Downloading Theme</value>
|
<value>Error Downloading Theme</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="Confirm.Theme.Delete" xml:space="preserve">
|
<data name="Confirm.Theme.Delete" xml:space="preserve">
|
||||||
<value>Are You Sure You Wish To Delete The {0} Theme?</value>
|
<value>Are You Sure You Wish To Uninstall The {0} Theme?</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="Error.Theme.Load" xml:space="preserve">
|
<data name="Error.Theme.Load" xml:space="preserve">
|
||||||
<value>Error Loading Themes</value>
|
<value>Error Loading Themes</value>
|
||||||
@ -136,10 +136,10 @@
|
|||||||
<value>Error Deleting Theme</value>
|
<value>Error Deleting Theme</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="DeleteTheme.Header" xml:space="preserve">
|
<data name="DeleteTheme.Header" xml:space="preserve">
|
||||||
<value>Delete Theme</value>
|
<value>Uninstall Theme</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="DeleteTheme.Text" xml:space="preserve">
|
<data name="DeleteTheme.Text" xml:space="preserve">
|
||||||
<value>Delete</value>
|
<value>Uninstall</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="CreateTheme.Text" xml:space="preserve">
|
<data name="CreateTheme.Text" xml:space="preserve">
|
||||||
<value>Create Theme</value>
|
<value>Create Theme</value>
|
||||||
|
@ -147,6 +147,9 @@
|
|||||||
<data name="Message.User.NoLogIn" xml:space="preserve">
|
<data name="Message.User.NoLogIn" xml:space="preserve">
|
||||||
<value>Current User Is Not Logged In</value>
|
<value>Current User Is Not Logged In</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="Message.User.NoEmail" xml:space="preserve">
|
||||||
|
<value>You Must Provide An Email Address For Your User Account</value>
|
||||||
|
</data>
|
||||||
<data name="Error.Profile.Load" xml:space="preserve">
|
<data name="Error.Profile.Load" xml:space="preserve">
|
||||||
<value>Error Loading User Profile</value>
|
<value>Error Loading User Profile</value>
|
||||||
</data>
|
</data>
|
||||||
|
@ -247,7 +247,7 @@
|
|||||||
<value>Domain Filter:</value>
|
<value>Domain Filter:</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="EmailClaimType.HelpText" xml:space="preserve">
|
<data name="EmailClaimType.HelpText" xml:space="preserve">
|
||||||
<value>The name of the email address claim provided by the identity provider</value>
|
<value>Optionally specify the type name of the email address claim provided by the identity provider. The typical value is 'email'.</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="EmailClaimType.Text" xml:space="preserve">
|
<data name="EmailClaimType.Text" xml:space="preserve">
|
||||||
<value>Email Claim:</value>
|
<value>Email Claim:</value>
|
||||||
@ -274,7 +274,7 @@
|
|||||||
<value>Use PKCE?</value>
|
<value>Use PKCE?</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="ProviderName.HelpText" xml:space="preserve">
|
<data name="ProviderName.HelpText" xml:space="preserve">
|
||||||
<value>The external login provider name which will be displayed on the login page</value>
|
<value>Specify a friendly name for the external login provider which will be displayed on the Login page</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="ProviderName.Text" xml:space="preserve">
|
<data name="ProviderName.Text" xml:space="preserve">
|
||||||
<value>Provider Name:</value>
|
<value>Provider Name:</value>
|
||||||
@ -373,7 +373,7 @@
|
|||||||
<value>Last Login</value>
|
<value>Last Login</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="IdentifierClaimType.HelpText" xml:space="preserve">
|
<data name="IdentifierClaimType.HelpText" xml:space="preserve">
|
||||||
<value>The name of the unique user identifier claim provided by the identity provider</value>
|
<value>Specify the type name of the unique user identifier claim provided by the identity provider. The default value is 'sub'.</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="IdentifierClaimType.Text" xml:space="preserve">
|
<data name="IdentifierClaimType.Text" xml:space="preserve">
|
||||||
<value>Identifier Claim:</value>
|
<value>Identifier Claim:</value>
|
||||||
@ -385,13 +385,13 @@
|
|||||||
<value>Parameters:</value>
|
<value>Parameters:</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="RoleClaimType.HelpText" xml:space="preserve">
|
<data name="RoleClaimType.HelpText" xml:space="preserve">
|
||||||
<value>Optionally provide the name of the role claim provided by the identity provider. These roles will be used in addition to any internal user roles assigned within the site.</value>
|
<value>Optionally provide the type name of the role claim provided by the identity provider. These roles will be used in addition to any internal user roles assigned within the site.</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="RoleClaimType.Text" xml:space="preserve">
|
<data name="RoleClaimType.Text" xml:space="preserve">
|
||||||
<value>Role Claim:</value>
|
<value>Role Claim:</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="ProfileClaimTypes.HelpText" xml:space="preserve">
|
<data name="ProfileClaimTypes.HelpText" xml:space="preserve">
|
||||||
<value>Optionally provide a comma delimited list of user profile claims provided by the identity provider, as well as mappings to your user profile definition. For example if the identity provider includes a 'given_name' claim and you have a 'FirstName' user profile definition you should specify 'given_name:FirstName'.</value>
|
<value>Optionally provide a comma delimited list of user profile claim type names provided by the identity provider, as well as mappings to your user profile definition. For example if the identity provider includes a 'given_name' claim and you have a 'FirstName' user profile definition you should specify 'given_name:FirstName'.</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="ProfileClaimTypes.Text" xml:space="preserve">
|
<data name="ProfileClaimTypes.Text" xml:space="preserve">
|
||||||
<value>User Profile Claims:</value>
|
<value>User Profile Claims:</value>
|
||||||
@ -409,31 +409,34 @@
|
|||||||
<value>Import Users</value>
|
<value>Import Users</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="AuthFlow.Code" xml:space="preserve">
|
<data name="AuthFlow.Code" xml:space="preserve">
|
||||||
<value>code</value>
|
<value>Authorization Code</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="AuthFlow.CodeIdToken" xml:space="preserve">
|
<data name="AuthFlow.CodeIdToken" xml:space="preserve">
|
||||||
<value>code id_token</value>
|
<value>Authorization Code + ID Token</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="AuthFlow.CodeIdTokenToken" xml:space="preserve">
|
<data name="AuthFlow.CodeIdTokenToken" xml:space="preserve">
|
||||||
<value>code id_token token</value>
|
<value>Authorization Code + ID Token + Access Token</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="AuthFlow.CodeToken" xml:space="preserve">
|
<data name="AuthFlow.CodeToken" xml:space="preserve">
|
||||||
<value>code token</value>
|
<value>Authorization Code + Access Token</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="AuthFlow.IdToken" xml:space="preserve">
|
<data name="AuthFlow.IdToken" xml:space="preserve">
|
||||||
<value>id_token</value>
|
<value>ID Token</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="AuthFlow.IdTokenToken" xml:space="preserve">
|
<data name="AuthFlow.IdTokenToken" xml:space="preserve">
|
||||||
<value>id_token token</value>
|
<value>ID Token + Access Token</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="AuthFlow.None" xml:space="preserve">
|
<data name="AuthFlow.None" xml:space="preserve">
|
||||||
<value>none</value>
|
<value>None</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="AuthFlow.Token" xml:space="preserve">
|
<data name="AuthFlow.Token" xml:space="preserve">
|
||||||
<value>token</value>
|
<value>Access Token</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="AuthResponseType" xml:space="preserve">
|
<data name="AuthResponseType.Text" xml:space="preserve">
|
||||||
<value>Authorization Response Type</value>
|
<value>Authorization Response Type:</value>
|
||||||
|
</data>
|
||||||
|
<data name="AuthResponseType.HelpText" xml:space="preserve">
|
||||||
|
<value>Specify the authorization response type. The default is Authorization Code which is considered to be the most secure option based on the latest OAuth specification.</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="VerifyUsers.HelpText" xml:space="preserve">
|
<data name="VerifyUsers.HelpText" xml:space="preserve">
|
||||||
<value>Do you want existing users to perform an additional email verification step to link their external login? If you disable this option, existing users will be linked automatically.</value>
|
<value>Do you want existing users to perform an additional email verification step to link their external login? If you disable this option, existing users will be linked automatically.</value>
|
||||||
@ -453,4 +456,16 @@
|
|||||||
<data name="CookieExpiration.Text" xml:space="preserve">
|
<data name="CookieExpiration.Text" xml:space="preserve">
|
||||||
<value>Cookie Expiration Timespan:</value>
|
<value>Cookie Expiration Timespan:</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="ReviewClaims.Text" xml:space="preserve">
|
||||||
|
<value>Review Claims?</value>
|
||||||
|
</data>
|
||||||
|
<data name="ReviewClaims.HelpText" xml:space="preserve">
|
||||||
|
<value>This option will record the full list of Claims returned by the Provider in the Event Log. It should only be used for testing purposes. External Login will be restricted when this option is enabled.</value>
|
||||||
|
</data>
|
||||||
|
<data name="NameClaimType.HelpText" xml:space="preserve">
|
||||||
|
<value>Optionally specify the type name of the user's name claim provided by the identity provider. The typical value is 'name'.</value>
|
||||||
|
</data>
|
||||||
|
<data name="NameClaimType.Text" xml:space="preserve">
|
||||||
|
<value>Name Claim:</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
@ -432,4 +432,10 @@
|
|||||||
<data name="ProfileRequired" xml:space="preserve">
|
<data name="ProfileRequired" xml:space="preserve">
|
||||||
<value>{0} Is Required</value>
|
<value>{0} Is Required</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="Uninstall" xml:space="preserve">
|
||||||
|
<value>Uninstall</value>
|
||||||
|
</data>
|
||||||
|
<data name="Test" xml:space="preserve">
|
||||||
|
<value>Test</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
@ -1,6 +1,6 @@
|
|||||||
using Oqtane.Models;
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using Oqtane.Models;
|
||||||
|
|
||||||
namespace Oqtane.Services
|
namespace Oqtane.Services
|
||||||
{
|
{
|
||||||
@ -39,6 +39,13 @@ namespace Oqtane.Services
|
|||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
Task<Language> AddLanguageAsync(Language language);
|
Task<Language> AddLanguageAsync(Language language);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Edits the given language
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="language"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
Task EditLanguageAsync(Language language);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Deletes the given language
|
/// Deletes the given language
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Oqtane.Documentation;
|
using Oqtane.Documentation;
|
||||||
@ -10,7 +9,7 @@ namespace Oqtane.Services
|
|||||||
{
|
{
|
||||||
[PrivateApi("Don't show in the documentation, as everything should use the Interface")]
|
[PrivateApi("Don't show in the documentation, as everything should use the Interface")]
|
||||||
public class LanguageService : ServiceBase, ILanguageService
|
public class LanguageService : ServiceBase, ILanguageService
|
||||||
{
|
{
|
||||||
public LanguageService(HttpClient http, SiteState siteState) : base(http, siteState) { }
|
public LanguageService(HttpClient http, SiteState siteState) : base(http, siteState) { }
|
||||||
|
|
||||||
private string Apiurl => CreateApiUrl("Language");
|
private string Apiurl => CreateApiUrl("Language");
|
||||||
@ -35,6 +34,11 @@ namespace Oqtane.Services
|
|||||||
return await PostJsonAsync<Language>(Apiurl, language);
|
return await PostJsonAsync<Language>(Apiurl, language);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task EditLanguageAsync(Language language)
|
||||||
|
{
|
||||||
|
await PutJsonAsync<Language>(Apiurl, language);
|
||||||
|
}
|
||||||
|
|
||||||
public async Task DeleteLanguageAsync(int languageId)
|
public async Task DeleteLanguageAsync(int languageId)
|
||||||
{
|
{
|
||||||
await DeleteAsync($"{Apiurl}/{languageId}");
|
await DeleteAsync($"{Apiurl}/{languageId}");
|
||||||
|
@ -26,8 +26,15 @@ namespace Oqtane.Themes.Controls
|
|||||||
var allowexternallogin = (SettingService.GetSetting(PageState.Site.Settings, "ExternalLogin:ProviderType", "") != "") ? true : false;
|
var allowexternallogin = (SettingService.GetSetting(PageState.Site.Settings, "ExternalLogin:ProviderType", "") != "") ? true : false;
|
||||||
var allowsitelogin = bool.Parse(SettingService.GetSetting(PageState.Site.Settings, "LoginOptions:AllowSiteLogin", "true"));
|
var allowsitelogin = bool.Parse(SettingService.GetSetting(PageState.Site.Settings, "LoginOptions:AllowSiteLogin", "true"));
|
||||||
|
|
||||||
Route route = new Route(PageState.Uri.AbsoluteUri, PageState.Alias.Path);
|
var returnurl = "";
|
||||||
var returnurl = WebUtility.UrlEncode(route.PathAndQuery);
|
if (!PageState.QueryString.ContainsKey("returnurl"))
|
||||||
|
{
|
||||||
|
returnurl = WebUtility.UrlEncode(PageState.Route.PathAndQuery); // remember current url
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
returnurl = PageState.QueryString["returnurl"]; // use existing value
|
||||||
|
}
|
||||||
|
|
||||||
if (allowexternallogin && !allowsitelogin)
|
if (allowexternallogin && !allowsitelogin)
|
||||||
{
|
{
|
||||||
@ -39,7 +46,6 @@ namespace Oqtane.Themes.Controls
|
|||||||
// local login
|
// local login
|
||||||
NavigationManager.NavigateTo(NavigateUrl("login", "?returnurl=" + returnurl));
|
NavigationManager.NavigateTo(NavigateUrl("login", "?returnurl=" + returnurl));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected async Task LogoutUser()
|
protected async Task LogoutUser()
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
@namespace Oqtane.Themes.Controls
|
@namespace Oqtane.Themes.Controls
|
||||||
|
@using System.Net
|
||||||
@inherits ThemeControlBase
|
@inherits ThemeControlBase
|
||||||
@inject IStringLocalizer<UserProfile> Localizer
|
@inject IStringLocalizer<UserProfile> Localizer
|
||||||
|
|
||||||
@ -26,14 +27,21 @@
|
|||||||
[Parameter]
|
[Parameter]
|
||||||
public bool ShowRegister { get; set; }
|
public bool ShowRegister { get; set; }
|
||||||
|
|
||||||
|
private string _returnurl = "";
|
||||||
|
|
||||||
|
protected override void OnParametersSet()
|
||||||
|
{
|
||||||
|
_returnurl = WebUtility.UrlEncode(PageState.Route.PathAndQuery);
|
||||||
|
}
|
||||||
|
|
||||||
private void RegisterUser()
|
private void RegisterUser()
|
||||||
{
|
{
|
||||||
NavigationManager.NavigateTo(NavigateUrl("register"));
|
NavigationManager.NavigateTo(NavigateUrl("register", "returnurl=" + _returnurl));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void UpdateProfile()
|
private void UpdateProfile()
|
||||||
{
|
{
|
||||||
NavigationManager.NavigateTo(NavigateUrl("profile"));
|
NavigationManager.NavigateTo(NavigateUrl("profile", "returnurl=" + _returnurl));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5,7 +5,10 @@
|
|||||||
|
|
||||||
@if (CurrentException is null)
|
@if (CurrentException is null)
|
||||||
{
|
{
|
||||||
<ModuleMessage Message="@_message" Type="@_messageType"/>
|
if (_messagePosition == "top")
|
||||||
|
{
|
||||||
|
<ModuleMessage Message="@_message" Type="@_messageType" />
|
||||||
|
}
|
||||||
@if (ModuleType != null)
|
@if (ModuleType != null)
|
||||||
{
|
{
|
||||||
<DynamicComponent Type="@ModuleType" Parameters="@ModuleParameters"></DynamicComponent>
|
<DynamicComponent Type="@ModuleType" Parameters="@ModuleParameters"></DynamicComponent>
|
||||||
@ -14,6 +17,10 @@
|
|||||||
<div class="app-progress-indicator"></div>
|
<div class="app-progress-indicator"></div>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (_messagePosition == "bottom")
|
||||||
|
{
|
||||||
|
<ModuleMessage Message="@_message" Type="@_messageType" />
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -24,49 +31,58 @@ else
|
|||||||
}
|
}
|
||||||
|
|
||||||
@code {
|
@code {
|
||||||
private string _message;
|
private string _message;
|
||||||
private string _error;
|
private string _error;
|
||||||
private MessageType _messageType;
|
private MessageType _messageType;
|
||||||
private bool _progressIndicator = false;
|
private string _messagePosition;
|
||||||
|
private bool _progressIndicator = false;
|
||||||
|
|
||||||
private Type ModuleType { get; set; }
|
private Type ModuleType { get; set; }
|
||||||
private IDictionary<string, object> ModuleParameters { get; set; }
|
private IDictionary<string, object> ModuleParameters { get; set; }
|
||||||
|
|
||||||
[CascadingParameter]
|
[CascadingParameter]
|
||||||
protected PageState PageState { get; set; }
|
protected PageState PageState { get; set; }
|
||||||
|
|
||||||
[CascadingParameter]
|
[CascadingParameter]
|
||||||
private Module ModuleState { get; set; }
|
private Module ModuleState { get; set; }
|
||||||
|
|
||||||
private ModuleMessage ModuleMessage { get; set; }
|
private ModuleMessage ModuleMessage { get; set; }
|
||||||
|
|
||||||
protected override void OnParametersSet()
|
protected override void OnParametersSet()
|
||||||
{
|
{
|
||||||
_message = "";
|
_message = "";
|
||||||
if (!string.IsNullOrEmpty(ModuleState.ModuleType))
|
if (!string.IsNullOrEmpty(ModuleState.ModuleType))
|
||||||
{
|
{
|
||||||
ModuleType = Type.GetType(ModuleState.ModuleType);
|
ModuleType = Type.GetType(ModuleState.ModuleType);
|
||||||
if (ModuleType != null)
|
if (ModuleType != null)
|
||||||
{
|
{
|
||||||
ModuleParameters = new Dictionary<string, object> { { "ModuleInstance", this } };
|
ModuleParameters = new Dictionary<string, object> { { "ModuleInstance", this } };
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// module does not exist with typename specified
|
// module does not exist with typename specified
|
||||||
_message = string.Format(Localizer["Error.Module.InvalidName"], Utilities.GetTypeNameLastSegment(ModuleState.ModuleType, 0));
|
_message = string.Format(Localizer["Error.Module.InvalidName"], Utilities.GetTypeNameLastSegment(ModuleState.ModuleType, 0));
|
||||||
_messageType = MessageType.Error;
|
_messageType = MessageType.Error;
|
||||||
}
|
_messagePosition = "top";
|
||||||
else
|
}
|
||||||
{
|
else
|
||||||
_message = string.Format(Localizer["Error.Module.InvalidType"], ModuleState.ModuleDefinitionName);
|
{
|
||||||
_messageType = MessageType.Error;
|
_message = string.Format(Localizer["Error.Module.InvalidType"], ModuleState.ModuleDefinitionName);
|
||||||
}
|
_messageType = MessageType.Error;
|
||||||
}
|
_messagePosition = "top";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void AddModuleMessage(string message, MessageType type)
|
public void AddModuleMessage(string message, MessageType type)
|
||||||
{
|
{
|
||||||
_message = message;
|
AddModuleMessage(message, type, "top");
|
||||||
_messageType = type;
|
}
|
||||||
_progressIndicator = false;
|
|
||||||
|
public void AddModuleMessage(string message, MessageType type, string position)
|
||||||
|
{
|
||||||
|
_message = message;
|
||||||
|
_messageType = type;
|
||||||
|
_messagePosition = position;
|
||||||
|
_progressIndicator = false;
|
||||||
StateHasChanged();
|
StateHasChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -105,11 +105,18 @@
|
|||||||
Route route = new Route(_absoluteUri, SiteState.Alias.Path);
|
Route route = new Route(_absoluteUri, SiteState.Alias.Path);
|
||||||
int moduleid = (int.TryParse(route.ModuleId, out moduleid)) ? moduleid : -1;
|
int moduleid = (int.TryParse(route.ModuleId, out moduleid)) ? moduleid : -1;
|
||||||
var action = (!string.IsNullOrEmpty(route.Action)) ? route.Action : Constants.DefaultAction;
|
var action = (!string.IsNullOrEmpty(route.Action)) ? route.Action : Constants.DefaultAction;
|
||||||
|
|
||||||
var querystring = Utilities.ParseQueryString(route.Query);
|
var querystring = Utilities.ParseQueryString(route.Query);
|
||||||
var returnurl = "";
|
var returnurl = "";
|
||||||
if (querystring.ContainsKey("returnurl"))
|
if (querystring.ContainsKey("returnurl"))
|
||||||
{
|
{
|
||||||
returnurl = WebUtility.UrlDecode(querystring["returnurl"]);
|
returnurl = WebUtility.UrlDecode(querystring["returnurl"]);
|
||||||
|
if (!returnurl.StartsWith("/"))
|
||||||
|
{
|
||||||
|
// urls which are not relative are vulnerable to open redirects or XSS
|
||||||
|
returnurl = "";
|
||||||
|
querystring["returnurl"] = "";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// reload the client application from the server if there is a forced reload
|
// reload the client application from the server if there is a forced reload
|
||||||
@ -155,7 +162,8 @@
|
|||||||
if (PageState == null || refresh || PageState.Alias.SiteId != SiteState.Alias.SiteId)
|
if (PageState == null || refresh || PageState.Alias.SiteId != SiteState.Alias.SiteId)
|
||||||
{
|
{
|
||||||
var authState = await AuthenticationStateProvider.GetAuthenticationStateAsync();
|
var authState = await AuthenticationStateProvider.GetAuthenticationStateAsync();
|
||||||
if (authState.User.Identity.IsAuthenticated)
|
// verify user is authenticated for current site
|
||||||
|
if (authState.User.Identity.IsAuthenticated && authState.User.Claims.Any(item => item.Type == "sitekey" && item.Value == SiteState.Alias.SiteKey))
|
||||||
{
|
{
|
||||||
user = await UserService.GetUserAsync(authState.User.Identity.Name, SiteState.Alias.SiteId);
|
user = await UserService.GetUserAsync(authState.User.Identity.Name, SiteState.Alias.SiteId);
|
||||||
if (user != null)
|
if (user != null)
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
@namespace Oqtane.UI
|
@namespace Oqtane.UI
|
||||||
|
@using System.Net
|
||||||
@inject IJSRuntime JSRuntime
|
@inject IJSRuntime JSRuntime
|
||||||
@inject NavigationManager NavigationManager
|
@inject NavigationManager NavigationManager
|
||||||
@inject SiteState SiteState
|
@inject SiteState SiteState
|
||||||
@ -87,6 +88,13 @@
|
|||||||
|
|
||||||
protected override async Task OnAfterRenderAsync(bool firstRender)
|
protected override async Task OnAfterRenderAsync(bool firstRender)
|
||||||
{
|
{
|
||||||
|
// force authenticated user to provide email address (email may be missing if using external login)
|
||||||
|
if (PageState.User != null && PageState.User.IsAuthenticated && string.IsNullOrEmpty(PageState.User.Email) && PageState.Route.PagePath != "profile")
|
||||||
|
{
|
||||||
|
NavigationManager.NavigateTo(Utilities.NavigateUrl(PageState.Alias.Path, "profile", "returnurl=" + WebUtility.UrlEncode(PageState.Route.PathAndQuery)));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (!firstRender)
|
if (!firstRender)
|
||||||
{
|
{
|
||||||
if (!string.IsNullOrEmpty(PageState.Page.HeadContent) && PageState.Page.HeadContent.Contains("<script"))
|
if (!string.IsNullOrEmpty(PageState.Page.HeadContent) && PageState.Page.HeadContent.Contains("<script"))
|
||||||
|
@ -1,12 +1,10 @@
|
|||||||
using System.Data;
|
using System.Data;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Microsoft.EntityFrameworkCore.Migrations;
|
|
||||||
using Microsoft.EntityFrameworkCore.Migrations.Operations;
|
using Microsoft.EntityFrameworkCore.Migrations.Operations;
|
||||||
using Microsoft.EntityFrameworkCore.Migrations.Operations.Builders;
|
using Microsoft.EntityFrameworkCore.Migrations.Operations.Builders;
|
||||||
using MySql.Data.MySqlClient;
|
using MySql.Data.MySqlClient;
|
||||||
using MySql.EntityFrameworkCore.Metadata;
|
using MySql.EntityFrameworkCore.Metadata;
|
||||||
using Oqtane.Databases;
|
using Oqtane.Databases;
|
||||||
using Oqtane.Shared;
|
|
||||||
|
|
||||||
namespace Oqtane.Database.MySQL
|
namespace Oqtane.Database.MySQL
|
||||||
{
|
{
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net8.0</TargetFramework>
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
<Version>5.0.0</Version>
|
<Version>5.0.1</Version>
|
||||||
<Product>Oqtane</Product>
|
<Product>Oqtane</Product>
|
||||||
<Authors>Shaun Walker</Authors>
|
<Authors>Shaun Walker</Authors>
|
||||||
<Company>.NET Foundation</Company>
|
<Company>.NET Foundation</Company>
|
||||||
@ -10,14 +10,10 @@
|
|||||||
<Copyright>.NET Foundation</Copyright>
|
<Copyright>.NET Foundation</Copyright>
|
||||||
<PackageProjectUrl>https://www.oqtane.org</PackageProjectUrl>
|
<PackageProjectUrl>https://www.oqtane.org</PackageProjectUrl>
|
||||||
<PackageLicenseUrl>https://github.com/oqtane/oqtane.framework/blob/dev/LICENSE</PackageLicenseUrl>
|
<PackageLicenseUrl>https://github.com/oqtane/oqtane.framework/blob/dev/LICENSE</PackageLicenseUrl>
|
||||||
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v5.0.0</PackageReleaseNotes>
|
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v5.0.1</PackageReleaseNotes>
|
||||||
<RepositoryUrl>https://github.com/oqtane/oqtane.framework</RepositoryUrl>
|
<RepositoryUrl>https://github.com/oqtane/oqtane.framework</RepositoryUrl>
|
||||||
<RepositoryType>Git</RepositoryType>
|
<RepositoryType>Git</RepositoryType>
|
||||||
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
|
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
|
||||||
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
|
|
||||||
<NuspecFile>$(MSBuildProjectName).nuspec</NuspecFile>
|
|
||||||
<PackageName>$(MSBuildProjectName).$(Version).nupkg</PackageName>
|
|
||||||
<SuppressDependenciesWhenPacking>true</SuppressDependenciesWhenPacking>
|
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
|
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
|
||||||
@ -27,6 +23,14 @@
|
|||||||
<PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
|
<PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
|
||||||
<OutputPath>bin</OutputPath>
|
<OutputPath>bin</OutputPath>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
||||||
|
<NoWarn>1701;1702;EF1001;AD0001</NoWarn>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
|
||||||
|
<NoWarn>1701;1702;EF1001;AD0001</NoWarn>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="MySql.EntityFrameworkCore" Version="8.0.0-preview" />
|
<PackageReference Include="MySql.EntityFrameworkCore" Version="8.0.0-preview" />
|
||||||
@ -37,7 +41,12 @@
|
|||||||
<ProjectReference Include="..\Oqtane.Server\Oqtane.Server.csproj" />
|
<ProjectReference Include="..\Oqtane.Server\Oqtane.Server.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<Target Name="CopyPackage" AfterTargets="Pack">
|
<ItemGroup>
|
||||||
<Copy SourceFiles="$(OutputPath)..\$(PackageName)" DestinationFiles="..\Oqtane.Server\wwwroot\Packages\$(MSBuildProjectName).nupkg.bak" />
|
<MySQLFiles Include="$(OutputPath)Oqtane.Database.MySQL.dll;$(OutputPath)Oqtane.Database.MySQL.pdb;$(OutputPath)MySql.EntityFrameworkCore.dll;$(OutputPath)MySql.Data.dll" DestinationPath="..\Oqtane.Server\bin\$(Configuration)\net8.0\%(Filename)%(Extension)" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<Target Name="PublishProvider" AfterTargets="PostBuildEvent" Inputs="@(MySQLFiles)" Outputs="@(MySQLFiles->'%(DestinationPath)')">
|
||||||
|
<Copy SourceFiles="@(MySQLFiles)" DestinationFiles="@(MySQLFiles->'%(DestinationPath)')" />
|
||||||
</Target>
|
</Target>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
@ -1,26 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
|
|
||||||
<metadata>
|
|
||||||
<id>Oqtane.Database.MySQL</id>
|
|
||||||
<version>5.0.0</version>
|
|
||||||
<authors>Shaun Walker</authors>
|
|
||||||
<owners>.NET Foundation</owners>
|
|
||||||
<title>Oqtane MySQL Provider</title>
|
|
||||||
<summary>MySQL database support for the Oqtane Framework</summary>
|
|
||||||
<description>MySQL database support for the Oqtane Framework</description>
|
|
||||||
<copyright>.NET Foundation</copyright>
|
|
||||||
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
|
||||||
<license type="expression">MIT</license>
|
|
||||||
<projectUrl>https://github.com/oqtane/oqtane.framework</projectUrl>
|
|
||||||
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v5.0.0</releaseNotes>
|
|
||||||
<icon>icon.png</icon>
|
|
||||||
<tags>oqtane</tags>
|
|
||||||
</metadata>
|
|
||||||
<files>
|
|
||||||
<file src="bin\net8.0\Oqtane.Database.MySQL.dll" target="lib\net8.0" />
|
|
||||||
<file src="bin\net8.0\Oqtane.Database.MySQL.pdb" target="lib\net8.0" />
|
|
||||||
<file src="bin\net8.0\MySql.EntityFrameworkCore.dll" target="lib\net8.0" />
|
|
||||||
<file src="bin\net8.0\MySql.Data.dll" target="lib\net8.0" />
|
|
||||||
<file src="icon.png" target="" />
|
|
||||||
</files>
|
|
||||||
</package>
|
|
Binary file not shown.
Before Width: | Height: | Size: 4.8 KiB |
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net8.0</TargetFramework>
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
<Version>5.0.0</Version>
|
<Version>5.0.1</Version>
|
||||||
<Product>Oqtane</Product>
|
<Product>Oqtane</Product>
|
||||||
<Authors>Shaun Walker</Authors>
|
<Authors>Shaun Walker</Authors>
|
||||||
<Company>.NET Foundation</Company>
|
<Company>.NET Foundation</Company>
|
||||||
@ -10,14 +10,10 @@
|
|||||||
<Copyright>.NET Foundation</Copyright>
|
<Copyright>.NET Foundation</Copyright>
|
||||||
<PackageProjectUrl>https://www.oqtane.org</PackageProjectUrl>
|
<PackageProjectUrl>https://www.oqtane.org</PackageProjectUrl>
|
||||||
<PackageLicenseUrl>https://github.com/oqtane/oqtane.framework/blob/dev/LICENSE</PackageLicenseUrl>
|
<PackageLicenseUrl>https://github.com/oqtane/oqtane.framework/blob/dev/LICENSE</PackageLicenseUrl>
|
||||||
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v5.0.0</PackageReleaseNotes>
|
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v5.0.1</PackageReleaseNotes>
|
||||||
<RepositoryUrl>https://github.com/oqtane/oqtane.framework</RepositoryUrl>
|
<RepositoryUrl>https://github.com/oqtane/oqtane.framework</RepositoryUrl>
|
||||||
<RepositoryType>Git</RepositoryType>
|
<RepositoryType>Git</RepositoryType>
|
||||||
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
|
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
|
||||||
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
|
|
||||||
<NuspecFile>$(MSBuildProjectName).nuspec</NuspecFile>
|
|
||||||
<PackageName>$(MSBuildProjectName).$(Version).nupkg</PackageName>
|
|
||||||
<SuppressDependenciesWhenPacking>true</SuppressDependenciesWhenPacking>
|
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
|
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
|
||||||
@ -27,18 +23,31 @@
|
|||||||
<PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
|
<PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
|
||||||
<OutputPath>bin</OutputPath>
|
<OutputPath>bin</OutputPath>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
||||||
|
<NoWarn>1701;1702;EF1001;AD0001</NoWarn>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
|
||||||
|
<NoWarn>1701;1702;EF1001;AD0001</NoWarn>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="EFCore.NamingConventions" Version="8.0.0-rc.2" />
|
<PackageReference Include="EFCore.NamingConventions" Version="8.0.0-rc.2" />
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="8.0.0" />
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="8.0.0" />
|
||||||
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="8.0.0-rc.2" />
|
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="8.0.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\Oqtane.Server\Oqtane.Server.csproj" />
|
<ProjectReference Include="..\Oqtane.Server\Oqtane.Server.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<Target Name="CopyPackage" AfterTargets="Pack">
|
<ItemGroup>
|
||||||
<Copy SourceFiles="$(OutputPath)..\$(PackageName)" DestinationFiles="..\Oqtane.Server\wwwroot\Packages\$(MSBuildProjectName).nupkg.bak" />
|
<PostgreSQLFiles Include="$(OutputPath)Oqtane.Database.PostgreSQL.dll;$(OutputPath)Oqtane.Database.PostgreSQL.pdb;$(OutputPath)EFCore.NamingConventions.dll;$(OutputPath)Npgsql.EntityFrameworkCore.PostgreSQL.dll;$(OutputPath)Npgsql.dll" DestinationPath="..\Oqtane.Server\bin\$(Configuration)\net8.0\%(Filename)%(Extension)" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<Target Name="PublishProvider" AfterTargets="PostBuildEvent" Inputs="@(PostgreSQLFiles)" Outputs="@(PostgreSQLFiles->'%(DestinationPath)')">
|
||||||
|
<Copy SourceFiles="@(PostgreSQLFiles)" DestinationFiles="@(PostgreSQLFiles->'%(DestinationPath)')" />
|
||||||
</Target>
|
</Target>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
@ -1,27 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
|
|
||||||
<metadata>
|
|
||||||
<id>Oqtane.Database.PostgreSQL</id>
|
|
||||||
<version>5.0.0</version>
|
|
||||||
<authors>Shaun Walker</authors>
|
|
||||||
<owners>.NET Foundation</owners>
|
|
||||||
<title>Oqtane PostgreSQL Provider</title>
|
|
||||||
<summary>PostgreSQL database support for the Oqtane Framework</summary>
|
|
||||||
<description>PostgreSQL database support for the Oqtane Framework</description>
|
|
||||||
<copyright>.NET Foundation</copyright>
|
|
||||||
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
|
||||||
<license type="expression">MIT</license>
|
|
||||||
<projectUrl>https://github.com/oqtane/oqtane.framework</projectUrl>
|
|
||||||
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v5.0.0</releaseNotes>
|
|
||||||
<icon>icon.png</icon>
|
|
||||||
<tags>oqtane</tags>
|
|
||||||
</metadata>
|
|
||||||
<files>
|
|
||||||
<file src="bin\net8.0\Oqtane.Database.PostgreSQL.dll" target="lib\net8.0" />
|
|
||||||
<file src="bin\net8.0\Oqtane.Database.PostgreSQL.pdb" target="lib\net8.0" />
|
|
||||||
<file src="bin\net8.0\EFCore.NamingConventions.dll" target="lib\net8.0" />
|
|
||||||
<file src="bin\net8.0\Npgsql.EntityFrameworkCore.PostgreSQL.dll" target="lib\net8.0" />
|
|
||||||
<file src="bin\net8.0\Npgsql.dll" target="lib\net8.0" />
|
|
||||||
<file src="icon.png" target="" />
|
|
||||||
</files>
|
|
||||||
</package>
|
|
@ -1,11 +1,9 @@
|
|||||||
using System;
|
using System;
|
||||||
using Microsoft.EntityFrameworkCore;
|
|
||||||
using Microsoft.EntityFrameworkCore.Metadata.Builders;
|
using Microsoft.EntityFrameworkCore.Metadata.Builders;
|
||||||
using Microsoft.EntityFrameworkCore.Migrations;
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
using Npgsql.EntityFrameworkCore.PostgreSQL.Migrations.Internal;
|
using Npgsql.EntityFrameworkCore.PostgreSQL.Migrations.Internal;
|
||||||
using Oqtane.Migrations.Framework;
|
using Oqtane.Migrations.Framework;
|
||||||
using Oqtane.Models;
|
using Oqtane.Models;
|
||||||
using Oqtane.Shared;
|
|
||||||
|
|
||||||
// ReSharper disable ClassNeverInstantiated.Global
|
// ReSharper disable ClassNeverInstantiated.Global
|
||||||
|
|
||||||
|
@ -9,7 +9,6 @@ using Microsoft.EntityFrameworkCore.Migrations.Operations.Builders;
|
|||||||
using Npgsql;
|
using Npgsql;
|
||||||
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
||||||
using Oqtane.Databases;
|
using Oqtane.Databases;
|
||||||
using Oqtane.Shared;
|
|
||||||
|
|
||||||
namespace Oqtane.Database.PostgreSQL
|
namespace Oqtane.Database.PostgreSQL
|
||||||
{
|
{
|
||||||
|
Binary file not shown.
Before Width: | Height: | Size: 4.8 KiB |
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net8.0</TargetFramework>
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
<Version>5.0.0</Version>
|
<Version>5.0.1</Version>
|
||||||
<Product>Oqtane</Product>
|
<Product>Oqtane</Product>
|
||||||
<Authors>Shaun Walker</Authors>
|
<Authors>Shaun Walker</Authors>
|
||||||
<Company>.NET Foundation</Company>
|
<Company>.NET Foundation</Company>
|
||||||
@ -10,14 +10,10 @@
|
|||||||
<Copyright>.NET Foundation</Copyright>
|
<Copyright>.NET Foundation</Copyright>
|
||||||
<PackageProjectUrl>https://www.oqtane.org</PackageProjectUrl>
|
<PackageProjectUrl>https://www.oqtane.org</PackageProjectUrl>
|
||||||
<PackageLicenseUrl>https://github.com/oqtane/oqtane.framework/blob/dev/LICENSE</PackageLicenseUrl>
|
<PackageLicenseUrl>https://github.com/oqtane/oqtane.framework/blob/dev/LICENSE</PackageLicenseUrl>
|
||||||
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v5.0.0</PackageReleaseNotes>
|
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v5.0.1</PackageReleaseNotes>
|
||||||
<RepositoryUrl>https://github.com/oqtane/oqtane.framework</RepositoryUrl>
|
<RepositoryUrl>https://github.com/oqtane/oqtane.framework</RepositoryUrl>
|
||||||
<RepositoryType>Git</RepositoryType>
|
<RepositoryType>Git</RepositoryType>
|
||||||
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
|
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
|
||||||
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
|
|
||||||
<NuspecFile>$(MSBuildProjectName).nuspec</NuspecFile>
|
|
||||||
<PackageName>$(MSBuildProjectName).$(Version).nupkg</PackageName>
|
|
||||||
<SuppressDependenciesWhenPacking>true</SuppressDependenciesWhenPacking>
|
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
|
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
|
||||||
@ -28,6 +24,14 @@
|
|||||||
<OutputPath>bin</OutputPath>
|
<OutputPath>bin</OutputPath>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
||||||
|
<NoWarn>1701;1702;EF1001;AD0001</NoWarn>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
|
||||||
|
<NoWarn>1701;1702;EF1001;AD0001</NoWarn>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="8.0.0" />
|
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="8.0.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
@ -36,8 +40,12 @@
|
|||||||
<ProjectReference Include="..\Oqtane.Server\Oqtane.Server.csproj" />
|
<ProjectReference Include="..\Oqtane.Server\Oqtane.Server.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<Target Name="CopyPackage" AfterTargets="Pack">
|
<ItemGroup>
|
||||||
<Copy SourceFiles="$(OutputPath)..\$(PackageName)" DestinationFiles="..\Oqtane.Server\wwwroot\Packages\$(MSBuildProjectName).nupkg.bak" />
|
<SqlServerFiles Include="$(OutputPath)Oqtane.Database.SqlServer.dll;$(OutputPath)Oqtane.Database.SqlServer.pdb;$(OutputPath)Microsoft.EntityFrameworkCore.SqlServer.dll" DestinationPath="..\Oqtane.Server\bin\$(Configuration)\net8.0\%(Filename)%(Extension)" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<Target Name="PublishProvider" AfterTargets="PostBuildEvent" Inputs="@(SqlServerFiles)" Outputs="@(SqlServerFiles->'%(DestinationPath)')">
|
||||||
|
<Copy SourceFiles="@(SqlServerFiles)" DestinationFiles="@(SqlServerFiles->'%(DestinationPath)')" />
|
||||||
</Target>
|
</Target>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
@ -1,25 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
|
|
||||||
<metadata>
|
|
||||||
<id>Oqtane.Database.SqlServer</id>
|
|
||||||
<version>5.0.0</version>
|
|
||||||
<authors>Shaun Walker</authors>
|
|
||||||
<owners>.NET Foundation</owners>
|
|
||||||
<title>Oqtane SQL Server Provider</title>
|
|
||||||
<summary>SQL Server database support for the Oqtane Framework</summary>
|
|
||||||
<description>SQL Server database support for the Oqtane Framework</description>
|
|
||||||
<copyright>.NET Foundation</copyright>
|
|
||||||
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
|
||||||
<license type="expression">MIT</license>
|
|
||||||
<projectUrl>https://github.com/oqtane/oqtane.framework</projectUrl>
|
|
||||||
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v5.0.0</releaseNotes>
|
|
||||||
<icon>icon.png</icon>
|
|
||||||
<tags>oqtane</tags>
|
|
||||||
</metadata>
|
|
||||||
<files>
|
|
||||||
<file src="bin\net8.0\Oqtane.Database.SqlServer.dll" target="lib\net8.0" />
|
|
||||||
<file src="bin\net8.0\Oqtane.Database.SqlServer.pdb" target="lib\net8.0" />
|
|
||||||
<file src="bin\net8.0\Microsoft.EntityFrameworkCore.SqlServer.dll" target="lib\net8.0" />
|
|
||||||
<file src="icon.png" target="" />
|
|
||||||
</files>
|
|
||||||
</package>
|
|
@ -1,13 +1,9 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Text;
|
|
||||||
using Microsoft.EntityFrameworkCore;
|
|
||||||
using Microsoft.EntityFrameworkCore.Metadata.Builders;
|
using Microsoft.EntityFrameworkCore.Metadata.Builders;
|
||||||
using Microsoft.EntityFrameworkCore.Migrations;
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
using Microsoft.EntityFrameworkCore.SqlServer.Migrations.Internal;
|
using Microsoft.EntityFrameworkCore.SqlServer.Migrations.Internal;
|
||||||
using Microsoft.EntityFrameworkCore.Storage;
|
|
||||||
using Oqtane.Migrations.Framework;
|
using Oqtane.Migrations.Framework;
|
||||||
using Oqtane.Models;
|
using Oqtane.Models;
|
||||||
using Oqtane.Shared;
|
|
||||||
|
|
||||||
// ReSharper disable ClassNeverInstantiated.Global
|
// ReSharper disable ClassNeverInstantiated.Global
|
||||||
|
|
||||||
|
@ -6,7 +6,6 @@ using Microsoft.EntityFrameworkCore.Migrations;
|
|||||||
using Microsoft.EntityFrameworkCore.Migrations.Operations;
|
using Microsoft.EntityFrameworkCore.Migrations.Operations;
|
||||||
using Microsoft.EntityFrameworkCore.Migrations.Operations.Builders;
|
using Microsoft.EntityFrameworkCore.Migrations.Operations.Builders;
|
||||||
using Oqtane.Databases;
|
using Oqtane.Databases;
|
||||||
using Oqtane.Shared;
|
|
||||||
|
|
||||||
namespace Oqtane.Database.SqlServer
|
namespace Oqtane.Database.SqlServer
|
||||||
{
|
{
|
||||||
|
Binary file not shown.
Before Width: | Height: | Size: 4.8 KiB |
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net8.0</TargetFramework>
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
<Version>5.0.0</Version>
|
<Version>5.0.1</Version>
|
||||||
<Product>Oqtane</Product>
|
<Product>Oqtane</Product>
|
||||||
<Authors>Shaun Walker</Authors>
|
<Authors>Shaun Walker</Authors>
|
||||||
<Company>.NET Foundation</Company>
|
<Company>.NET Foundation</Company>
|
||||||
@ -10,14 +10,10 @@
|
|||||||
<Copyright>.NET Foundation</Copyright>
|
<Copyright>.NET Foundation</Copyright>
|
||||||
<PackageProjectUrl>https://www.oqtane.org</PackageProjectUrl>
|
<PackageProjectUrl>https://www.oqtane.org</PackageProjectUrl>
|
||||||
<PackageLicenseUrl>https://github.com/oqtane/oqtane.framework/blob/dev/LICENSE</PackageLicenseUrl>
|
<PackageLicenseUrl>https://github.com/oqtane/oqtane.framework/blob/dev/LICENSE</PackageLicenseUrl>
|
||||||
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v5.0.0</PackageReleaseNotes>
|
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v5.0.1</PackageReleaseNotes>
|
||||||
<RepositoryUrl>https://github.com/oqtane/oqtane.framework</RepositoryUrl>
|
<RepositoryUrl>https://github.com/oqtane/oqtane.framework</RepositoryUrl>
|
||||||
<RepositoryType>Git</RepositoryType>
|
<RepositoryType>Git</RepositoryType>
|
||||||
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
|
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
|
||||||
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
|
|
||||||
<NuspecFile>$(MSBuildProjectName).nuspec</NuspecFile>
|
|
||||||
<PackageName>$(MSBuildProjectName).$(Version).nupkg</PackageName>
|
|
||||||
<SuppressDependenciesWhenPacking>true</SuppressDependenciesWhenPacking>
|
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
|
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
|
||||||
@ -28,6 +24,14 @@
|
|||||||
<OutputPath>bin</OutputPath>
|
<OutputPath>bin</OutputPath>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
||||||
|
<NoWarn>1701;1702;EF1001;AD0001</NoWarn>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
|
||||||
|
<NoWarn>1701;1702;EF1001;AD0001</NoWarn>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="8.0.0" />
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="8.0.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
@ -36,8 +40,12 @@
|
|||||||
<ProjectReference Include="..\Oqtane.Server\Oqtane.Server.csproj" />
|
<ProjectReference Include="..\Oqtane.Server\Oqtane.Server.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<Target Name="CopyPackage" AfterTargets="Pack">
|
<ItemGroup>
|
||||||
<Copy SourceFiles="$(OutputPath)..\$(PackageName)" DestinationFiles="..\Oqtane.Server\wwwroot\Packages\$(MSBuildProjectName).nupkg.bak" />
|
<SqliteFiles Include="$(OutputPath)Oqtane.Database.Sqlite.dll;$(OutputPath)Oqtane.Database.Sqlite.pdb;$(OutputPath)Microsoft.EntityFrameworkCore.Sqlite.dll" DestinationPath="..\Oqtane.Server\bin\$(Configuration)\net8.0\%(Filename)%(Extension)" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<Target Name="PublishProvider" AfterTargets="PostBuildEvent" Inputs="@(SqliteFiles)" Outputs="@(SqliteFiles->'%(DestinationPath)')">
|
||||||
|
<Copy SourceFiles="@(SqliteFiles)" DestinationFiles="@(SqliteFiles->'%(DestinationPath)')" />
|
||||||
</Target>
|
</Target>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
@ -1,25 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
|
|
||||||
<metadata>
|
|
||||||
<id>Oqtane.Database.Sqlite</id>
|
|
||||||
<version>5.0.0</version>
|
|
||||||
<authors>Shaun Walker</authors>
|
|
||||||
<owners>.NET Foundation</owners>
|
|
||||||
<title>Oqtane SQLite Provider</title>
|
|
||||||
<summary>SQLite database support for the Oqtane Framework</summary>
|
|
||||||
<description>SQLite database support for the Oqtane Framework</description>
|
|
||||||
<copyright>.NET Foundation</copyright>
|
|
||||||
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
|
||||||
<license type="expression">MIT</license>
|
|
||||||
<projectUrl>https://github.com/oqtane/oqtane.framework</projectUrl>
|
|
||||||
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v5.0.0</releaseNotes>
|
|
||||||
<icon>icon.png</icon>
|
|
||||||
<tags>oqtane</tags>
|
|
||||||
</metadata>
|
|
||||||
<files>
|
|
||||||
<file src="bin\net8.0\Oqtane.Database.Sqlite.dll" target="lib\net8.0" />
|
|
||||||
<file src="bin\net8.0\Oqtane.Database.Sqlite.pdb" target="lib\net8.0" />
|
|
||||||
<file src="bin\net8.0\Microsoft.EntityFrameworkCore.Sqlite.dll" target="lib\net8.0" />
|
|
||||||
<file src="icon.png" target="" />
|
|
||||||
</files>
|
|
||||||
</package>
|
|
@ -1,11 +1,9 @@
|
|||||||
using System;
|
using System;
|
||||||
using Microsoft.EntityFrameworkCore;
|
|
||||||
using Microsoft.EntityFrameworkCore.Metadata.Builders;
|
using Microsoft.EntityFrameworkCore.Metadata.Builders;
|
||||||
using Microsoft.EntityFrameworkCore.Migrations;
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
using Microsoft.EntityFrameworkCore.Sqlite.Migrations.Internal;
|
using Microsoft.EntityFrameworkCore.Sqlite.Migrations.Internal;
|
||||||
using Oqtane.Migrations.Framework;
|
using Oqtane.Migrations.Framework;
|
||||||
using Oqtane.Models;
|
using Oqtane.Models;
|
||||||
using Oqtane.Shared;
|
|
||||||
|
|
||||||
// ReSharper disable ClassNeverInstantiated.Global
|
// ReSharper disable ClassNeverInstantiated.Global
|
||||||
|
|
||||||
|
@ -6,7 +6,6 @@ using Microsoft.EntityFrameworkCore.Migrations;
|
|||||||
using Microsoft.EntityFrameworkCore.Migrations.Operations;
|
using Microsoft.EntityFrameworkCore.Migrations.Operations;
|
||||||
using Microsoft.EntityFrameworkCore.Migrations.Operations.Builders;
|
using Microsoft.EntityFrameworkCore.Migrations.Operations.Builders;
|
||||||
using Oqtane.Databases;
|
using Oqtane.Databases;
|
||||||
using Oqtane.Shared;
|
|
||||||
|
|
||||||
namespace Oqtane.Database.Sqlite
|
namespace Oqtane.Database.Sqlite
|
||||||
{
|
{
|
||||||
|
Binary file not shown.
Before Width: | Height: | Size: 4.8 KiB |
@ -1,56 +0,0 @@
|
|||||||
|
|
||||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
|
||||||
# Visual Studio Version 16
|
|
||||||
VisualStudioVersion = 16.0.28822.285
|
|
||||||
MinimumVisualStudioVersion = 10.0.40219.1
|
|
||||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{77EECA8C-B58E-469E-B8C5-D543AFC9A654}"
|
|
||||||
ProjectSection(SolutionItems) = preProject
|
|
||||||
.editorconfig = .editorconfig
|
|
||||||
.gitignore = .gitignore
|
|
||||||
README.md = README.md
|
|
||||||
EndProjectSection
|
|
||||||
EndProject
|
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Oqtane.Database.MySQL", "Oqtane.Database.MySQL\Oqtane.Database.MySQL.csproj", "{A996FD2D-DAC8-4DFA-92B2-51DF32C6E014}"
|
|
||||||
EndProject
|
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Oqtane.Database.PostgreSQL", "Oqtane.Database.PostgreSQL\Oqtane.Database.PostgreSQL.csproj", "{3B29B35F-65E7-4819-9AED-EAC7FCFA309B}"
|
|
||||||
EndProject
|
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Oqtane.Database.Sqlite", "Oqtane.Database.Sqlite\Oqtane.Database.Sqlite.csproj", "{E4F50CA9-19A6-465A-9469-C033748AD95B}"
|
|
||||||
EndProject
|
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Oqtane.Database.SqlServer", "Oqtane.Database.SqlServer\Oqtane.Database.SqlServer.csproj", "{033DCA37-6354-4A3D-8250-4EC20740EE19}"
|
|
||||||
EndProject
|
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Oqtane.Server", "Oqtane.Server\Oqtane.Server.csproj", "{6A60C4DD-67E6-42A7-B9AA-A1EE45AD45C7}"
|
|
||||||
EndProject
|
|
||||||
Global
|
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
|
||||||
Debug|Any CPU = Debug|Any CPU
|
|
||||||
Release|Any CPU = Release|Any CPU
|
|
||||||
EndGlobalSection
|
|
||||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
|
||||||
{A996FD2D-DAC8-4DFA-92B2-51DF32C6E014}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
|
||||||
{A996FD2D-DAC8-4DFA-92B2-51DF32C6E014}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
|
||||||
{A996FD2D-DAC8-4DFA-92B2-51DF32C6E014}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
|
||||||
{A996FD2D-DAC8-4DFA-92B2-51DF32C6E014}.Release|Any CPU.Build.0 = Release|Any CPU
|
|
||||||
{3B29B35F-65E7-4819-9AED-EAC7FCFA309B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
|
||||||
{3B29B35F-65E7-4819-9AED-EAC7FCFA309B}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
|
||||||
{3B29B35F-65E7-4819-9AED-EAC7FCFA309B}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
|
||||||
{3B29B35F-65E7-4819-9AED-EAC7FCFA309B}.Release|Any CPU.Build.0 = Release|Any CPU
|
|
||||||
{E4F50CA9-19A6-465A-9469-C033748AD95B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
|
||||||
{E4F50CA9-19A6-465A-9469-C033748AD95B}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
|
||||||
{E4F50CA9-19A6-465A-9469-C033748AD95B}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
|
||||||
{E4F50CA9-19A6-465A-9469-C033748AD95B}.Release|Any CPU.Build.0 = Release|Any CPU
|
|
||||||
{033DCA37-6354-4A3D-8250-4EC20740EE19}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
|
||||||
{033DCA37-6354-4A3D-8250-4EC20740EE19}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
|
||||||
{033DCA37-6354-4A3D-8250-4EC20740EE19}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
|
||||||
{033DCA37-6354-4A3D-8250-4EC20740EE19}.Release|Any CPU.Build.0 = Release|Any CPU
|
|
||||||
{6A60C4DD-67E6-42A7-B9AA-A1EE45AD45C7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
|
||||||
{6A60C4DD-67E6-42A7-B9AA-A1EE45AD45C7}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
|
||||||
{6A60C4DD-67E6-42A7-B9AA-A1EE45AD45C7}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
|
||||||
{6A60C4DD-67E6-42A7-B9AA-A1EE45AD45C7}.Release|Any CPU.Build.0 = Release|Any CPU
|
|
||||||
EndGlobalSection
|
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
|
||||||
HideSolutionNode = FALSE
|
|
||||||
EndGlobalSection
|
|
||||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
|
||||||
SolutionGuid = {1FB11796-35DE-4AED-9A52-17733557FCC4}
|
|
||||||
EndGlobalSection
|
|
||||||
EndGlobal
|
|
@ -6,7 +6,7 @@
|
|||||||
<!-- <TargetFrameworks>net8.0-android;net8.0-ios;net8.0-maccatalyst</TargetFrameworks> -->
|
<!-- <TargetFrameworks>net8.0-android;net8.0-ios;net8.0-maccatalyst</TargetFrameworks> -->
|
||||||
<!-- <TargetFrameworks>$(TargetFrameworks);net8.0-tizen</TargetFrameworks> -->
|
<!-- <TargetFrameworks>$(TargetFrameworks);net8.0-tizen</TargetFrameworks> -->
|
||||||
<OutputType>Exe</OutputType>
|
<OutputType>Exe</OutputType>
|
||||||
<Version>5.0.0</Version>
|
<Version>5.0.1</Version>
|
||||||
<Product>Oqtane</Product>
|
<Product>Oqtane</Product>
|
||||||
<Authors>Shaun Walker</Authors>
|
<Authors>Shaun Walker</Authors>
|
||||||
<Company>.NET Foundation</Company>
|
<Company>.NET Foundation</Company>
|
||||||
@ -14,7 +14,7 @@
|
|||||||
<Copyright>.NET Foundation</Copyright>
|
<Copyright>.NET Foundation</Copyright>
|
||||||
<PackageProjectUrl>https://www.oqtane.org</PackageProjectUrl>
|
<PackageProjectUrl>https://www.oqtane.org</PackageProjectUrl>
|
||||||
<PackageLicenseUrl>https://github.com/oqtane/oqtane.framework/blob/dev/LICENSE</PackageLicenseUrl>
|
<PackageLicenseUrl>https://github.com/oqtane/oqtane.framework/blob/dev/LICENSE</PackageLicenseUrl>
|
||||||
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v5.0.0</PackageReleaseNotes>
|
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v5.0.1</PackageReleaseNotes>
|
||||||
<RepositoryUrl>https://github.com/oqtane/oqtane.framework</RepositoryUrl>
|
<RepositoryUrl>https://github.com/oqtane/oqtane.framework</RepositoryUrl>
|
||||||
<RepositoryType>Git</RepositoryType>
|
<RepositoryType>Git</RepositoryType>
|
||||||
<RootNamespace>Oqtane.Maui</RootNamespace>
|
<RootNamespace>Oqtane.Maui</RootNamespace>
|
||||||
@ -31,7 +31,7 @@
|
|||||||
<ApplicationIdGuid>0E29FC31-1B83-48ED-B6E0-9F3C67B775D4</ApplicationIdGuid>
|
<ApplicationIdGuid>0E29FC31-1B83-48ED-B6E0-9F3C67B775D4</ApplicationIdGuid>
|
||||||
|
|
||||||
<!-- Versions -->
|
<!-- Versions -->
|
||||||
<ApplicationDisplayVersion>5.0.0</ApplicationDisplayVersion>
|
<ApplicationDisplayVersion>5.0.1</ApplicationDisplayVersion>
|
||||||
<ApplicationVersion>1</ApplicationVersion>
|
<ApplicationVersion>1</ApplicationVersion>
|
||||||
|
|
||||||
<SupportedOSPlatformVersion Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'ios'">14.2</SupportedOSPlatformVersion>
|
<SupportedOSPlatformVersion Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'ios'">14.2</SupportedOSPlatformVersion>
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
|
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
|
||||||
<metadata>
|
<metadata>
|
||||||
<id>Oqtane.Client</id>
|
<id>Oqtane.Client</id>
|
||||||
<version>5.0.0</version>
|
<version>5.0.1</version>
|
||||||
<authors>Shaun Walker</authors>
|
<authors>Shaun Walker</authors>
|
||||||
<owners>.NET Foundation</owners>
|
<owners>.NET Foundation</owners>
|
||||||
<title>Oqtane Framework</title>
|
<title>Oqtane Framework</title>
|
||||||
@ -12,7 +12,7 @@
|
|||||||
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
||||||
<license type="expression">MIT</license>
|
<license type="expression">MIT</license>
|
||||||
<projectUrl>https://github.com/oqtane/oqtane.framework</projectUrl>
|
<projectUrl>https://github.com/oqtane/oqtane.framework</projectUrl>
|
||||||
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v5.0.0</releaseNotes>
|
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v5.0.1</releaseNotes>
|
||||||
<icon>icon.png</icon>
|
<icon>icon.png</icon>
|
||||||
<tags>oqtane</tags>
|
<tags>oqtane</tags>
|
||||||
</metadata>
|
</metadata>
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
|
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
|
||||||
<metadata>
|
<metadata>
|
||||||
<id>Oqtane.Framework</id>
|
<id>Oqtane.Framework</id>
|
||||||
<version>5.0.0</version>
|
<version>5.0.1</version>
|
||||||
<authors>Shaun Walker</authors>
|
<authors>Shaun Walker</authors>
|
||||||
<owners>.NET Foundation</owners>
|
<owners>.NET Foundation</owners>
|
||||||
<title>Oqtane Framework</title>
|
<title>Oqtane Framework</title>
|
||||||
@ -11,8 +11,8 @@
|
|||||||
<copyright>.NET Foundation</copyright>
|
<copyright>.NET Foundation</copyright>
|
||||||
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
||||||
<license type="expression">MIT</license>
|
<license type="expression">MIT</license>
|
||||||
<projectUrl>https://github.com/oqtane/oqtane.framework/releases/download/v5.0.0Oqtane.Framework.5.0.0.Upgrade.zip</projectUrl>
|
<projectUrl>https://github.com/oqtane/oqtane.framework/releases/download/v5.0.1/Oqtane.Framework.5.0.1.Upgrade.zip</projectUrl>
|
||||||
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v5.0.0</releaseNotes>
|
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v5.0.1</releaseNotes>
|
||||||
<icon>icon.png</icon>
|
<icon>icon.png</icon>
|
||||||
<tags>oqtane framework</tags>
|
<tags>oqtane framework</tags>
|
||||||
</metadata>
|
</metadata>
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
|
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
|
||||||
<metadata>
|
<metadata>
|
||||||
<id>Oqtane.Server</id>
|
<id>Oqtane.Server</id>
|
||||||
<version>5.0.0</version>
|
<version>5.0.1</version>
|
||||||
<authors>Shaun Walker</authors>
|
<authors>Shaun Walker</authors>
|
||||||
<owners>.NET Foundation</owners>
|
<owners>.NET Foundation</owners>
|
||||||
<title>Oqtane Framework</title>
|
<title>Oqtane Framework</title>
|
||||||
@ -12,7 +12,7 @@
|
|||||||
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
||||||
<license type="expression">MIT</license>
|
<license type="expression">MIT</license>
|
||||||
<projectUrl>https://github.com/oqtane/oqtane.framework</projectUrl>
|
<projectUrl>https://github.com/oqtane/oqtane.framework</projectUrl>
|
||||||
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v5.0.0</releaseNotes>
|
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v5.0.1</releaseNotes>
|
||||||
<icon>icon.png</icon>
|
<icon>icon.png</icon>
|
||||||
<tags>oqtane</tags>
|
<tags>oqtane</tags>
|
||||||
</metadata>
|
</metadata>
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
|
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
|
||||||
<metadata>
|
<metadata>
|
||||||
<id>Oqtane.Shared</id>
|
<id>Oqtane.Shared</id>
|
||||||
<version>5.0.0</version>
|
<version>5.0.1</version>
|
||||||
<authors>Shaun Walker</authors>
|
<authors>Shaun Walker</authors>
|
||||||
<owners>.NET Foundation</owners>
|
<owners>.NET Foundation</owners>
|
||||||
<title>Oqtane Framework</title>
|
<title>Oqtane Framework</title>
|
||||||
@ -12,7 +12,7 @@
|
|||||||
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
||||||
<license type="expression">MIT</license>
|
<license type="expression">MIT</license>
|
||||||
<projectUrl>https://github.com/oqtane/oqtane.framework</projectUrl>
|
<projectUrl>https://github.com/oqtane/oqtane.framework</projectUrl>
|
||||||
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v5.0.0</releaseNotes>
|
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v5.0.1</releaseNotes>
|
||||||
<icon>icon.png</icon>
|
<icon>icon.png</icon>
|
||||||
<tags>oqtane</tags>
|
<tags>oqtane</tags>
|
||||||
</metadata>
|
</metadata>
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
|
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
|
||||||
<metadata>
|
<metadata>
|
||||||
<id>Oqtane.Updater</id>
|
<id>Oqtane.Updater</id>
|
||||||
<version>5.0.0</version>
|
<version>5.0.1</version>
|
||||||
<authors>Shaun Walker</authors>
|
<authors>Shaun Walker</authors>
|
||||||
<owners>.NET Foundation</owners>
|
<owners>.NET Foundation</owners>
|
||||||
<title>Oqtane Framework</title>
|
<title>Oqtane Framework</title>
|
||||||
@ -12,7 +12,7 @@
|
|||||||
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
||||||
<license type="expression">MIT</license>
|
<license type="expression">MIT</license>
|
||||||
<projectUrl>https://github.com/oqtane/oqtane.framework</projectUrl>
|
<projectUrl>https://github.com/oqtane/oqtane.framework</projectUrl>
|
||||||
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v5.0.0</releaseNotes>
|
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v5.0.1</releaseNotes>
|
||||||
<icon>icon.png</icon>
|
<icon>icon.png</icon>
|
||||||
<tags>oqtane</tags>
|
<tags>oqtane</tags>
|
||||||
</metadata>
|
</metadata>
|
||||||
|
@ -1 +1 @@
|
|||||||
Compress-Archive -Path "..\Oqtane.Server\bin\Release\net8.0\publish\*" -DestinationPath "Oqtane.Framework.5.0.0.Install.zip" -Force
|
Compress-Archive -Path "..\Oqtane.Server\bin\Release\net8.0\publish\*" -DestinationPath "Oqtane.Framework.5.0.1.Install.zip" -Force
|
@ -1,8 +1,6 @@
|
|||||||
del "*.nupkg"
|
del "*.nupkg"
|
||||||
del "*.zip"
|
del "*.zip"
|
||||||
dotnet clean -c Release ..\Oqtane.Databases.sln
|
|
||||||
dotnet clean -c Release ..\Oqtane.sln
|
dotnet clean -c Release ..\Oqtane.sln
|
||||||
dotnet build -c Release ..\Oqtane.Databases.sln
|
|
||||||
dotnet build -c Release ..\Oqtane.sln
|
dotnet build -c Release ..\Oqtane.sln
|
||||||
nuget.exe pack Oqtane.Client.nuspec
|
nuget.exe pack Oqtane.Client.nuspec
|
||||||
nuget.exe pack Oqtane.Server.nuspec
|
nuget.exe pack Oqtane.Server.nuspec
|
||||||
|
@ -1 +1 @@
|
|||||||
Compress-Archive -Path "..\Oqtane.Server\bin\Release\net8.0\publish\*" -DestinationPath "Oqtane.Framework.5.0.0.Upgrade.zip" -Force
|
Compress-Archive -Path "..\Oqtane.Server\bin\Release\net8.0\publish\*" -DestinationPath "Oqtane.Framework.5.0.1.Upgrade.zip" -Force
|
@ -76,7 +76,7 @@ namespace Oqtane.Controllers
|
|||||||
[Authorize(Roles = RoleNames.Host)]
|
[Authorize(Roles = RoleNames.Host)]
|
||||||
public Alias Put(int id, [FromBody] Alias alias)
|
public Alias Put(int id, [FromBody] Alias alias)
|
||||||
{
|
{
|
||||||
if (ModelState.IsValid && _aliases.GetAlias(alias.AliasId, false) != null)
|
if (ModelState.IsValid && alias.AliasId == id && _aliases.GetAlias(alias.AliasId, false) != null)
|
||||||
{
|
{
|
||||||
alias = _aliases.UpdateAlias(alias);
|
alias = _aliases.UpdateAlias(alias);
|
||||||
_syncManager.AddSyncEvent(alias.TenantId, EntityNames.Alias, alias.AliasId, SyncEventActions.Update);
|
_syncManager.AddSyncEvent(alias.TenantId, EntityNames.Alias, alias.AliasId, SyncEventActions.Update);
|
||||||
|
@ -35,8 +35,8 @@ namespace Oqtane.Controllers
|
|||||||
private readonly ISyncManager _syncManager;
|
private readonly ISyncManager _syncManager;
|
||||||
private readonly ILogManager _logger;
|
private readonly ILogManager _logger;
|
||||||
private readonly Alias _alias;
|
private readonly Alias _alias;
|
||||||
|
private readonly ISettingRepository _settingRepository;
|
||||||
public FileController(IWebHostEnvironment environment, IFileRepository files, IFolderRepository folders, IUserPermissions userPermissions, ISyncManager syncManager, ILogManager logger, ITenantManager tenantManager)
|
public FileController(IWebHostEnvironment environment, IFileRepository files, IFolderRepository folders, IUserPermissions userPermissions, ISettingRepository settingRepository, ISyncManager syncManager, ILogManager logger, ITenantManager tenantManager)
|
||||||
{
|
{
|
||||||
_environment = environment;
|
_environment = environment;
|
||||||
_files = files;
|
_files = files;
|
||||||
@ -45,6 +45,7 @@ namespace Oqtane.Controllers
|
|||||||
_syncManager = syncManager;
|
_syncManager = syncManager;
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
_alias = tenantManager.GetAlias();
|
_alias = tenantManager.GetAlias();
|
||||||
|
_settingRepository = settingRepository;
|
||||||
}
|
}
|
||||||
|
|
||||||
// GET: api/<controller>?folder=x
|
// GET: api/<controller>?folder=x
|
||||||
@ -207,7 +208,7 @@ namespace Oqtane.Controllers
|
|||||||
public Models.File Put(int id, [FromBody] Models.File file)
|
public Models.File Put(int id, [FromBody] Models.File file)
|
||||||
{
|
{
|
||||||
var File = _files.GetFile(file.FileId, false);
|
var File = _files.GetFile(file.FileId, false);
|
||||||
if (ModelState.IsValid && file.Folder.SiteId == _alias.SiteId && File != null // ensure file exists
|
if (ModelState.IsValid && file.Folder.SiteId == _alias.SiteId && file.FileId == id && File != null // ensure file exists
|
||||||
&& _userPermissions.IsAuthorized(User, file.Folder.SiteId, EntityNames.Folder, File.FolderId, PermissionNames.Edit) // ensure user had edit rights to original folder
|
&& _userPermissions.IsAuthorized(User, file.Folder.SiteId, EntityNames.Folder, File.FolderId, PermissionNames.Edit) // ensure user had edit rights to original folder
|
||||||
&& _userPermissions.IsAuthorized(User, file.Folder.SiteId, EntityNames.Folder, file.FolderId, PermissionNames.Edit)) // ensure user has edit rights to new folder
|
&& _userPermissions.IsAuthorized(User, file.Folder.SiteId, EntityNames.Folder, file.FolderId, PermissionNames.Edit)) // ensure user has edit rights to new folder
|
||||||
{
|
{
|
||||||
@ -287,6 +288,8 @@ namespace Oqtane.Controllers
|
|||||||
folder = _folders.GetFolder(FolderId);
|
folder = _folders.GetFolder(FolderId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var _UploadableFiles = (_settingRepository.GetSetting(EntityNames.Site, _alias.SiteId, "UploadableFiles")?.SettingValue ?? Constants.UploadableFiles) ?? Constants.UploadableFiles;
|
||||||
|
|
||||||
if (folder != null && folder.SiteId == _alias.SiteId && _userPermissions.IsAuthorized(User, PermissionNames.Edit, folder.PermissionList))
|
if (folder != null && folder.SiteId == _alias.SiteId && _userPermissions.IsAuthorized(User, PermissionNames.Edit, folder.PermissionList))
|
||||||
{
|
{
|
||||||
string folderPath = _folders.GetFolderPath(folder);
|
string folderPath = _folders.GetFolderPath(folder);
|
||||||
@ -297,7 +300,7 @@ namespace Oqtane.Controllers
|
|||||||
name = url.Substring(url.LastIndexOf("/", StringComparison.Ordinal) + 1);
|
name = url.Substring(url.LastIndexOf("/", StringComparison.Ordinal) + 1);
|
||||||
}
|
}
|
||||||
// check for allowable file extensions
|
// check for allowable file extensions
|
||||||
if (!Constants.UploadableFiles.Split(',').Contains(Path.GetExtension(name).ToLower().Replace(".", "")))
|
if (!_UploadableFiles.Split(',').Contains(Path.GetExtension(name).ToLower().Replace(".", "")))
|
||||||
{
|
{
|
||||||
_logger.Log(LogLevel.Error, this, LogFunction.Create, "File Could Not Be Downloaded From Url Due To Its File Extension {Url}", url);
|
_logger.Log(LogLevel.Error, this, LogFunction.Create, "File Could Not Be Downloaded From Url Due To Its File Extension {Url}", url);
|
||||||
HttpContext.Response.StatusCode = (int)HttpStatusCode.Conflict;
|
HttpContext.Response.StatusCode = (int)HttpStatusCode.Conflict;
|
||||||
@ -362,6 +365,10 @@ namespace Oqtane.Controllers
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Get the UploadableFiles extensions
|
||||||
|
string uploadfilesSetting = _settingRepository.GetSetting(EntityNames.Site, _alias.SiteId, "UploadableFiles")?.SettingValue;
|
||||||
|
string _UploadableFiles = uploadfilesSetting ?? Constants.UploadableFiles;
|
||||||
|
|
||||||
// ensure filename is valid
|
// ensure filename is valid
|
||||||
string token = ".part_";
|
string token = ".part_";
|
||||||
if (!formfile.FileName.IsPathOrFileValid() || !formfile.FileName.Contains(token))
|
if (!formfile.FileName.IsPathOrFileValid() || !formfile.FileName.Contains(token))
|
||||||
@ -371,7 +378,7 @@ namespace Oqtane.Controllers
|
|||||||
|
|
||||||
// check for allowable file extensions (ignore token)
|
// check for allowable file extensions (ignore token)
|
||||||
var extension = Path.GetExtension(formfile.FileName.Substring(0, formfile.FileName.IndexOf(token))).Replace(".", "");
|
var extension = Path.GetExtension(formfile.FileName.Substring(0, formfile.FileName.IndexOf(token))).Replace(".", "");
|
||||||
if (!Constants.UploadableFiles.Split(',').Contains(extension.ToLower()))
|
if (!_UploadableFiles.Split(',').Contains(extension.ToLower()))
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -604,9 +611,11 @@ namespace Oqtane.Controllers
|
|||||||
public IActionResult GetImage(int id, int width, int height, string mode, string position, string background, string rotate, string recreate)
|
public IActionResult GetImage(int id, int width, int height, string mode, string position, string background, string rotate, string recreate)
|
||||||
{
|
{
|
||||||
var file = _files.GetFile(id);
|
var file = _files.GetFile(id);
|
||||||
|
|
||||||
|
var _ImageFiles = (_settingRepository.GetSetting(EntityNames.Site, _alias.SiteId, "ImageFiles")?.SettingValue ?? Constants.ImageFiles) ?? Constants.ImageFiles;
|
||||||
if (file != null && file.Folder.SiteId == _alias.SiteId && _userPermissions.IsAuthorized(User, PermissionNames.View, file.Folder.PermissionList))
|
if (file != null && file.Folder.SiteId == _alias.SiteId && _userPermissions.IsAuthorized(User, PermissionNames.View, file.Folder.PermissionList))
|
||||||
{
|
{
|
||||||
if (Constants.ImageFiles.Split(',').Contains(file.Extension.ToLower()))
|
if (_ImageFiles.Split(',').Contains(file.Extension.ToLower()))
|
||||||
{
|
{
|
||||||
var filepath = _files.GetFilePath(file);
|
var filepath = _files.GetFilePath(file);
|
||||||
if (System.IO.File.Exists(filepath))
|
if (System.IO.File.Exists(filepath))
|
||||||
@ -658,8 +667,15 @@ namespace Oqtane.Controllers
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized File Access Attempt {FileId}", id);
|
if (file != null)
|
||||||
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
|
{
|
||||||
|
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized File Access Attempt {FileId}", id);
|
||||||
|
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
HttpContext.Response.StatusCode = (int)HttpStatusCode.NotFound;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
string errorPath = Path.Combine(GetFolderPath("wwwroot/images"), "error.png");
|
string errorPath = Path.Combine(GetFolderPath("wwwroot/images"), "error.png");
|
||||||
@ -763,6 +779,7 @@ namespace Oqtane.Controllers
|
|||||||
private Models.File CreateFile(string filename, int folderid, string filepath)
|
private Models.File CreateFile(string filename, int folderid, string filepath)
|
||||||
{
|
{
|
||||||
var file = _files.GetFile(folderid, filename);
|
var file = _files.GetFile(folderid, filename);
|
||||||
|
var _ImageFiles = (_settingRepository.GetSetting(EntityNames.Site, _alias.SiteId, "ImageFiles")?.SettingValue ?? Constants.ImageFiles) ?? Constants.ImageFiles;
|
||||||
|
|
||||||
int size = 0;
|
int size = 0;
|
||||||
var folder = _folders.GetFolder(folderid, false);
|
var folder = _folders.GetFolder(folderid, false);
|
||||||
@ -789,7 +806,7 @@ namespace Oqtane.Controllers
|
|||||||
file.ImageHeight = 0;
|
file.ImageHeight = 0;
|
||||||
file.ImageWidth = 0;
|
file.ImageWidth = 0;
|
||||||
|
|
||||||
if (Constants.ImageFiles.Split(',').Contains(file.Extension.ToLower()))
|
if (_ImageFiles.Split(',').Contains(file.Extension.ToLower()))
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
@ -204,7 +204,7 @@ namespace Oqtane.Controllers
|
|||||||
[Authorize(Roles = RoleNames.Registered)]
|
[Authorize(Roles = RoleNames.Registered)]
|
||||||
public Folder Put(int id, [FromBody] Folder folder)
|
public Folder Put(int id, [FromBody] Folder folder)
|
||||||
{
|
{
|
||||||
if (ModelState.IsValid && folder.SiteId == _alias.SiteId && _folders.GetFolder(folder.FolderId, false) != null && _userPermissions.IsAuthorized(User, folder.SiteId, EntityNames.Folder, folder.FolderId, PermissionNames.Edit))
|
if (ModelState.IsValid && folder.SiteId == _alias.SiteId && folder.FolderId == id && _folders.GetFolder(folder.FolderId, false) != null && _userPermissions.IsAuthorized(User, folder.SiteId, EntityNames.Folder, folder.FolderId, PermissionNames.Edit))
|
||||||
{
|
{
|
||||||
if (folder.IsPathValid())
|
if (folder.IsPathValid())
|
||||||
{
|
{
|
||||||
|
@ -30,11 +30,12 @@ namespace Oqtane.Controllers
|
|||||||
private readonly IMemoryCache _cache;
|
private readonly IMemoryCache _cache;
|
||||||
private readonly IHttpContextAccessor _accessor;
|
private readonly IHttpContextAccessor _accessor;
|
||||||
private readonly IAliasRepository _aliases;
|
private readonly IAliasRepository _aliases;
|
||||||
|
private readonly ISiteRepository _sites;
|
||||||
private readonly ILogger<InstallationController> _filelogger;
|
private readonly ILogger<InstallationController> _filelogger;
|
||||||
private readonly ITenantManager _tenantManager;
|
private readonly ITenantManager _tenantManager;
|
||||||
private readonly IServerStateManager _serverState;
|
private readonly IServerStateManager _serverState;
|
||||||
|
|
||||||
public InstallationController(IConfigManager configManager, IInstallationManager installationManager, IDatabaseManager databaseManager, ILocalizationManager localizationManager, IMemoryCache cache, IHttpContextAccessor accessor, IAliasRepository aliases, ILogger<InstallationController> filelogger, ITenantManager tenantManager, IServerStateManager serverState)
|
public InstallationController(IConfigManager configManager, IInstallationManager installationManager, IDatabaseManager databaseManager, ILocalizationManager localizationManager, IMemoryCache cache, IHttpContextAccessor accessor, IAliasRepository aliases, ISiteRepository sites, ILogger<InstallationController> filelogger, ITenantManager tenantManager, IServerStateManager serverState)
|
||||||
{
|
{
|
||||||
_configManager = configManager;
|
_configManager = configManager;
|
||||||
_installationManager = installationManager;
|
_installationManager = installationManager;
|
||||||
@ -43,6 +44,7 @@ namespace Oqtane.Controllers
|
|||||||
_cache = cache;
|
_cache = cache;
|
||||||
_accessor = accessor;
|
_accessor = accessor;
|
||||||
_aliases = aliases;
|
_aliases = aliases;
|
||||||
|
_sites = sites;
|
||||||
_filelogger = filelogger;
|
_filelogger = filelogger;
|
||||||
_tenantManager = tenantManager;
|
_tenantManager = tenantManager;
|
||||||
_serverState = serverState;
|
_serverState = serverState;
|
||||||
@ -108,6 +110,70 @@ namespace Oqtane.Controllers
|
|||||||
return GetAssemblyList().Select(item => item.HashedName).ToList();
|
return GetAssemblyList().Select(item => item.HashedName).ToList();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private List<ClientAssembly> GetAssemblyList()
|
||||||
|
{
|
||||||
|
var alias = _tenantManager.GetAlias();
|
||||||
|
|
||||||
|
return _cache.GetOrCreate($"assemblieslist:{alias.SiteKey}", entry =>
|
||||||
|
{
|
||||||
|
var assemblyList = new List<ClientAssembly>();
|
||||||
|
|
||||||
|
var site = _sites.GetSite(alias.SiteId);
|
||||||
|
if (site != null && (site.Runtime == "WebAssembly" || site.HybridEnabled))
|
||||||
|
{
|
||||||
|
var binFolder = Path.GetDirectoryName(Assembly.GetEntryAssembly().Location);
|
||||||
|
|
||||||
|
// testmode setting is used for validating that the API is downloading the appropriate assemblies to the client
|
||||||
|
bool hashfilename = true;
|
||||||
|
if (_configManager.GetSetting($"{SettingKeys.TestModeKey}", "false") == "true")
|
||||||
|
{
|
||||||
|
hashfilename = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// get site assemblies which should be downloaded to client
|
||||||
|
var assemblies = _serverState.GetServerState(alias.SiteKey).Assemblies;
|
||||||
|
|
||||||
|
// populate assembly list
|
||||||
|
foreach (var assembly in assemblies)
|
||||||
|
{
|
||||||
|
if (assembly != Constants.ClientId)
|
||||||
|
{
|
||||||
|
var filepath = Path.Combine(binFolder, assembly) + ".dll";
|
||||||
|
if (System.IO.File.Exists(filepath))
|
||||||
|
{
|
||||||
|
assemblyList.Add(new ClientAssembly(Path.Combine(binFolder, assembly + ".dll"), hashfilename));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// insert satellite assemblies at beginning of list
|
||||||
|
foreach (var culture in _localizationManager.GetInstalledCultures())
|
||||||
|
{
|
||||||
|
if (culture != Constants.DefaultCulture)
|
||||||
|
{
|
||||||
|
var assembliesFolderPath = Path.Combine(binFolder, culture);
|
||||||
|
if (Directory.Exists(assembliesFolderPath))
|
||||||
|
{
|
||||||
|
foreach (var assembly in assemblies)
|
||||||
|
{
|
||||||
|
var filepath = Path.Combine(assembliesFolderPath, assembly) + ".resources.dll";
|
||||||
|
if (System.IO.File.Exists(filepath))
|
||||||
|
{
|
||||||
|
assemblyList.Insert(0, new ClientAssembly(Path.Combine(assembliesFolderPath, assembly + ".resources.dll"), hashfilename));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_filelogger.LogError(Utilities.LogMessage(this, $"The Satellite Assembly Folder For {culture} Does Not Exist"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return assemblyList;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// GET api/<controller>/load?list=x,y
|
// GET api/<controller>/load?list=x,y
|
||||||
[HttpGet("load")]
|
[HttpGet("load")]
|
||||||
public IActionResult Load(string list = "*")
|
public IActionResult Load(string list = "*")
|
||||||
@ -115,126 +181,79 @@ namespace Oqtane.Controllers
|
|||||||
return File(GetAssemblies(list), System.Net.Mime.MediaTypeNames.Application.Octet, "oqtane.dll");
|
return File(GetAssemblies(list), System.Net.Mime.MediaTypeNames.Application.Octet, "oqtane.dll");
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<ClientAssembly> GetAssemblyList()
|
|
||||||
{
|
|
||||||
var siteKey = _tenantManager.GetAlias().SiteKey;
|
|
||||||
|
|
||||||
return _cache.GetOrCreate($"assemblieslist:{siteKey}", entry =>
|
|
||||||
{
|
|
||||||
var binFolder = Path.GetDirectoryName(Assembly.GetEntryAssembly().Location);
|
|
||||||
var assemblyList = new List<ClientAssembly>();
|
|
||||||
|
|
||||||
// testmode setting is used for validating that the API is downloading the appropriate assemblies to the client
|
|
||||||
bool hashfilename = true;
|
|
||||||
if (_configManager.GetSetting($"{SettingKeys.TestModeKey}", "false") == "true")
|
|
||||||
{
|
|
||||||
hashfilename = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// get site assemblies which should be downloaded to client
|
|
||||||
var assemblies = _serverState.GetServerState(siteKey).Assemblies;
|
|
||||||
|
|
||||||
// populate assembly list
|
|
||||||
foreach (var assembly in assemblies)
|
|
||||||
{
|
|
||||||
if (assembly != Constants.ClientId)
|
|
||||||
{
|
|
||||||
var filepath = Path.Combine(binFolder, assembly) + ".dll";
|
|
||||||
if (System.IO.File.Exists(filepath))
|
|
||||||
{
|
|
||||||
assemblyList.Add(new ClientAssembly(Path.Combine(binFolder, assembly + ".dll"), hashfilename));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// insert satellite assemblies at beginning of list
|
|
||||||
foreach (var culture in _localizationManager.GetInstalledCultures())
|
|
||||||
{
|
|
||||||
if (culture != Constants.DefaultCulture)
|
|
||||||
{
|
|
||||||
var assembliesFolderPath = Path.Combine(binFolder, culture);
|
|
||||||
if (Directory.Exists(assembliesFolderPath))
|
|
||||||
{
|
|
||||||
foreach (var assembly in assemblies)
|
|
||||||
{
|
|
||||||
var filepath = Path.Combine(assembliesFolderPath, assembly) + ".resources.dll";
|
|
||||||
if (System.IO.File.Exists(filepath))
|
|
||||||
{
|
|
||||||
assemblyList.Insert(0, new ClientAssembly(Path.Combine(assembliesFolderPath, assembly + ".resources.dll"), hashfilename));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_filelogger.LogError(Utilities.LogMessage(this, $"The Satellite Assembly Folder For {culture} Does Not Exist"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return assemblyList;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private byte[] GetAssemblies(string list)
|
private byte[] GetAssemblies(string list)
|
||||||
{
|
{
|
||||||
var siteKey = _tenantManager.GetAlias().SiteKey;
|
var alias = _tenantManager.GetAlias();
|
||||||
|
|
||||||
if (list == "*")
|
if (list == "*")
|
||||||
{
|
{
|
||||||
return _cache.GetOrCreate($"assemblies:{siteKey}", entry =>
|
return _cache.GetOrCreate($"assemblies:{alias.SiteKey}", entry =>
|
||||||
{
|
{
|
||||||
return GetZIP(list);
|
return GetZIP(list, alias);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
return GetZIP(list);
|
return GetZIP(list, alias);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private byte[] GetZIP(string list)
|
private byte[] GetZIP(string list, Alias alias)
|
||||||
{
|
{
|
||||||
var binFolder = Path.GetDirectoryName(Assembly.GetEntryAssembly().Location);
|
var site = _sites.GetSite(alias.SiteId);
|
||||||
|
if (site != null && (site.Runtime == "WebAssembly" || site.HybridEnabled))
|
||||||
// get list of assemblies which should be downloaded to client
|
|
||||||
List<ClientAssembly> assemblies = GetAssemblyList();
|
|
||||||
if (list != "*")
|
|
||||||
{
|
{
|
||||||
var filter = list.Split(',').ToList();
|
var binFolder = Path.GetDirectoryName(Assembly.GetEntryAssembly().Location);
|
||||||
assemblies.RemoveAll(item => !filter.Contains(item.HashedName));
|
|
||||||
}
|
|
||||||
|
|
||||||
// create zip file containing assemblies and debug symbols
|
// get list of assemblies which should be downloaded to client
|
||||||
using (var memoryStream = new MemoryStream())
|
List<ClientAssembly> assemblies = GetAssemblyList();
|
||||||
{
|
if (list != "*")
|
||||||
using (var archive = new ZipArchive(memoryStream, ZipArchiveMode.Create, true))
|
|
||||||
{
|
{
|
||||||
foreach (var assembly in assemblies)
|
var filter = list.Split(',').ToList();
|
||||||
|
assemblies.RemoveAll(item => !filter.Contains(item.HashedName));
|
||||||
|
}
|
||||||
|
|
||||||
|
// create zip file containing assemblies and debug symbols
|
||||||
|
using (var memoryStream = new MemoryStream())
|
||||||
|
{
|
||||||
|
using (var archive = new ZipArchive(memoryStream, ZipArchiveMode.Create, true))
|
||||||
{
|
{
|
||||||
if (Path.GetFileNameWithoutExtension(assembly.FilePath) != Constants.ClientId)
|
foreach (var assembly in assemblies)
|
||||||
{
|
{
|
||||||
if (System.IO.File.Exists(assembly.FilePath))
|
if (Path.GetFileNameWithoutExtension(assembly.FilePath) != Constants.ClientId)
|
||||||
{
|
{
|
||||||
using (var filestream = new FileStream(assembly.FilePath, FileMode.Open, FileAccess.Read))
|
if (System.IO.File.Exists(assembly.FilePath))
|
||||||
using (var entrystream = archive.CreateEntry(assembly.HashedName).Open())
|
|
||||||
{
|
{
|
||||||
filestream.CopyTo(entrystream);
|
using (var filestream = new FileStream(assembly.FilePath, FileMode.Open, FileAccess.Read))
|
||||||
|
using (var entrystream = archive.CreateEntry(assembly.HashedName).Open())
|
||||||
|
{
|
||||||
|
filestream.CopyTo(entrystream);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
var pdb = assembly.FilePath.Replace(".dll", ".pdb");
|
||||||
var pdb = assembly.FilePath.Replace(".dll", ".pdb");
|
if (System.IO.File.Exists(pdb))
|
||||||
if (System.IO.File.Exists(pdb))
|
|
||||||
{
|
|
||||||
using (var filestream = new FileStream(pdb, FileMode.Open, FileAccess.Read))
|
|
||||||
using (var entrystream = archive.CreateEntry(assembly.HashedName.Replace(".dll", ".pdb")).Open())
|
|
||||||
{
|
{
|
||||||
filestream.CopyTo(entrystream);
|
using (var filestream = new FileStream(pdb, FileMode.Open, FileAccess.Read))
|
||||||
|
using (var entrystream = archive.CreateEntry(assembly.HashedName.Replace(".dll", ".pdb")).Open())
|
||||||
|
{
|
||||||
|
filestream.CopyTo(entrystream);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return memoryStream.ToArray();
|
return memoryStream.ToArray();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// return empty zip
|
||||||
|
using (var memoryStream = new MemoryStream())
|
||||||
|
{
|
||||||
|
using (var zip = new ZipArchive(memoryStream, ZipArchiveMode.Create)) {}
|
||||||
|
return memoryStream.ToArray();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -67,7 +67,7 @@ namespace Oqtane.Controllers
|
|||||||
[Authorize(Roles = RoleNames.Host)]
|
[Authorize(Roles = RoleNames.Host)]
|
||||||
public Job Put(int id, [FromBody] Job job)
|
public Job Put(int id, [FromBody] Job job)
|
||||||
{
|
{
|
||||||
if (ModelState.IsValid && _jobs.GetJob(job.JobId, false) != null)
|
if (ModelState.IsValid && job.JobId == id && _jobs.GetJob(job.JobId, false) != null)
|
||||||
{
|
{
|
||||||
job = _jobs.UpdateJob(job);
|
job = _jobs.UpdateJob(job);
|
||||||
_logger.Log(LogLevel.Information, this, LogFunction.Update, "Job Updated {Job}", job);
|
_logger.Log(LogLevel.Information, this, LogFunction.Update, "Job Updated {Job}", job);
|
||||||
|
@ -1,5 +1,8 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.Globalization;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using Microsoft.AspNetCore.Authorization;
|
using Microsoft.AspNetCore.Authorization;
|
||||||
@ -9,9 +12,6 @@ using Oqtane.Infrastructure;
|
|||||||
using Oqtane.Models;
|
using Oqtane.Models;
|
||||||
using Oqtane.Repository;
|
using Oqtane.Repository;
|
||||||
using Oqtane.Shared;
|
using Oqtane.Shared;
|
||||||
using System.Linq;
|
|
||||||
using System.Diagnostics;
|
|
||||||
using System.Globalization;
|
|
||||||
|
|
||||||
namespace Oqtane.Controllers
|
namespace Oqtane.Controllers
|
||||||
{
|
{
|
||||||
@ -102,6 +102,24 @@ namespace Oqtane.Controllers
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[HttpPut]
|
||||||
|
[Authorize(Roles = RoleNames.Admin)]
|
||||||
|
public void Put([FromBody] Language language)
|
||||||
|
{
|
||||||
|
if (ModelState.IsValid && language.SiteId == _alias.SiteId)
|
||||||
|
{
|
||||||
|
_languages.UpdateLanguage(language);
|
||||||
|
_syncManager.AddSyncEvent(_alias.TenantId, EntityNames.Language, language.LanguageId, SyncEventActions.Update);
|
||||||
|
_syncManager.AddSyncEvent(_alias.TenantId, EntityNames.Site, _alias.SiteId, SyncEventActions.Refresh);
|
||||||
|
_logger.Log(LogLevel.Information, this, LogFunction.Create, "Language Updated {Language}", language);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized Language Put Attempt {Language}", language);
|
||||||
|
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
[HttpPost]
|
[HttpPost]
|
||||||
[Authorize(Roles = RoleNames.Admin)]
|
[Authorize(Roles = RoleNames.Admin)]
|
||||||
public Language Post([FromBody] Language language)
|
public Language Post([FromBody] Language language)
|
||||||
|
@ -154,7 +154,7 @@ namespace Oqtane.Controllers
|
|||||||
{
|
{
|
||||||
var _module = _modules.GetModule(module.ModuleId, false);
|
var _module = _modules.GetModule(module.ModuleId, false);
|
||||||
|
|
||||||
if (ModelState.IsValid && module.SiteId == _alias.SiteId && _module != null && _userPermissions.IsAuthorized(User, module.SiteId, EntityNames.Module, module.ModuleId, PermissionNames.Edit))
|
if (ModelState.IsValid && module.SiteId == _alias.SiteId && module.ModuleId == id && _module != null && _userPermissions.IsAuthorized(User, module.SiteId, EntityNames.Module, module.ModuleId, PermissionNames.Edit))
|
||||||
{
|
{
|
||||||
module = _modules.UpdateModule(module);
|
module = _modules.UpdateModule(module);
|
||||||
|
|
||||||
|
@ -167,7 +167,7 @@ namespace Oqtane.Controllers
|
|||||||
[Authorize(Roles = RoleNames.Admin)]
|
[Authorize(Roles = RoleNames.Admin)]
|
||||||
public void Put(int id, [FromBody] ModuleDefinition moduleDefinition)
|
public void Put(int id, [FromBody] ModuleDefinition moduleDefinition)
|
||||||
{
|
{
|
||||||
if (ModelState.IsValid && moduleDefinition.SiteId == _alias.SiteId && _moduleDefinitions.GetModuleDefinition(moduleDefinition.ModuleDefinitionId, moduleDefinition.SiteId) != null)
|
if (ModelState.IsValid && moduleDefinition.SiteId == _alias.SiteId && moduleDefinition.ModuleDefinitionId == id && _moduleDefinitions.GetModuleDefinition(moduleDefinition.ModuleDefinitionId, moduleDefinition.SiteId) != null)
|
||||||
{
|
{
|
||||||
_moduleDefinitions.UpdateModuleDefinition(moduleDefinition);
|
_moduleDefinitions.UpdateModuleDefinition(moduleDefinition);
|
||||||
_syncManager.AddSyncEvent(_alias.TenantId, EntityNames.ModuleDefinition, moduleDefinition.ModuleDefinitionId, SyncEventActions.Update);
|
_syncManager.AddSyncEvent(_alias.TenantId, EntityNames.ModuleDefinition, moduleDefinition.ModuleDefinitionId, SyncEventActions.Update);
|
||||||
|
@ -161,6 +161,12 @@ namespace Oqtane.Controllers
|
|||||||
{
|
{
|
||||||
if (ModelState.IsValid && notification.SiteId == _alias.SiteId && IsAuthorized(notification.FromUserId))
|
if (ModelState.IsValid && notification.SiteId == _alias.SiteId && IsAuthorized(notification.FromUserId))
|
||||||
{
|
{
|
||||||
|
if (!User.IsInRole(RoleNames.Admin))
|
||||||
|
{
|
||||||
|
// content must be HTML encoded for non-admins to prevent HTML injection
|
||||||
|
notification.Subject = WebUtility.HtmlEncode(notification.Subject);
|
||||||
|
notification.Body = WebUtility.HtmlEncode(notification.Body);
|
||||||
|
}
|
||||||
notification = _notifications.AddNotification(notification);
|
notification = _notifications.AddNotification(notification);
|
||||||
_syncManager.AddSyncEvent(_alias.TenantId, EntityNames.Notification, notification.NotificationId, SyncEventActions.Create);
|
_syncManager.AddSyncEvent(_alias.TenantId, EntityNames.Notification, notification.NotificationId, SyncEventActions.Create);
|
||||||
_logger.Log(LogLevel.Information, this, LogFunction.Create, "Notification Added {NotificationId}", notification.NotificationId);
|
_logger.Log(LogLevel.Information, this, LogFunction.Create, "Notification Added {NotificationId}", notification.NotificationId);
|
||||||
@ -179,8 +185,14 @@ namespace Oqtane.Controllers
|
|||||||
[Authorize(Roles = RoleNames.Registered)]
|
[Authorize(Roles = RoleNames.Registered)]
|
||||||
public Notification Put(int id, [FromBody] Notification notification)
|
public Notification Put(int id, [FromBody] Notification notification)
|
||||||
{
|
{
|
||||||
if (ModelState.IsValid && notification.SiteId == _alias.SiteId && _notifications.GetNotification(notification.NotificationId, false) != null && (IsAuthorized(notification.FromUserId) || IsAuthorized(notification.ToUserId)))
|
if (ModelState.IsValid && notification.SiteId == _alias.SiteId && notification.NotificationId == id && _notifications.GetNotification(notification.NotificationId, false) != null && (IsAuthorized(notification.FromUserId) || IsAuthorized(notification.ToUserId)))
|
||||||
{
|
{
|
||||||
|
if (!User.IsInRole(RoleNames.Admin))
|
||||||
|
{
|
||||||
|
// content must be HTML encoded for non-admins to prevent HTML injection
|
||||||
|
notification.Subject = WebUtility.HtmlEncode(notification.Subject);
|
||||||
|
notification.Body = WebUtility.HtmlEncode(notification.Body);
|
||||||
|
}
|
||||||
notification = _notifications.UpdateNotification(notification);
|
notification = _notifications.UpdateNotification(notification);
|
||||||
_syncManager.AddSyncEvent(_alias.TenantId, EntityNames.Notification, notification.NotificationId, SyncEventActions.Update);
|
_syncManager.AddSyncEvent(_alias.TenantId, EntityNames.Notification, notification.NotificationId, SyncEventActions.Update);
|
||||||
_logger.Log(LogLevel.Information, this, LogFunction.Update, "Notification Updated {NotificationId}", notification.NotificationId);
|
_logger.Log(LogLevel.Information, this, LogFunction.Update, "Notification Updated {NotificationId}", notification.NotificationId);
|
||||||
|
@ -269,7 +269,7 @@ namespace Oqtane.Controllers
|
|||||||
// get current page
|
// get current page
|
||||||
var currentPage = _pages.GetPage(page.PageId, false);
|
var currentPage = _pages.GetPage(page.PageId, false);
|
||||||
|
|
||||||
if (ModelState.IsValid && page.SiteId == _alias.SiteId && currentPage != null && _userPermissions.IsAuthorized(User, page.SiteId, EntityNames.Page, page.PageId, PermissionNames.Edit))
|
if (ModelState.IsValid && page.SiteId == _alias.SiteId && page.PageId == id && currentPage != null && _userPermissions.IsAuthorized(User, page.SiteId, EntityNames.Page, page.PageId, PermissionNames.Edit))
|
||||||
{
|
{
|
||||||
// get current page permissions
|
// get current page permissions
|
||||||
var currentPermissions = _permissionRepository.GetPermissions(page.SiteId, EntityNames.Page, page.PageId).ToList();
|
var currentPermissions = _permissionRepository.GetPermissions(page.SiteId, EntityNames.Page, page.PageId).ToList();
|
||||||
|
@ -109,7 +109,7 @@ namespace Oqtane.Controllers
|
|||||||
public PageModule Put(int id, [FromBody] PageModule pageModule)
|
public PageModule Put(int id, [FromBody] PageModule pageModule)
|
||||||
{
|
{
|
||||||
var page = _pages.GetPage(pageModule.PageId);
|
var page = _pages.GetPage(pageModule.PageId);
|
||||||
if (ModelState.IsValid && page != null && page.SiteId == _alias.SiteId && _pageModules.GetPageModule(pageModule.PageModuleId, false) != null && _userPermissions.IsAuthorized(User, page.SiteId, EntityNames.Page, pageModule.PageId, PermissionNames.Edit))
|
if (ModelState.IsValid && page != null && page.SiteId == _alias.SiteId && pageModule.PageModuleId == id && _pageModules.GetPageModule(pageModule.PageModuleId, false) != null && _userPermissions.IsAuthorized(User, page.SiteId, EntityNames.Page, pageModule.PageId, PermissionNames.Edit))
|
||||||
{
|
{
|
||||||
pageModule = _pageModules.UpdatePageModule(pageModule);
|
pageModule = _pageModules.UpdatePageModule(pageModule);
|
||||||
_syncManager.AddSyncEvent(_alias.TenantId, EntityNames.PageModule, pageModule.PageModuleId, SyncEventActions.Update);
|
_syncManager.AddSyncEvent(_alias.TenantId, EntityNames.PageModule, pageModule.PageModuleId, SyncEventActions.Update);
|
||||||
|
@ -94,7 +94,7 @@ namespace Oqtane.Controllers
|
|||||||
[Authorize(Policy = $"{EntityNames.Profile}:{PermissionNames.Write}:{RoleNames.Admin}")]
|
[Authorize(Policy = $"{EntityNames.Profile}:{PermissionNames.Write}:{RoleNames.Admin}")]
|
||||||
public Profile Put(int id, [FromBody] Profile profile)
|
public Profile Put(int id, [FromBody] Profile profile)
|
||||||
{
|
{
|
||||||
if (ModelState.IsValid && profile.SiteId == _alias.SiteId && _profiles.GetProfile(profile.ProfileId, false) != null)
|
if (ModelState.IsValid && profile.SiteId == _alias.SiteId && profile.ProfileId == id && _profiles.GetProfile(profile.ProfileId, false) != null)
|
||||||
{
|
{
|
||||||
profile = _profiles.UpdateProfile(profile);
|
profile = _profiles.UpdateProfile(profile);
|
||||||
_syncManager.AddSyncEvent(_alias.TenantId, EntityNames.Profile, profile.ProfileId, SyncEventActions.Update);
|
_syncManager.AddSyncEvent(_alias.TenantId, EntityNames.Profile, profile.ProfileId, SyncEventActions.Update);
|
||||||
|
@ -98,7 +98,7 @@ namespace Oqtane.Controllers
|
|||||||
[Authorize(Policy = $"{EntityNames.Role}:{PermissionNames.Write}:{RoleNames.Admin}")]
|
[Authorize(Policy = $"{EntityNames.Role}:{PermissionNames.Write}:{RoleNames.Admin}")]
|
||||||
public Role Put(int id, [FromBody] Role role)
|
public Role Put(int id, [FromBody] Role role)
|
||||||
{
|
{
|
||||||
if (ModelState.IsValid && role.SiteId == _alias.SiteId && _roles.GetRole(role.RoleId, false) != null)
|
if (ModelState.IsValid && role.SiteId == _alias.SiteId && role.RoleId == id && _roles.GetRole(role.RoleId, false) != null)
|
||||||
{
|
{
|
||||||
role = _roles.UpdateRole(role);
|
role = _roles.UpdateRole(role);
|
||||||
_syncManager.AddSyncEvent(_alias.TenantId, EntityNames.Role, role.RoleId, SyncEventActions.Update);
|
_syncManager.AddSyncEvent(_alias.TenantId, EntityNames.Role, role.RoleId, SyncEventActions.Update);
|
||||||
|
@ -128,7 +128,7 @@ namespace Oqtane.Controllers
|
|||||||
[HttpPut("{id}")]
|
[HttpPut("{id}")]
|
||||||
public Setting Put(int id, [FromBody] Setting setting)
|
public Setting Put(int id, [FromBody] Setting setting)
|
||||||
{
|
{
|
||||||
if (ModelState.IsValid && IsAuthorized(setting.EntityName, setting.EntityId, PermissionNames.Edit))
|
if (ModelState.IsValid && setting.SettingId == id && IsAuthorized(setting.EntityName, setting.EntityId, PermissionNames.Edit))
|
||||||
{
|
{
|
||||||
setting = _settings.UpdateSetting(setting);
|
setting = _settings.UpdateSetting(setting);
|
||||||
AddSyncEvent(setting.EntityName, setting.SettingId, SyncEventActions.Update);
|
AddSyncEvent(setting.EntityName, setting.SettingId, SyncEventActions.Update);
|
||||||
|
@ -86,6 +86,12 @@ namespace Oqtane.Controllers
|
|||||||
.Where(item => !item.IsPrivate || User.IsInRole(RoleNames.Admin))
|
.Where(item => !item.IsPrivate || User.IsInRole(RoleNames.Admin))
|
||||||
.ToDictionary(setting => setting.SettingName, setting => setting.SettingValue);
|
.ToDictionary(setting => setting.SettingName, setting => setting.SettingValue);
|
||||||
|
|
||||||
|
// populate File Extensions
|
||||||
|
site.ImageFiles = site.Settings.ContainsKey("ImageFiles") && !string.IsNullOrEmpty(site.Settings["ImageFiles"])
|
||||||
|
? site.Settings["ImageFiles"] : Constants.ImageFiles;
|
||||||
|
site.UploadableFiles = site.Settings.ContainsKey("UploadableFiles") && !string.IsNullOrEmpty(site.Settings["UploadableFiles"])
|
||||||
|
? site.Settings["UploadableFiles"] : Constants.UploadableFiles;
|
||||||
|
|
||||||
// pages
|
// pages
|
||||||
List<Setting> settings = _settings.GetSettings(EntityNames.Page).ToList();
|
List<Setting> settings = _settings.GetSettings(EntityNames.Page).ToList();
|
||||||
site.Pages = new List<Page>();
|
site.Pages = new List<Page>();
|
||||||
@ -192,7 +198,7 @@ namespace Oqtane.Controllers
|
|||||||
public Site Put(int id, [FromBody] Site site)
|
public Site Put(int id, [FromBody] Site site)
|
||||||
{
|
{
|
||||||
var current = _sites.GetSite(site.SiteId, false);
|
var current = _sites.GetSite(site.SiteId, false);
|
||||||
if (ModelState.IsValid && site.SiteId == _alias.SiteId && site.TenantId == _alias.TenantId && current != null)
|
if (ModelState.IsValid && site.SiteId == _alias.SiteId && site.TenantId == _alias.TenantId && site.SiteId == id && current != null)
|
||||||
{
|
{
|
||||||
site = _sites.UpdateSite(site);
|
site = _sites.UpdateSite(site);
|
||||||
_syncManager.AddSyncEvent(_alias.TenantId, EntityNames.Site, site.SiteId, SyncEventActions.Update);
|
_syncManager.AddSyncEvent(_alias.TenantId, EntityNames.Site, site.SiteId, SyncEventActions.Update);
|
||||||
|
@ -71,7 +71,7 @@ namespace Oqtane.Controllers
|
|||||||
[Authorize(Roles = RoleNames.Admin)]
|
[Authorize(Roles = RoleNames.Admin)]
|
||||||
public void Put(int id, [FromBody] Theme theme)
|
public void Put(int id, [FromBody] Theme theme)
|
||||||
{
|
{
|
||||||
if (ModelState.IsValid && theme.SiteId == _alias.SiteId && _themes.GetTheme(theme.ThemeId,theme.SiteId) != null)
|
if (ModelState.IsValid && theme.SiteId == _alias.SiteId && theme.ThemeId == id && _themes.GetTheme(theme.ThemeId,theme.SiteId) != null)
|
||||||
{
|
{
|
||||||
_themes.UpdateTheme(theme);
|
_themes.UpdateTheme(theme);
|
||||||
_syncManager.AddSyncEvent(_alias.TenantId, EntityNames.Theme, theme.ThemeId, SyncEventActions.Update);
|
_syncManager.AddSyncEvent(_alias.TenantId, EntityNames.Theme, theme.ThemeId, SyncEventActions.Update);
|
||||||
|
@ -118,7 +118,7 @@ namespace Oqtane.Controllers
|
|||||||
[Authorize(Roles = RoleNames.Admin)]
|
[Authorize(Roles = RoleNames.Admin)]
|
||||||
public UrlMapping Put(int id, [FromBody] UrlMapping urlMapping)
|
public UrlMapping Put(int id, [FromBody] UrlMapping urlMapping)
|
||||||
{
|
{
|
||||||
if (ModelState.IsValid && urlMapping.SiteId == _alias.SiteId && _urlMappings.GetUrlMapping(urlMapping.UrlMappingId, false) != null)
|
if (ModelState.IsValid && urlMapping.SiteId == _alias.SiteId && urlMapping.UrlMappingId == id && _urlMappings.GetUrlMapping(urlMapping.UrlMappingId, false) != null)
|
||||||
{
|
{
|
||||||
urlMapping = _urlMappings.UpdateUrlMapping(urlMapping);
|
urlMapping = _urlMappings.UpdateUrlMapping(urlMapping);
|
||||||
_syncManager.AddSyncEvent(_alias.TenantId, EntityNames.UrlMapping, urlMapping.UrlMappingId, SyncEventActions.Update);
|
_syncManager.AddSyncEvent(_alias.TenantId, EntityNames.UrlMapping, urlMapping.UrlMappingId, SyncEventActions.Update);
|
||||||
|
@ -173,15 +173,16 @@ namespace Oqtane.Controllers
|
|||||||
[Authorize]
|
[Authorize]
|
||||||
public async Task<User> Put(int id, [FromBody] User user)
|
public async Task<User> Put(int id, [FromBody] User user)
|
||||||
{
|
{
|
||||||
if (ModelState.IsValid && user.SiteId == _tenantManager.GetAlias().SiteId && _users.GetUser(user.UserId, false) != null
|
if (ModelState.IsValid && user.SiteId == _tenantManager.GetAlias().SiteId && user.UserId == id && _users.GetUser(user.UserId, false) != null
|
||||||
&& (_userPermissions.IsAuthorized(User, user.SiteId, EntityNames.User, -1, PermissionNames.Write, RoleNames.Admin) || User.Identity.Name == user.Username))
|
&& (_userPermissions.IsAuthorized(User, user.SiteId, EntityNames.User, -1, PermissionNames.Write, RoleNames.Admin) || User.Identity.Name == user.Username))
|
||||||
{
|
{
|
||||||
|
user.EmailConfirmed = User.IsInRole(RoleNames.Admin);
|
||||||
user = await _userManager.UpdateUser(user);
|
user = await _userManager.UpdateUser(user);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
user.Password = ""; // remove sensitive information
|
user.Password = ""; // remove sensitive information
|
||||||
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized User Post Attempt {User}", user);
|
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized User Put Attempt {User}", user);
|
||||||
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
|
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
|
||||||
user = null;
|
user = null;
|
||||||
}
|
}
|
||||||
|
@ -149,7 +149,7 @@ namespace Oqtane.Controllers
|
|||||||
public UserRole Put(int id, [FromBody] UserRole userRole)
|
public UserRole Put(int id, [FromBody] UserRole userRole)
|
||||||
{
|
{
|
||||||
var role = _roles.GetRole(userRole.RoleId);
|
var role = _roles.GetRole(userRole.RoleId);
|
||||||
if (ModelState.IsValid && role != null && SiteValid(role.SiteId) && RoleValid(role.Name) && _userRoles.GetUserRole(userRole.UserRoleId, false) != null)
|
if (ModelState.IsValid && role != null && SiteValid(role.SiteId) && RoleValid(role.Name) && userRole.UserRoleId == id && _userRoles.GetUserRole(userRole.UserRoleId, false) != null)
|
||||||
{
|
{
|
||||||
userRole = _userRoles.UpdateUserRole(userRole);
|
userRole = _userRoles.UpdateUserRole(userRole);
|
||||||
_syncManager.AddSyncEvent(_alias.TenantId, EntityNames.UserRole, userRole.UserRoleId, SyncEventActions.Update);
|
_syncManager.AddSyncEvent(_alias.TenantId, EntityNames.UserRole, userRole.UserRoleId, SyncEventActions.Update);
|
||||||
|
54
Oqtane.Server/Extensions/ClaimsPrincipalExtensions.cs
Normal file
54
Oqtane.Server/Extensions/ClaimsPrincipalExtensions.cs
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
using System.Linq;
|
||||||
|
using System.Security.Claims;
|
||||||
|
|
||||||
|
namespace Oqtane.Extensions
|
||||||
|
{
|
||||||
|
public static class ClaimsPrincipalExtensions
|
||||||
|
{
|
||||||
|
public static string Username(this ClaimsPrincipal claimsPrincipal)
|
||||||
|
{
|
||||||
|
if (claimsPrincipal.HasClaim(item => item.Type == ClaimTypes.Name))
|
||||||
|
{
|
||||||
|
return claimsPrincipal.Claims.FirstOrDefault(item => item.Type == ClaimTypes.Name).Value;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int UserId(this ClaimsPrincipal claimsPrincipal)
|
||||||
|
{
|
||||||
|
if (claimsPrincipal.HasClaim(item => item.Type == ClaimTypes.NameIdentifier))
|
||||||
|
{
|
||||||
|
return int.Parse(claimsPrincipal.Claims.First(item => item.Type == ClaimTypes.NameIdentifier).Value);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string Roles(this ClaimsPrincipal claimsPrincipal)
|
||||||
|
{
|
||||||
|
var roles = "";
|
||||||
|
foreach (var claim in claimsPrincipal.Claims.Where(item => item.Type == ClaimTypes.Role))
|
||||||
|
{
|
||||||
|
roles += ((roles == "") ? "" : ";") + claim.Value;
|
||||||
|
}
|
||||||
|
return roles;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string SiteKey(this ClaimsPrincipal claimsPrincipal)
|
||||||
|
{
|
||||||
|
if (claimsPrincipal.HasClaim(item => item.Type == "sitekey"))
|
||||||
|
{
|
||||||
|
return claimsPrincipal.Claims.FirstOrDefault(item => item.Type == "sitekey").Value;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -124,7 +124,7 @@ namespace Microsoft.Extensions.DependencyInjection
|
|||||||
// note that ConfigureApplicationCookie internally uses an ApplicationScheme of "Identity.Application"
|
// note that ConfigureApplicationCookie internally uses an ApplicationScheme of "Identity.Application"
|
||||||
services.ConfigureApplicationCookie(options =>
|
services.ConfigureApplicationCookie(options =>
|
||||||
{
|
{
|
||||||
options.Cookie.HttpOnly = false;
|
options.Cookie.HttpOnly = true;
|
||||||
options.Cookie.SameSite = SameSiteMode.Strict;
|
options.Cookie.SameSite = SameSiteMode.Strict;
|
||||||
options.Cookie.SecurePolicy = CookieSecurePolicy.SameAsRequest;
|
options.Cookie.SecurePolicy = CookieSecurePolicy.SameAsRequest;
|
||||||
options.Events.OnRedirectToLogin = context =>
|
options.Events.OnRedirectToLogin = context =>
|
||||||
@ -179,7 +179,7 @@ namespace Microsoft.Extensions.DependencyInjection
|
|||||||
options.Lockout.AllowedForNewUsers = false;
|
options.Lockout.AllowedForNewUsers = false;
|
||||||
|
|
||||||
// SignIn settings
|
// SignIn settings
|
||||||
options.SignIn.RequireConfirmedEmail = true;
|
options.SignIn.RequireConfirmedEmail = true;
|
||||||
options.SignIn.RequireConfirmedPhoneNumber = false;
|
options.SignIn.RequireConfirmedPhoneNumber = false;
|
||||||
|
|
||||||
// User settings
|
// User settings
|
||||||
|
@ -50,7 +50,6 @@ namespace Oqtane.Extensions
|
|||||||
options.SaveTokens = false;
|
options.SaveTokens = false;
|
||||||
options.GetClaimsFromUserInfoEndpoint = true;
|
options.GetClaimsFromUserInfoEndpoint = true;
|
||||||
options.CallbackPath = string.IsNullOrEmpty(alias.Path) ? "/signin-" + AuthenticationProviderTypes.OpenIDConnect : "/" + alias.Path + "/signin-" + AuthenticationProviderTypes.OpenIDConnect;
|
options.CallbackPath = string.IsNullOrEmpty(alias.Path) ? "/signin-" + AuthenticationProviderTypes.OpenIDConnect : "/" + alias.Path + "/signin-" + AuthenticationProviderTypes.OpenIDConnect;
|
||||||
options.ResponseType = sitesettings.GetValue("ExternalLogin:AuthResponseType", "code"); // authorization code flow
|
|
||||||
options.ResponseMode = OpenIdConnectResponseMode.FormPost; // recommended as most secure
|
options.ResponseMode = OpenIdConnectResponseMode.FormPost; // recommended as most secure
|
||||||
|
|
||||||
// cookie config is required to avoid Correlation Failed errors
|
// cookie config is required to avoid Correlation Failed errors
|
||||||
@ -62,6 +61,7 @@ namespace Oqtane.Extensions
|
|||||||
options.MetadataAddress = sitesettings.GetValue("ExternalLogin:MetadataUrl", "");
|
options.MetadataAddress = sitesettings.GetValue("ExternalLogin:MetadataUrl", "");
|
||||||
options.ClientId = sitesettings.GetValue("ExternalLogin:ClientId", "");
|
options.ClientId = sitesettings.GetValue("ExternalLogin:ClientId", "");
|
||||||
options.ClientSecret = sitesettings.GetValue("ExternalLogin:ClientSecret", "");
|
options.ClientSecret = sitesettings.GetValue("ExternalLogin:ClientSecret", "");
|
||||||
|
options.ResponseType = sitesettings.GetValue("ExternalLogin:AuthResponseType", "code"); // default is authorization code flow
|
||||||
options.UsePkce = bool.Parse(sitesettings.GetValue("ExternalLogin:PKCE", "false"));
|
options.UsePkce = bool.Parse(sitesettings.GetValue("ExternalLogin:PKCE", "false"));
|
||||||
if (!string.IsNullOrEmpty(sitesettings.GetValue("ExternalLogin:RoleClaimType", "")))
|
if (!string.IsNullOrEmpty(sitesettings.GetValue("ExternalLogin:RoleClaimType", "")))
|
||||||
{
|
{
|
||||||
@ -150,15 +150,16 @@ namespace Oqtane.Extensions
|
|||||||
private static async Task OnCreatingTicket(OAuthCreatingTicketContext context)
|
private static async Task OnCreatingTicket(OAuthCreatingTicketContext context)
|
||||||
{
|
{
|
||||||
// OAuth 2.0
|
// OAuth 2.0
|
||||||
var email = "";
|
|
||||||
var id = "";
|
|
||||||
var claims = "";
|
var claims = "";
|
||||||
|
var id = "";
|
||||||
|
var name = "";
|
||||||
|
var email = "";
|
||||||
|
|
||||||
if (context.Options.UserInformationEndpoint != "")
|
if (context.Options.UserInformationEndpoint != "")
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
// call user information endpoint
|
// call user information endpoint using access token
|
||||||
var request = new HttpRequestMessage(HttpMethod.Get, context.Options.UserInformationEndpoint);
|
var request = new HttpRequestMessage(HttpMethod.Get, context.Options.UserInformationEndpoint);
|
||||||
request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
|
request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
|
||||||
request.Headers.UserAgent.Add(new ProductInfoHeaderValue(Constants.PackageId, Constants.Version));
|
request.Headers.UserAgent.Add(new ProductInfoHeaderValue(Constants.PackageId, Constants.Version));
|
||||||
@ -167,32 +168,49 @@ namespace Oqtane.Extensions
|
|||||||
response.EnsureSuccessStatusCode();
|
response.EnsureSuccessStatusCode();
|
||||||
claims = await response.Content.ReadAsStringAsync();
|
claims = await response.Content.ReadAsStringAsync();
|
||||||
|
|
||||||
// parse json output
|
// get claim types
|
||||||
var idClaimType = context.HttpContext.GetSiteSettings().GetValue("ExternalLogin:IdentifierClaimType", "");
|
var idClaimType = context.HttpContext.GetSiteSettings().GetValue("ExternalLogin:IdentifierClaimType", "");
|
||||||
|
var nameClaimType = context.HttpContext.GetSiteSettings().GetValue("ExternalLogin:NameClaimType", "");
|
||||||
var emailClaimType = context.HttpContext.GetSiteSettings().GetValue("ExternalLogin:EmailClaimType", "");
|
var emailClaimType = context.HttpContext.GetSiteSettings().GetValue("ExternalLogin:EmailClaimType", "");
|
||||||
if (!claims.StartsWith("[") && !claims.EndsWith("]"))
|
|
||||||
|
// some user endpoints can return multiple objects (ie. GitHub) so convert single object to array (if necessary)
|
||||||
|
var jsonclaims = claims;
|
||||||
|
if (!jsonclaims.StartsWith("[") && !jsonclaims.EndsWith("]"))
|
||||||
{
|
{
|
||||||
claims = "[" + claims + "]"; // convert to json array
|
jsonclaims = "[" + jsonclaims + "]";
|
||||||
}
|
}
|
||||||
JsonNode items = JsonNode.Parse(claims)!;
|
|
||||||
|
// parse claim values
|
||||||
|
JsonNode items = JsonNode.Parse(jsonclaims)!;
|
||||||
foreach (var item in items.AsArray())
|
foreach (var item in items.AsArray())
|
||||||
{
|
{
|
||||||
if (item[emailClaimType] != null)
|
name = "";
|
||||||
|
email = "";
|
||||||
|
|
||||||
|
// id claim is required
|
||||||
|
if (!string.IsNullOrEmpty(idClaimType) && item[idClaimType] != null)
|
||||||
{
|
{
|
||||||
if (EmailValid(item[emailClaimType].ToString(), context.HttpContext.GetSiteSettings().GetValue("ExternalLogin:DomainFilter", "")))
|
id = item[idClaimType].ToString();
|
||||||
|
|
||||||
|
// name claim is optional
|
||||||
|
if (!string.IsNullOrEmpty(nameClaimType) && item[nameClaimType] != null)
|
||||||
{
|
{
|
||||||
email = item[emailClaimType].ToString().ToLower();
|
name = item[nameClaimType].ToString();
|
||||||
if (item[idClaimType] != null)
|
}
|
||||||
|
|
||||||
|
// email claim is optional
|
||||||
|
if (!string.IsNullOrEmpty(emailClaimType) && item[emailClaimType] != null)
|
||||||
|
{
|
||||||
|
if (EmailValid(item[emailClaimType].ToString(), context.HttpContext.GetSiteSettings().GetValue("ExternalLogin:DomainFilter", "")))
|
||||||
{
|
{
|
||||||
id = item[idClaimType].ToString();
|
email = item[emailClaimType].ToString().ToLower();
|
||||||
}
|
}
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
if (!string.IsNullOrEmpty(id))
|
||||||
if (string.IsNullOrEmpty(id))
|
{
|
||||||
{
|
break;
|
||||||
id = email;
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
@ -203,7 +221,7 @@ namespace Oqtane.Extensions
|
|||||||
}
|
}
|
||||||
|
|
||||||
// validate user
|
// validate user
|
||||||
var identity = await ValidateUser(email, id, claims, context.HttpContext, context.Principal);
|
var identity = await ValidateUser(id, name, email, claims, context.HttpContext, context.Principal);
|
||||||
if (identity.Label == ExternalLoginStatus.Success)
|
if (identity.Label == ExternalLoginStatus.Success)
|
||||||
{
|
{
|
||||||
identity.AddClaim(new Claim("access_token", context.AccessToken));
|
identity.AddClaim(new Claim("access_token", context.AccessToken));
|
||||||
@ -213,6 +231,14 @@ namespace Oqtane.Extensions
|
|||||||
// pass properties to OnTicketReceived
|
// pass properties to OnTicketReceived
|
||||||
context.Properties.SetParameter("status", identity.Label);
|
context.Properties.SetParameter("status", identity.Label);
|
||||||
context.Properties.SetParameter("redirecturl", context.Properties.RedirectUri);
|
context.Properties.SetParameter("redirecturl", context.Properties.RedirectUri);
|
||||||
|
|
||||||
|
// set cookie expiration
|
||||||
|
string cookieExpStr = context.HttpContext.GetSiteSettings().GetValue("LoginOptions:CookieExpiration", "");
|
||||||
|
if (!string.IsNullOrEmpty(cookieExpStr) && TimeSpan.TryParse(cookieExpStr, out TimeSpan cookieExpTS))
|
||||||
|
{
|
||||||
|
context.Properties.ExpiresUtc = DateTime.Now.Add(cookieExpTS);
|
||||||
|
context.Properties.IsPersistent = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Task OnTicketReceived(TicketReceivedContext context)
|
private static Task OnTicketReceived(TicketReceivedContext context)
|
||||||
@ -231,28 +257,46 @@ namespace Oqtane.Extensions
|
|||||||
private static async Task OnTokenValidated(TokenValidatedContext context)
|
private static async Task OnTokenValidated(TokenValidatedContext context)
|
||||||
{
|
{
|
||||||
// OpenID Connect
|
// OpenID Connect
|
||||||
|
var claims = "";
|
||||||
|
var id = "";
|
||||||
|
var name = "";
|
||||||
|
var email = "";
|
||||||
|
|
||||||
|
// serialize claims
|
||||||
|
foreach (var claim in context.Principal.Claims)
|
||||||
|
{
|
||||||
|
claims += "\"" + claim.Type + "\":\"" + claim.Value + "\",";
|
||||||
|
}
|
||||||
|
claims = "{" + claims.Substring(0, claims.Length - 1) + "}";
|
||||||
|
|
||||||
|
// get claim types
|
||||||
var idClaimType = context.HttpContext.GetSiteSettings().GetValue("ExternalLogin:IdentifierClaimType", "");
|
var idClaimType = context.HttpContext.GetSiteSettings().GetValue("ExternalLogin:IdentifierClaimType", "");
|
||||||
var id = context.Principal.FindFirstValue(idClaimType);
|
var nameClaimType = context.HttpContext.GetSiteSettings().GetValue("ExternalLogin:NameClaimType", "");
|
||||||
var emailClaimType = context.HttpContext.GetSiteSettings().GetValue("ExternalLogin:EmailClaimType", "");
|
var emailClaimType = context.HttpContext.GetSiteSettings().GetValue("ExternalLogin:EmailClaimType", "");
|
||||||
var email = context.Principal.FindFirstValue(emailClaimType);
|
|
||||||
var claims = string.Join(", ", context.Principal.Claims.Select(item => item.Type).ToArray());
|
// parse claim values - id claim is required
|
||||||
|
id = context.Principal.FindFirstValue(idClaimType);
|
||||||
|
|
||||||
|
// name claim is optional
|
||||||
|
if (!string.IsNullOrEmpty(nameClaimType) && context.Principal.FindFirstValue(nameClaimType) != null)
|
||||||
|
{
|
||||||
|
name = context.Principal.FindFirstValue(nameClaimType);
|
||||||
|
}
|
||||||
|
|
||||||
|
// email claim is optional
|
||||||
|
if (!string.IsNullOrEmpty(emailClaimType) && context.Principal.FindFirstValue(emailClaimType) != null)
|
||||||
|
{
|
||||||
|
if (EmailValid(context.Principal.FindFirstValue(emailClaimType), context.HttpContext.GetSiteSettings().GetValue("ExternalLogin:DomainFilter", "")))
|
||||||
|
{
|
||||||
|
email = context.Principal.FindFirstValue(emailClaimType);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// validate user
|
// validate user
|
||||||
var identity = await ValidateUser(email, id, claims, context.HttpContext, context.Principal);
|
var identity = await ValidateUser(id, name, email, claims, context.HttpContext, context.Principal);
|
||||||
if (identity.Label == ExternalLoginStatus.Success)
|
if (identity.Label == ExternalLoginStatus.Success)
|
||||||
{
|
{
|
||||||
// external roles
|
// include access token
|
||||||
if (!string.IsNullOrEmpty(context.HttpContext.GetSiteSettings().GetValue("ExternalLogin:RoleClaimType", "")))
|
|
||||||
{
|
|
||||||
foreach (var claim in context.Principal.Claims.Where(item => item.Type == ClaimTypes.Role))
|
|
||||||
{
|
|
||||||
if (!identity.Claims.Any(item => item.Type == ClaimTypes.Role && item.Value == claim.Value))
|
|
||||||
{
|
|
||||||
identity.AddClaim(new Claim(ClaimTypes.Role, claim.Value));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
identity.AddClaim(new Claim("access_token", context.SecurityToken.RawData));
|
identity.AddClaim(new Claim("access_token", context.SecurityToken.RawData));
|
||||||
context.Principal = new ClaimsPrincipal(identity);
|
context.Principal = new ClaimsPrincipal(identity);
|
||||||
}
|
}
|
||||||
@ -284,12 +328,20 @@ namespace Oqtane.Extensions
|
|||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static async Task<ClaimsIdentity> ValidateUser(string email, string id, string claims, HttpContext httpContext, ClaimsPrincipal claimsPrincipal)
|
private static async Task<ClaimsIdentity> ValidateUser(string id, string name, string email, string claims, HttpContext httpContext, ClaimsPrincipal claimsPrincipal)
|
||||||
{
|
{
|
||||||
var _logger = httpContext.RequestServices.GetRequiredService<ILogManager>();
|
var _logger = httpContext.RequestServices.GetRequiredService<ILogManager>();
|
||||||
ClaimsIdentity identity = new ClaimsIdentity(Constants.AuthenticationScheme);
|
ClaimsIdentity identity = new ClaimsIdentity(Constants.AuthenticationScheme);
|
||||||
// use identity.Label as a temporary location to store validation status information
|
// use identity.Label as a temporary location to store validation status information
|
||||||
|
|
||||||
|
// review claims feature (for testing - external login is disabled)
|
||||||
|
if (bool.Parse(httpContext.GetSiteSettings().GetValue("ExternalLogin:ReviewClaims", "false")))
|
||||||
|
{
|
||||||
|
_logger.Log(LogLevel.Information, "ExternalLogin", Enums.LogFunction.Security, "Provider Returned The Following Claims: {Claims}", claims);
|
||||||
|
identity.Label = ExternalLoginStatus.ReviewClaims;
|
||||||
|
return identity;
|
||||||
|
}
|
||||||
|
|
||||||
var providerType = httpContext.GetSiteSettings().GetValue("ExternalLogin:ProviderType", "");
|
var providerType = httpContext.GetSiteSettings().GetValue("ExternalLogin:ProviderType", "");
|
||||||
var providerName = httpContext.GetSiteSettings().GetValue("ExternalLogin:ProviderName", "");
|
var providerName = httpContext.GetSiteSettings().GetValue("ExternalLogin:ProviderName", "");
|
||||||
var alias = httpContext.GetAlias();
|
var alias = httpContext.GetAlias();
|
||||||
@ -308,136 +360,158 @@ namespace Oqtane.Extensions
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (EmailValid(email, httpContext.GetSiteSettings().GetValue("ExternalLogin:DomainFilter", "")))
|
bool duplicates = false;
|
||||||
|
if (!string.IsNullOrEmpty(email))
|
||||||
{
|
{
|
||||||
bool duplicates = false;
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
identityuser = await _identityUserManager.FindByEmailAsync(email);
|
identityuser = await _identityUserManager.FindByEmailAsync(email);
|
||||||
}
|
}
|
||||||
catch
|
catch // FindByEmailAsync will throw an error if the email matches multiple user accounts
|
||||||
{
|
{
|
||||||
// FindByEmailAsync will throw an error if the email matches multiple user accounts
|
|
||||||
duplicates = true;
|
duplicates = true;
|
||||||
}
|
}
|
||||||
if (identityuser == null)
|
}
|
||||||
|
if (identityuser == null)
|
||||||
|
{
|
||||||
|
if (duplicates)
|
||||||
{
|
{
|
||||||
if (duplicates)
|
identity.Label = ExternalLoginStatus.DuplicateEmail;
|
||||||
|
_logger.Log(LogLevel.Error, "ExternalLogin", Enums.LogFunction.Security, "Multiple Users Exist With Email Address {Email}. Login Denied.", email);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (bool.Parse(httpContext.GetSiteSettings().GetValue("ExternalLogin:CreateUsers", "true")))
|
||||||
{
|
{
|
||||||
identity.Label = ExternalLoginStatus.DuplicateEmail;
|
// user identifiers
|
||||||
_logger.Log(LogLevel.Error, "ExternalLogin", Enums.LogFunction.Security, "Multiple Users Exist With Email Address {Email}. Login Denied.", email);
|
var username = "";
|
||||||
}
|
var emailaddress = "";
|
||||||
else
|
var displayname = "";
|
||||||
{
|
bool emailconfirmed = false;
|
||||||
if (bool.Parse(httpContext.GetSiteSettings().GetValue("ExternalLogin:CreateUsers", "true")))
|
|
||||||
{
|
|
||||||
identityuser = new IdentityUser();
|
|
||||||
identityuser.UserName = email;
|
|
||||||
identityuser.Email = email;
|
|
||||||
identityuser.EmailConfirmed = true;
|
|
||||||
var result = await _identityUserManager.CreateAsync(identityuser, DateTime.UtcNow.ToString("yyyy-MMM-dd-HH-mm-ss", CultureInfo.InvariantCulture));
|
|
||||||
if (result.Succeeded)
|
|
||||||
{
|
|
||||||
user = new User
|
|
||||||
{
|
|
||||||
SiteId = alias.SiteId,
|
|
||||||
Username = email,
|
|
||||||
DisplayName = email,
|
|
||||||
Email = email,
|
|
||||||
LastLoginOn = null,
|
|
||||||
LastIPAddress = ""
|
|
||||||
};
|
|
||||||
user = _users.AddUser(user);
|
|
||||||
|
|
||||||
if (user != null)
|
if (!string.IsNullOrEmpty(email)) // email claim provided
|
||||||
|
{
|
||||||
|
username = email;
|
||||||
|
emailaddress = email;
|
||||||
|
displayname = (!string.IsNullOrEmpty(name)) ? name : email;
|
||||||
|
emailconfirmed = true;
|
||||||
|
}
|
||||||
|
else if (!string.IsNullOrEmpty(name)) // name claim provided
|
||||||
|
{
|
||||||
|
username = name.ToLower().Replace(" ", "") + DateTime.UtcNow.ToString("mmss");
|
||||||
|
emailaddress = ""; // unknown - will need to be requested from user later
|
||||||
|
displayname = name;
|
||||||
|
}
|
||||||
|
else // neither email nor name provided
|
||||||
|
{
|
||||||
|
username = Guid.NewGuid().ToString("N");
|
||||||
|
emailaddress = ""; // unknown - will need to be requested from user later
|
||||||
|
displayname = username;
|
||||||
|
}
|
||||||
|
|
||||||
|
identityuser = new IdentityUser();
|
||||||
|
identityuser.UserName = username;
|
||||||
|
identityuser.Email = emailaddress;
|
||||||
|
identityuser.EmailConfirmed = emailconfirmed;
|
||||||
|
|
||||||
|
// generate password based on random date and punctuation ie. Jan-23-1981+14:43:12!
|
||||||
|
Random rnd = new Random();
|
||||||
|
var date = DateTime.UtcNow.AddDays(-rnd.Next(50 * 365)).AddHours(rnd.Next(0, 24)).AddMinutes(rnd.Next(0, 60)).AddSeconds(rnd.Next(0, 60));
|
||||||
|
var password = date.ToString("MMM-dd-yyyy+HH:mm:ss", CultureInfo.InvariantCulture) + (char)rnd.Next(33, 47);
|
||||||
|
|
||||||
|
var result = await _identityUserManager.CreateAsync(identityuser, password);
|
||||||
|
if (result.Succeeded)
|
||||||
|
{
|
||||||
|
user = new User
|
||||||
|
{
|
||||||
|
SiteId = alias.SiteId,
|
||||||
|
Username = username,
|
||||||
|
DisplayName = displayname,
|
||||||
|
Email = emailaddress,
|
||||||
|
LastLoginOn = null,
|
||||||
|
LastIPAddress = ""
|
||||||
|
};
|
||||||
|
user = _users.AddUser(user);
|
||||||
|
|
||||||
|
if (user != null)
|
||||||
|
{
|
||||||
|
if (!string.IsNullOrEmpty(email))
|
||||||
{
|
{
|
||||||
var _notifications = httpContext.RequestServices.GetRequiredService<INotificationRepository>();
|
var _notifications = httpContext.RequestServices.GetRequiredService<INotificationRepository>();
|
||||||
string url = httpContext.Request.Scheme + "://" + alias.Name;
|
string url = httpContext.Request.Scheme + "://" + alias.Name;
|
||||||
string body = "You Recently Used An External Account To Sign In To Our Site.\n\n" + url + "\n\nThank You!";
|
string body = "You Recently Used An External Account To Sign In To Our Site.\n\n" + url + "\n\nThank You!";
|
||||||
var notification = new Notification(user.SiteId, user, "User Account Notification", body);
|
var notification = new Notification(user.SiteId, user, "User Account Notification", body);
|
||||||
_notifications.AddNotification(notification);
|
_notifications.AddNotification(notification);
|
||||||
|
|
||||||
// add user login
|
|
||||||
await _identityUserManager.AddLoginAsync(identityuser, new UserLoginInfo(providerType + ":" + user.SiteId.ToString(), id, providerName));
|
|
||||||
|
|
||||||
_logger.Log(user.SiteId, LogLevel.Information, "ExternalLogin", Enums.LogFunction.Create, "User Added {User}", user);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
identity.Label = ExternalLoginStatus.UserNotCreated;
|
|
||||||
_logger.Log(alias.SiteId, LogLevel.Error, "ExternalLogin", Enums.LogFunction.Create, "Unable To Add User {Email}", email);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// add user login
|
||||||
|
await _identityUserManager.AddLoginAsync(identityuser, new UserLoginInfo(providerType + ":" + user.SiteId.ToString(), id, providerName));
|
||||||
|
|
||||||
|
_logger.Log(user.SiteId, LogLevel.Information, "ExternalLogin", Enums.LogFunction.Create, "User Added {User}", user);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
identity.Label = ExternalLoginStatus.UserNotCreated;
|
identity.Label = ExternalLoginStatus.UserNotCreated;
|
||||||
_logger.Log(alias.SiteId, LogLevel.Error, "ExternalLogin", Enums.LogFunction.Create, "Unable To Add Identity User {Email} {Error}", email, result.Errors.ToString());
|
_logger.Log(alias.SiteId, LogLevel.Error, "ExternalLogin", Enums.LogFunction.Create, "Unable To Add User {Email}", email);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
identity.Label = ExternalLoginStatus.UserDoesNotExist;
|
identity.Label = ExternalLoginStatus.UserNotCreated;
|
||||||
_logger.Log(LogLevel.Error, "ExternalLogin", Enums.LogFunction.Security, "Creation Of New Users Is Disabled For This Site. User With Email Address {Email} Will First Need To Be Registered On The Site.", email);
|
_logger.Log(alias.SiteId, LogLevel.Error, "ExternalLogin", Enums.LogFunction.Create, "Unable To Add Identity User {Email} {Error}", email, result.Errors.ToString());
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var logins = await _identityUserManager.GetLoginsAsync(identityuser);
|
|
||||||
var login = logins.FirstOrDefault(item => item.LoginProvider == (providerType + ":" + alias.SiteId.ToString()));
|
|
||||||
if (login == null)
|
|
||||||
{
|
|
||||||
if (bool.Parse(httpContext.GetSiteSettings().GetValue("ExternalLogin:VerifyUsers", "true")))
|
|
||||||
{
|
|
||||||
// external login using existing user account - verification required
|
|
||||||
var _notifications = httpContext.RequestServices.GetRequiredService<INotificationRepository>();
|
|
||||||
string token = await _identityUserManager.GenerateEmailConfirmationTokenAsync(identityuser);
|
|
||||||
string url = httpContext.Request.Scheme + "://" + alias.Name;
|
|
||||||
url += $"/login?name={identityuser.UserName}&token={WebUtility.UrlEncode(token)}&key={WebUtility.UrlEncode(id)}";
|
|
||||||
string body = $"You Recently Signed In To Our Site With {providerName} Using The Email Address {email}. ";
|
|
||||||
body += "In Order To Complete The Linkage Of Your User Account Please Click The Link Displayed Below:\n\n" + url + "\n\nThank You!";
|
|
||||||
var notification = new Notification(alias.SiteId, email, email, "External Login Linkage", body);
|
|
||||||
_notifications.AddNotification(notification);
|
|
||||||
|
|
||||||
identity.Label = ExternalLoginStatus.VerificationRequired;
|
|
||||||
_logger.Log(alias.SiteId, LogLevel.Information, "ExternalLogin", Enums.LogFunction.Create, "External Login Linkage Verification For Provider {Provider} Sent To {Email}", providerName, email);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// external login using existing user account - link automatically
|
|
||||||
user = _users.GetUser(identityuser.UserName);
|
|
||||||
user.SiteId = alias.SiteId;
|
|
||||||
|
|
||||||
var _notifications = httpContext.RequestServices.GetRequiredService<INotificationRepository>();
|
|
||||||
string url = httpContext.Request.Scheme + "://" + alias.Name;
|
|
||||||
string body = "You Recently Used An External Account To Sign In To Our Site.\n\n" + url + "\n\nThank You!";
|
|
||||||
var notification = new Notification(user.SiteId, user, "User Account Notification", body);
|
|
||||||
_notifications.AddNotification(notification);
|
|
||||||
|
|
||||||
// add user login
|
|
||||||
await _identityUserManager.AddLoginAsync(identityuser, new UserLoginInfo(providerType + ":" + user.SiteId.ToString(), id, providerName));
|
|
||||||
|
|
||||||
_logger.Log(user.SiteId, LogLevel.Information, "ExternalLogin", Enums.LogFunction.Create, "External Login Linkage Created For User {Username} And Provider {Provider}", user.Username, providerName);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// provider keys do not match
|
identity.Label = ExternalLoginStatus.UserDoesNotExist;
|
||||||
identity.Label = ExternalLoginStatus.ProviderKeyMismatch;
|
_logger.Log(LogLevel.Error, "ExternalLogin", Enums.LogFunction.Security, "Creation Of New Users Is Disabled For This Site. User With Email Address {Email} Will First Need To Be Registered On The Site.", email);
|
||||||
_logger.Log(LogLevel.Error, "ExternalLogin", Enums.LogFunction.Security, "Provider Key Does Not Match For User {Username}. Login Denied.", identityuser.UserName);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else // email invalid
|
else
|
||||||
{
|
{
|
||||||
identity.Label = ExternalLoginStatus.InvalidEmail;
|
var logins = await _identityUserManager.GetLoginsAsync(identityuser);
|
||||||
if (!string.IsNullOrEmpty(email))
|
var login = logins.FirstOrDefault(item => item.LoginProvider == (providerType + ":" + alias.SiteId.ToString()));
|
||||||
|
if (login == null)
|
||||||
{
|
{
|
||||||
_logger.Log(LogLevel.Error, "ExternalLogin", Enums.LogFunction.Security, "The Email Address {Email} Is Invalid Or Does Not Match The Domain Filter Criteria. Login Denied.", email);
|
if (bool.Parse(httpContext.GetSiteSettings().GetValue("ExternalLogin:VerifyUsers", "true")))
|
||||||
|
{
|
||||||
|
// external login using existing user account - verification required
|
||||||
|
var _notifications = httpContext.RequestServices.GetRequiredService<INotificationRepository>();
|
||||||
|
string token = await _identityUserManager.GenerateEmailConfirmationTokenAsync(identityuser);
|
||||||
|
string url = httpContext.Request.Scheme + "://" + alias.Name;
|
||||||
|
url += $"/login?name={identityuser.UserName}&token={WebUtility.UrlEncode(token)}&key={WebUtility.UrlEncode(id)}";
|
||||||
|
string body = $"You Recently Signed In To Our Site With {providerName} Using The Email Address {email}. ";
|
||||||
|
body += "In Order To Complete The Linkage Of Your User Account Please Click The Link Displayed Below:\n\n" + url + "\n\nThank You!";
|
||||||
|
var notification = new Notification(alias.SiteId, email, email, "External Login Linkage", body);
|
||||||
|
_notifications.AddNotification(notification);
|
||||||
|
|
||||||
|
identity.Label = ExternalLoginStatus.VerificationRequired;
|
||||||
|
_logger.Log(alias.SiteId, LogLevel.Information, "ExternalLogin", Enums.LogFunction.Create, "External Login Linkage Verification For Provider {Provider} Sent To {Email}", providerName, email);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// external login using existing user account - link automatically
|
||||||
|
user = _users.GetUser(identityuser.UserName);
|
||||||
|
user.SiteId = alias.SiteId;
|
||||||
|
|
||||||
|
var _notifications = httpContext.RequestServices.GetRequiredService<INotificationRepository>();
|
||||||
|
string url = httpContext.Request.Scheme + "://" + alias.Name;
|
||||||
|
string body = "You Recently Used An External Account To Sign In To Our Site.\n\n" + url + "\n\nThank You!";
|
||||||
|
var notification = new Notification(user.SiteId, user, "User Account Notification", body);
|
||||||
|
_notifications.AddNotification(notification);
|
||||||
|
|
||||||
|
// add user login
|
||||||
|
await _identityUserManager.AddLoginAsync(identityuser, new UserLoginInfo(providerType + ":" + user.SiteId.ToString(), id, providerName));
|
||||||
|
|
||||||
|
_logger.Log(user.SiteId, LogLevel.Information, "ExternalLogin", Enums.LogFunction.Create, "External Login Linkage Created For User {Username} And Provider {Provider}", user.Username, providerName);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
_logger.Log(LogLevel.Error, "ExternalLogin", Enums.LogFunction.Security, "Provider Did Not Return An Email Address To Uniquely Identify The User. The Email Claim Specified Was {EmailCLaimType} And Actual Claim Types Are {Claims}. Login Denied.", httpContext.GetSiteSettings().GetValue("ExternalLogin:EmailClaimType", ""), claims);
|
// provider keys do not match
|
||||||
|
identity.Label = ExternalLoginStatus.ProviderKeyMismatch;
|
||||||
|
_logger.Log(LogLevel.Error, "ExternalLogin", Enums.LogFunction.Security, "Provider Key Does Not Match For User {Username}. Login Denied.", identityuser.UserName);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -455,6 +529,25 @@ namespace Oqtane.Extensions
|
|||||||
user.LastIPAddress = httpContext.Connection.RemoteIpAddress.ToString();
|
user.LastIPAddress = httpContext.Connection.RemoteIpAddress.ToString();
|
||||||
_users.UpdateUser(user);
|
_users.UpdateUser(user);
|
||||||
|
|
||||||
|
// external roles
|
||||||
|
if (!string.IsNullOrEmpty(httpContext.GetSiteSettings().GetValue("ExternalLogin:RoleClaimType", "")))
|
||||||
|
{
|
||||||
|
if (claimsPrincipal.Claims.Any(item => item.Type == ClaimTypes.Role))
|
||||||
|
{
|
||||||
|
foreach (var claim in claimsPrincipal.Claims.Where(item => item.Type == ClaimTypes.Role))
|
||||||
|
{
|
||||||
|
if (!identity.Claims.Any(item => item.Type == ClaimTypes.Role && item.Value == claim.Value))
|
||||||
|
{
|
||||||
|
identity.AddClaim(new Claim(ClaimTypes.Role, claim.Value));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_logger.Log(LogLevel.Error, "ExternalLogin", Enums.LogFunction.Security, "The Role Claim {ClaimType} Does Not Exist. Please Use The Review Claims Feature To View The Claims Returned By Your Provider.", httpContext.GetSiteSettings().GetValue("ExternalLogin:RoleClaimType", ""));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// user profile claims
|
// user profile claims
|
||||||
if (!string.IsNullOrEmpty(httpContext.GetSiteSettings().GetValue("ExternalLogin:ProfileClaimTypes", "")))
|
if (!string.IsNullOrEmpty(httpContext.GetSiteSettings().GetValue("ExternalLogin:ProfileClaimTypes", "")))
|
||||||
{
|
{
|
||||||
@ -493,7 +586,7 @@ namespace Oqtane.Extensions
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
_logger.Log(LogLevel.Error, "ExternalLogin", Enums.LogFunction.Security, "The User Profile Claim {ClaimType} Does Not Exist. The Valid Claims Are {Claims}.", mapping.Split(":")[0], claims);
|
_logger.Log(LogLevel.Error, "ExternalLogin", Enums.LogFunction.Security, "The User Profile Claim {ClaimType} Does Not Exist. Please Use The Review Claims Feature To View The Claims Returned By Your Provider.", mapping.Split(":")[0]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -506,9 +599,10 @@ namespace Oqtane.Extensions
|
|||||||
_logger.Log(LogLevel.Information, "ExternalLogin", Enums.LogFunction.Security, "External User Login Successful For {Username} Using Provider {Provider}", user.Username, providerName);
|
_logger.Log(LogLevel.Information, "ExternalLogin", Enums.LogFunction.Security, "External User Login Successful For {Username} Using Provider {Provider}", user.Username, providerName);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else // id invalid
|
else // claims invalid
|
||||||
{
|
{
|
||||||
_logger.Log(LogLevel.Error, "ExternalLogin", Enums.LogFunction.Security, "Provider Did Not Return An Identifier To Uniquely Identify The User. The Identifier Claim Specified Was {IdentifierCLaimType} And Actual Claim Types Are {Claims}. Login Denied.", httpContext.GetSiteSettings().GetValue("ExternalLogin:IdentifierClaimType", ""), claims);
|
identity.Label = ExternalLoginStatus.MissingClaims;
|
||||||
|
_logger.Log(LogLevel.Error, "ExternalLogin", Enums.LogFunction.Security, "Provider Did Not Return All Of The Claims Types Specified Or Email Address Does Not Saitisfy Domain Filter. The Actual Claims Returned Were {Claims}. Login Was Denied.", claims);
|
||||||
}
|
}
|
||||||
|
|
||||||
return identity;
|
return identity;
|
||||||
|
@ -105,12 +105,6 @@ namespace Oqtane.Infrastructure
|
|||||||
IsNewTenant = false
|
IsNewTenant = false
|
||||||
};
|
};
|
||||||
|
|
||||||
// on upgrade install the associated Nuget package
|
|
||||||
if (!string.IsNullOrEmpty(install.ConnectionString))
|
|
||||||
{
|
|
||||||
InstallDatabase(install);
|
|
||||||
}
|
|
||||||
|
|
||||||
var installation = IsInstalled();
|
var installation = IsInstalled();
|
||||||
if (!installation.Success)
|
if (!installation.Success)
|
||||||
{
|
{
|
||||||
@ -209,57 +203,6 @@ namespace Oqtane.Infrastructure
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Installation InstallDatabase(InstallConfig install)
|
|
||||||
{
|
|
||||||
var result = new Installation {Success = false, Message = string.Empty};
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
bool installPackages = false;
|
|
||||||
|
|
||||||
// iterate database packages in installation folder
|
|
||||||
var packagesFolder = new DirectoryInfo(Path.Combine(_environment.ContentRootPath, Constants.PackagesFolder));
|
|
||||||
foreach (var package in packagesFolder.GetFiles("*.nupkg.bak"))
|
|
||||||
{
|
|
||||||
// determine if package needs to be upgraded or installed
|
|
||||||
bool upgrade = System.IO.File.Exists(package.FullName.Replace(".nupkg.bak",".log"));
|
|
||||||
if (upgrade || package.Name.StartsWith(Utilities.GetAssemblyName(install.DatabaseType)))
|
|
||||||
{
|
|
||||||
var packageName = Path.Combine(package.DirectoryName, package.Name);
|
|
||||||
packageName = packageName.Substring(0, packageName.IndexOf(".bak"));
|
|
||||||
package.MoveTo(packageName, true);
|
|
||||||
installPackages = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (installPackages)
|
|
||||||
{
|
|
||||||
using (var scope = _serviceScopeFactory.CreateScope())
|
|
||||||
{
|
|
||||||
var installationManager = scope.ServiceProvider.GetRequiredService<IInstallationManager>();
|
|
||||||
installationManager.InstallPackages();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// load the installation database type (if necessary)
|
|
||||||
if (Type.GetType(install.DatabaseType) == null)
|
|
||||||
{
|
|
||||||
var assemblyPath = Path.GetDirectoryName(Assembly.GetEntryAssembly()?.Location);
|
|
||||||
var assembliesFolder = new DirectoryInfo(assemblyPath);
|
|
||||||
var assemblyFile = new FileInfo($"{assembliesFolder}/{Utilities.GetAssemblyName(install.DatabaseType)}.dll");
|
|
||||||
AssemblyLoadContext.Default.LoadOqtaneAssembly(assemblyFile);
|
|
||||||
}
|
|
||||||
|
|
||||||
result.Success = true;
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
result.Message = ex.ToString();
|
|
||||||
_filelogger.LogError(Utilities.LogMessage(this, result.Message));
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
private Installation CreateDatabase(InstallConfig install)
|
private Installation CreateDatabase(InstallConfig install)
|
||||||
{
|
{
|
||||||
var result = new Installation { Success = false, Message = string.Empty };
|
var result = new Installation { Success = false, Message = string.Empty };
|
||||||
@ -268,8 +211,6 @@ namespace Oqtane.Infrastructure
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
InstallDatabase(install);
|
|
||||||
|
|
||||||
var databaseType = install.DatabaseType;
|
var databaseType = install.DatabaseType;
|
||||||
|
|
||||||
// get database type
|
// get database type
|
||||||
@ -436,7 +377,7 @@ namespace Oqtane.Infrastructure
|
|||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
result.Message = "An Error Occurred Migrating A Tenant Database. This Is Usually Related To A Tenant Database Not Being In A Supported State. " + ex.ToString();
|
result.Message = "An Error Occurred Migrating The Database For Tenant " + tenant.Name + ". This Is Usually Related To Database Permissions, Connection String Mappings, Or The Database Not Being In A Supported State. " + ex.ToString();
|
||||||
_filelogger.LogError(Utilities.LogMessage(this, result.Message));
|
_filelogger.LogError(Utilities.LogMessage(this, result.Message));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -457,7 +398,7 @@ namespace Oqtane.Infrastructure
|
|||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
result.Message = "An Error Occurred Executing Upgrade Logic. " + ex.ToString();
|
result.Message = "An Error Occurred Executing Upgrade Logic On Tenant " + tenant.Name + ". " + ex.ToString();
|
||||||
_filelogger.LogError(Utilities.LogMessage(this, result.Message));
|
_filelogger.LogError(Utilities.LogMessage(this, result.Message));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -527,7 +468,7 @@ namespace Oqtane.Infrastructure
|
|||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
result.Message = "An Error Occurred Installing " + moduleDefinition.Name + " Version " + versions[i] + " - " + ex.ToString();
|
result.Message = "An Error Occurred Installing " + moduleDefinition.Name + " Version " + versions[i] + " On Tenant " + tenant.Name + " - " + ex.ToString();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -614,6 +555,7 @@ namespace Oqtane.Infrastructure
|
|||||||
SiteTemplateType = install.SiteTemplate,
|
SiteTemplateType = install.SiteTemplate,
|
||||||
Runtime = (!string.IsNullOrEmpty(install.Runtime)) ? install.Runtime : _configManager.GetSection("Runtime").Value,
|
Runtime = (!string.IsNullOrEmpty(install.Runtime)) ? install.Runtime : _configManager.GetSection("Runtime").Value,
|
||||||
RenderMode = (!string.IsNullOrEmpty(install.RenderMode)) ? install.RenderMode : _configManager.GetSection("RenderMode").Value,
|
RenderMode = (!string.IsNullOrEmpty(install.RenderMode)) ? install.RenderMode : _configManager.GetSection("RenderMode").Value,
|
||||||
|
HybridEnabled = false
|
||||||
};
|
};
|
||||||
site = sites.AddSite(site);
|
site = sites.AddSite(site);
|
||||||
|
|
||||||
|
@ -15,10 +15,18 @@ namespace Oqtane.Infrastructure.EventSubscribers
|
|||||||
|
|
||||||
public void EntityChanged(SyncEvent syncEvent)
|
public void EntityChanged(SyncEvent syncEvent)
|
||||||
{
|
{
|
||||||
|
// when site entities change (ie. site, pages, modules, etc...) a site refresh event is raised and the site cache item needs to be refreshed
|
||||||
if (syncEvent.EntityName == EntityNames.Site && syncEvent.Action == SyncEventActions.Refresh)
|
if (syncEvent.EntityName == EntityNames.Site && syncEvent.Action == SyncEventActions.Refresh)
|
||||||
{
|
{
|
||||||
_cache.Remove($"site:{syncEvent.TenantId}:{syncEvent.EntityId}");
|
_cache.Remove($"site:{syncEvent.TenantId}:{syncEvent.EntityId}");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// when a site entity is updated the hosting model may have changed, so the client assemblies cache items need to be refreshed
|
||||||
|
if (syncEvent.EntityName == EntityNames.Site && syncEvent.Action == SyncEventActions.Update)
|
||||||
|
{
|
||||||
|
_cache.Remove($"assemblieslist:{syncEvent.TenantId}:{syncEvent.EntityId}");
|
||||||
|
_cache.Remove($"assemblies:{syncEvent.TenantId}:{syncEvent.EntityId}");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -13,7 +13,6 @@ using System.Xml;
|
|||||||
using Microsoft.AspNetCore.Hosting;
|
using Microsoft.AspNetCore.Hosting;
|
||||||
using Microsoft.Extensions.Hosting;
|
using Microsoft.Extensions.Hosting;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using Oqtane.Controllers;
|
|
||||||
using Oqtane.Shared;
|
using Oqtane.Shared;
|
||||||
// ReSharper disable AssignNullToNotNullAttribute
|
// ReSharper disable AssignNullToNotNullAttribute
|
||||||
|
|
||||||
@ -41,51 +40,25 @@ namespace Oqtane.Infrastructure
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// method must be static as it is called in ConfigureServices during Startup
|
||||||
public static string InstallPackages(string webRootPath, string contentRootPath)
|
public static string InstallPackages(string webRootPath, string contentRootPath)
|
||||||
{
|
{
|
||||||
string errors = "";
|
string errors = "";
|
||||||
string binPath = Path.GetDirectoryName(Assembly.GetEntryAssembly()?.Location);
|
string binPath = Path.GetDirectoryName(Assembly.GetEntryAssembly()?.Location);
|
||||||
|
|
||||||
string sourceFolder = Path.Combine(contentRootPath, "Packages");
|
string sourceFolder = Path.Combine(contentRootPath, Constants.PackagesFolder);
|
||||||
if (!Directory.Exists(sourceFolder))
|
if (!Directory.Exists(sourceFolder))
|
||||||
{
|
{
|
||||||
Directory.CreateDirectory(sourceFolder);
|
Directory.CreateDirectory(sourceFolder);
|
||||||
}
|
}
|
||||||
|
|
||||||
// move packages to secure /Packages folder
|
// read assembly log
|
||||||
foreach (var folderName in "Modules,Themes,Packages".Split(","))
|
var assemblyLogPath = Path.Combine(sourceFolder, "assemblies.log");
|
||||||
{
|
var assemblies = GetAssemblyLog(assemblyLogPath);
|
||||||
string folder = Path.Combine(webRootPath, folderName);
|
|
||||||
if (Directory.Exists(folder))
|
|
||||||
{
|
|
||||||
foreach (var file in Directory.GetFiles(folder, "*.nupkg*"))
|
|
||||||
{
|
|
||||||
var destinationFile = Path.Combine(sourceFolder, Path.GetFileName(file));
|
|
||||||
if (File.Exists(destinationFile))
|
|
||||||
{
|
|
||||||
File.Delete(destinationFile);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (destinationFile.ToLower().EndsWith(".nupkg.bak"))
|
// install Nuget packages in secure Packages folder
|
||||||
{
|
var packages = Directory.GetFiles(sourceFolder, "*.nupkg");
|
||||||
// leave a copy in the current folder as it is distributed with the core framework
|
foreach (string packagename in packages)
|
||||||
File.Copy(file, destinationFile);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// move to destination
|
|
||||||
File.Move(file, destinationFile);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Directory.CreateDirectory(folder);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// iterate through Nuget packages in source folder
|
|
||||||
foreach (string packagename in Directory.GetFiles(sourceFolder, "*.nupkg"))
|
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@ -154,10 +127,29 @@ namespace Oqtane.Infrastructure
|
|||||||
// ContentRootPath sometimes produces inconsistent path casing - so can't use string.Replace()
|
// ContentRootPath sometimes produces inconsistent path casing - so can't use string.Replace()
|
||||||
filename = Regex.Replace(filename, Regex.Escape(contentRootPath), "", RegexOptions.IgnoreCase);
|
filename = Regex.Replace(filename, Regex.Escape(contentRootPath), "", RegexOptions.IgnoreCase);
|
||||||
assets.Add(filename);
|
assets.Add(filename);
|
||||||
if (!manifest && Path.GetExtension(filename) == ".log")
|
|
||||||
|
// packages can include a manifest (rather than relying on the framework to dynamically create one)
|
||||||
|
if (!manifest && filename.EndsWith(name + ".log"))
|
||||||
{
|
{
|
||||||
manifest = true;
|
manifest = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// register assembly
|
||||||
|
if (Path.GetExtension(filename) == ".dll")
|
||||||
|
{
|
||||||
|
// if package version was not installed previously
|
||||||
|
if (!File.Exists(Path.Combine(sourceFolder, name + ".log")))
|
||||||
|
{
|
||||||
|
if (assemblies.ContainsKey(Path.GetFileName(filename)))
|
||||||
|
{
|
||||||
|
assemblies[Path.GetFileName(filename)] += 1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
assemblies.Add(Path.GetFileName(filename), 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -187,6 +179,12 @@ namespace Oqtane.Infrastructure
|
|||||||
File.Delete(packagename);
|
File.Delete(packagename);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (packages.Length != 0)
|
||||||
|
{
|
||||||
|
// save assembly log
|
||||||
|
SetAssemblyLog(assemblyLogPath, assemblies);
|
||||||
|
}
|
||||||
|
|
||||||
return errors;
|
return errors;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -232,6 +230,10 @@ namespace Oqtane.Infrastructure
|
|||||||
{
|
{
|
||||||
if (!string.IsNullOrEmpty(PackageName))
|
if (!string.IsNullOrEmpty(PackageName))
|
||||||
{
|
{
|
||||||
|
// read assembly log
|
||||||
|
var assemblyLogPath = Path.Combine(Path.Combine(_environment.ContentRootPath, Constants.PackagesFolder), "assemblies.log");
|
||||||
|
var assemblies = GetAssemblyLog(assemblyLogPath);
|
||||||
|
|
||||||
// get manifest with highest version
|
// get manifest with highest version
|
||||||
string packagename = "";
|
string packagename = "";
|
||||||
string[] packages = Directory.GetFiles(Path.Combine(_environment.ContentRootPath, Constants.PackagesFolder), PackageName + "*.log");
|
string[] packages = Directory.GetFiles(Path.Combine(_environment.ContentRootPath, Constants.PackagesFolder), PackageName + "*.log");
|
||||||
@ -249,17 +251,31 @@ namespace Oqtane.Infrastructure
|
|||||||
{
|
{
|
||||||
// legacy support for assets that were stored as absolute paths
|
// legacy support for assets that were stored as absolute paths
|
||||||
string filepath = asset.StartsWith("\\") ? Path.Combine(_environment.ContentRootPath, asset.Substring(1)) : asset;
|
string filepath = asset.StartsWith("\\") ? Path.Combine(_environment.ContentRootPath, asset.Substring(1)) : asset;
|
||||||
if (File.Exists(filepath))
|
|
||||||
|
// delete assets
|
||||||
|
if (Path.GetExtension(filepath) == ".dll")
|
||||||
{
|
{
|
||||||
// do not remove licensing assemblies - this is a temporary fix until a more robust dependency management solution is available
|
// use assembly log to determine if assembly is used in other packages
|
||||||
if (!filepath.Contains("Oqtane.Licensing."))
|
if (assemblies.ContainsKey(Path.GetFileName(filepath)))
|
||||||
{
|
{
|
||||||
File.Delete(filepath);
|
if (assemblies[Path.GetFileName(filepath)] == 1)
|
||||||
if (!Directory.EnumerateFiles(Path.GetDirectoryName(filepath)).Any())
|
|
||||||
{
|
{
|
||||||
Directory.Delete(Path.GetDirectoryName(filepath), true);
|
DeleteFile(filepath);
|
||||||
|
assemblies.Remove(Path.GetFileName(filepath));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
assemblies[Path.GetFileName(filepath)] -= 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else // does not exist in assembly log
|
||||||
|
{
|
||||||
|
DeleteFile(filepath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else // not an assembly
|
||||||
|
{
|
||||||
|
DeleteFile(filepath);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -269,6 +285,9 @@ namespace Oqtane.Infrastructure
|
|||||||
File.Delete(asset);
|
File.Delete(asset);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// save assembly log
|
||||||
|
SetAssemblyLog(assemblyLogPath, assemblies);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -276,6 +295,76 @@ namespace Oqtane.Infrastructure
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void DeleteFile(string filepath)
|
||||||
|
{
|
||||||
|
if (File.Exists(filepath))
|
||||||
|
{
|
||||||
|
File.Delete(filepath);
|
||||||
|
if (!Directory.EnumerateFiles(Path.GetDirectoryName(filepath)).Any())
|
||||||
|
{
|
||||||
|
Directory.Delete(Path.GetDirectoryName(filepath), true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public int RegisterAssemblies()
|
||||||
|
{
|
||||||
|
var assemblyLogPath = GetAssemblyLogPath();
|
||||||
|
var binFolder = Path.GetDirectoryName(Assembly.GetEntryAssembly().Location);
|
||||||
|
|
||||||
|
var assemblies = GetAssemblyLog(assemblyLogPath);
|
||||||
|
|
||||||
|
// remove assemblies that no longer exist
|
||||||
|
foreach (var dll in assemblies)
|
||||||
|
{
|
||||||
|
if (!File.Exists(Path.Combine(binFolder, dll.Key)))
|
||||||
|
{
|
||||||
|
assemblies.Remove(dll.Key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// add assemblies which are not registered
|
||||||
|
foreach (var dll in Directory.GetFiles(binFolder, "*.dll"))
|
||||||
|
{
|
||||||
|
if (!assemblies.ContainsKey(Path.GetFileName(dll)))
|
||||||
|
{
|
||||||
|
assemblies.Add(Path.GetFileName(dll), 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SetAssemblyLog(assemblyLogPath, assemblies);
|
||||||
|
|
||||||
|
return assemblies.Count;
|
||||||
|
}
|
||||||
|
|
||||||
|
private string GetAssemblyLogPath()
|
||||||
|
{
|
||||||
|
string packagesFolder = Path.Combine(_environment.ContentRootPath, Constants.PackagesFolder);
|
||||||
|
if (!Directory.Exists(packagesFolder))
|
||||||
|
{
|
||||||
|
Directory.CreateDirectory(packagesFolder);
|
||||||
|
}
|
||||||
|
return Path.Combine(packagesFolder, "assemblies.log");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Dictionary<string, int> GetAssemblyLog(string assemblyLogPath)
|
||||||
|
{
|
||||||
|
Dictionary<string, int> assemblies = new Dictionary<string, int>();
|
||||||
|
if (File.Exists(assemblyLogPath))
|
||||||
|
{
|
||||||
|
assemblies = JsonSerializer.Deserialize<Dictionary<string, int>>(File.ReadAllText(assemblyLogPath));
|
||||||
|
}
|
||||||
|
return assemblies;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void SetAssemblyLog(string assemblyLogPath, Dictionary<string, int> assemblies)
|
||||||
|
{
|
||||||
|
if (File.Exists(assemblyLogPath))
|
||||||
|
{
|
||||||
|
File.Delete(assemblyLogPath);
|
||||||
|
}
|
||||||
|
File.WriteAllText(assemblyLogPath, JsonSerializer.Serialize(assemblies, new JsonSerializerOptions { WriteIndented = true }));
|
||||||
|
}
|
||||||
|
|
||||||
public async Task UpgradeFramework()
|
public async Task UpgradeFramework()
|
||||||
{
|
{
|
||||||
string folder = Path.Combine(_environment.ContentRootPath, Constants.PackagesFolder);
|
string folder = Path.Combine(_environment.ContentRootPath, Constants.PackagesFolder);
|
||||||
|
@ -6,6 +6,7 @@ namespace Oqtane.Infrastructure
|
|||||||
{
|
{
|
||||||
void InstallPackages();
|
void InstallPackages();
|
||||||
bool UninstallPackage(string PackageName);
|
bool UninstallPackage(string PackageName);
|
||||||
|
int RegisterAssemblies();
|
||||||
Task UpgradeFramework();
|
Task UpgradeFramework();
|
||||||
void RestartApplication();
|
void RestartApplication();
|
||||||
}
|
}
|
||||||
|
@ -7,6 +7,7 @@ namespace Oqtane.Infrastructure
|
|||||||
Alias GetAlias();
|
Alias GetAlias();
|
||||||
Tenant GetTenant();
|
Tenant GetTenant();
|
||||||
void SetAlias(Alias alias);
|
void SetAlias(Alias alias);
|
||||||
|
void SetAlias(int tenantId, int siteId);
|
||||||
void SetTenant(int tenantId);
|
void SetTenant(int tenantId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -32,6 +32,7 @@ namespace Oqtane.Infrastructure
|
|||||||
var logRepository = provider.GetRequiredService<ILogRepository>();
|
var logRepository = provider.GetRequiredService<ILogRepository>();
|
||||||
var visitorRepository = provider.GetRequiredService<IVisitorRepository>();
|
var visitorRepository = provider.GetRequiredService<IVisitorRepository>();
|
||||||
var notificationRepository = provider.GetRequiredService<INotificationRepository>();
|
var notificationRepository = provider.GetRequiredService<INotificationRepository>();
|
||||||
|
var installationManager = provider.GetRequiredService<IInstallationManager>();
|
||||||
|
|
||||||
// iterate through sites for current tenant
|
// iterate through sites for current tenant
|
||||||
List<Site> sites = siteRepository.GetSites().ToList();
|
List<Site> sites = siteRepository.GetSites().ToList();
|
||||||
@ -96,6 +97,17 @@ namespace Oqtane.Infrastructure
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// register assemblies
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var assemblies = installationManager.RegisterAssemblies();
|
||||||
|
log += assemblies.ToString() + " Assemblies Registered<br />";
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
log += $"Error Registering Assemblies - {ex.Message}<br />";
|
||||||
|
}
|
||||||
|
|
||||||
return log;
|
return log;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4,6 +4,7 @@ using Microsoft.AspNetCore.Http;
|
|||||||
using Microsoft.Extensions.Caching.Memory;
|
using Microsoft.Extensions.Caching.Memory;
|
||||||
using Oqtane.Repository;
|
using Oqtane.Repository;
|
||||||
using Oqtane.Shared;
|
using Oqtane.Shared;
|
||||||
|
using SixLabors.ImageSharp.Metadata.Profiles.Exif;
|
||||||
|
|
||||||
namespace Oqtane.Infrastructure
|
namespace Oqtane.Infrastructure
|
||||||
{
|
{
|
||||||
@ -22,8 +23,7 @@ namespace Oqtane.Infrastructure
|
|||||||
var config = context.RequestServices.GetService(typeof(IConfigManager)) as IConfigManager;
|
var config = context.RequestServices.GetService(typeof(IConfigManager)) as IConfigManager;
|
||||||
string path = context.Request.Path.ToString();
|
string path = context.Request.Path.ToString();
|
||||||
|
|
||||||
|
if (config.IsInstalled() && !path.StartsWith("/_")) // ignore Blazor framework requests
|
||||||
if (config.IsInstalled() && !path.StartsWith("/_blazor"))
|
|
||||||
{
|
{
|
||||||
// get alias (note that this also sets SiteState.Alias)
|
// get alias (note that this also sets SiteState.Alias)
|
||||||
var tenantManager = context.RequestServices.GetService(typeof(ITenantManager)) as ITenantManager;
|
var tenantManager = context.RequestServices.GetService(typeof(ITenantManager)) as ITenantManager;
|
||||||
@ -57,9 +57,25 @@ namespace Oqtane.Infrastructure
|
|||||||
{
|
{
|
||||||
if (path.StartsWith("/" + alias.Path) && (Constants.ReservedRoutes.Any(item => path.Contains("/" + item + "/"))))
|
if (path.StartsWith("/" + alias.Path) && (Constants.ReservedRoutes.Any(item => path.Contains("/" + item + "/"))))
|
||||||
{
|
{
|
||||||
context.Request.Path = path.Replace("/" + alias.Path, "");
|
context.Request.Path = path.Substring(alias.Path.Length + 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// handle sitemap.xml request
|
||||||
|
if (context.Request.Path.ToString().Contains("/sitemap.xml") && !context.Request.Path.ToString().Contains("/pages"))
|
||||||
|
{
|
||||||
|
context.Request.Path = "/pages/sitemap.xml";
|
||||||
|
}
|
||||||
|
|
||||||
|
// handle robots.txt root request (does not support subfolder aliases)
|
||||||
|
if (context.Request.Path.StartsWithSegments("/robots.txt") && string.IsNullOrEmpty(alias.Path))
|
||||||
|
{
|
||||||
|
// allow all user agents and specify site map
|
||||||
|
var robots = $"User-agent: *\n\nSitemap: {context.Request.Scheme}://{alias.Name}/sitemap.xml";
|
||||||
|
context.Response.ContentType = "text/plain";
|
||||||
|
await context.Response.WriteAsync(robots);
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -26,13 +26,14 @@ namespace Oqtane.Infrastructure
|
|||||||
{
|
{
|
||||||
Alias alias = null;
|
Alias alias = null;
|
||||||
|
|
||||||
if (_siteState?.Alias != null && _siteState.Alias.AliasId != -1)
|
// does not support mock Alias objects (GetTenant should be used to retrieve a TenantId)
|
||||||
|
if (_siteState?.Alias != null && _siteState.Alias.AliasId != -1)
|
||||||
{
|
{
|
||||||
alias = _siteState.Alias;
|
alias = _siteState.Alias;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// if there is http context
|
// if there is HttpContext
|
||||||
var httpcontext = _httpContextAccessor.HttpContext;
|
var httpcontext = _httpContextAccessor.HttpContext;
|
||||||
if (httpcontext != null)
|
if (httpcontext != null)
|
||||||
{
|
{
|
||||||
@ -78,15 +79,19 @@ namespace Oqtane.Infrastructure
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// background processes can set the alias using the SiteState service
|
||||||
public void SetAlias(Alias alias)
|
public void SetAlias(Alias alias)
|
||||||
{
|
{
|
||||||
// background processes can set the alias using the SiteState service
|
|
||||||
_siteState.Alias = alias;
|
_siteState.Alias = alias;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void SetAlias(int tenantId, int siteId)
|
||||||
|
{
|
||||||
|
_siteState.Alias = _aliasRepository.GetAliases().ToList().FirstOrDefault(item => item.TenantId == tenantId && item.SiteId == siteId);
|
||||||
|
}
|
||||||
|
|
||||||
public void SetTenant(int tenantId)
|
public void SetTenant(int tenantId)
|
||||||
{
|
{
|
||||||
// background processes can set the alias using the SiteState service
|
|
||||||
_siteState.Alias = new Alias { TenantId = tenantId, AliasId = -1, SiteId = -1 };
|
_siteState.Alias = new Alias { TenantId = tenantId, AliasId = -1, SiteId = -1 };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -106,7 +106,7 @@ namespace Oqtane.Managers
|
|||||||
{
|
{
|
||||||
if (string.IsNullOrEmpty(user.Password))
|
if (string.IsNullOrEmpty(user.Password))
|
||||||
{
|
{
|
||||||
// create random interal password based on random date and punctuation ie. Jan-23-1981+14:43:12!
|
// generate password based on random date and punctuation ie. Jan-23-1981+14:43:12!
|
||||||
Random rnd = new Random();
|
Random rnd = new Random();
|
||||||
var date = DateTime.UtcNow.AddDays(-rnd.Next(50 * 365)).AddHours(rnd.Next(0, 24)).AddMinutes(rnd.Next(0, 60)).AddSeconds(rnd.Next(0, 60));
|
var date = DateTime.UtcNow.AddDays(-rnd.Next(50 * 365)).AddHours(rnd.Next(0, 24)).AddMinutes(rnd.Next(0, 60)).AddSeconds(rnd.Next(0, 60));
|
||||||
user.Password = date.ToString("MMM-dd-yyyy+HH:mm:ss", CultureInfo.InvariantCulture) + (char)rnd.Next(33, 47);
|
user.Password = date.ToString("MMM-dd-yyyy+HH:mm:ss", CultureInfo.InvariantCulture) + (char)rnd.Next(33, 47);
|
||||||
@ -152,7 +152,7 @@ namespace Oqtane.Managers
|
|||||||
{
|
{
|
||||||
string token = await _identityUserManager.GenerateEmailConfirmationTokenAsync(identityuser);
|
string token = await _identityUserManager.GenerateEmailConfirmationTokenAsync(identityuser);
|
||||||
string url = alias.Protocol + alias.Name + "/login?name=" + user.Username + "&token=" + WebUtility.UrlEncode(token);
|
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!";
|
string body = "Dear " + user.DisplayName + ",\n\nIn Order To Verify The Email Address Associated To 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);
|
var notification = new Notification(user.SiteId, User, "User Account Verification", body);
|
||||||
_notifications.AddNotification(notification);
|
_notifications.AddNotification(notification);
|
||||||
}
|
}
|
||||||
@ -205,8 +205,22 @@ namespace Oqtane.Managers
|
|||||||
if (user.Email != identityuser.Email)
|
if (user.Email != identityuser.Email)
|
||||||
{
|
{
|
||||||
await _identityUserManager.SetEmailAsync(identityuser, user.Email);
|
await _identityUserManager.SetEmailAsync(identityuser, user.Email);
|
||||||
var emailConfirmationToken = await _identityUserManager.GenerateEmailConfirmationTokenAsync(identityuser);
|
|
||||||
await _identityUserManager.ConfirmEmailAsync(identityuser, emailConfirmationToken);
|
// if email address changed and user is not administrator, email verification is required for new email address
|
||||||
|
if (!user.EmailConfirmed)
|
||||||
|
{
|
||||||
|
var alias = _tenantManager.GetAlias();
|
||||||
|
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 Verify The Email Address Associated To 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
|
||||||
|
{
|
||||||
|
var emailConfirmationToken = await _identityUserManager.GenerateEmailConfirmationTokenAsync(identityuser);
|
||||||
|
await _identityUserManager.ConfirmEmailAsync(identityuser, emailConfirmationToken);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
user = _users.UpdateUser(user);
|
user = _users.UpdateUser(user);
|
||||||
@ -308,7 +322,7 @@ namespace Oqtane.Managers
|
|||||||
user = _users.GetUser(identityuser.UserName);
|
user = _users.GetUser(identityuser.UserName);
|
||||||
if (user != null)
|
if (user != null)
|
||||||
{
|
{
|
||||||
if (identityuser.EmailConfirmed)
|
if (await _identityUserManager.IsEmailConfirmedAsync(identityuser))
|
||||||
{
|
{
|
||||||
user.IsAuthenticated = true;
|
user.IsAuthenticated = true;
|
||||||
user.LastLoginOn = DateTime.UtcNow;
|
user.LastLoginOn = DateTime.UtcNow;
|
||||||
@ -323,7 +337,7 @@ namespace Oqtane.Managers
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
_logger.Log(LogLevel.Information, this, LogFunction.Security, "User Not Verified {Username}", user.Username);
|
_logger.Log(LogLevel.Information, this, LogFunction.Security, "User Email Address Not Verified {Username}", user.Username);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,30 @@
|
|||||||
|
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
using Oqtane.Databases.Interfaces;
|
||||||
|
using Oqtane.Migrations.EntityBuilders;
|
||||||
|
using Oqtane.Repository;
|
||||||
|
|
||||||
|
namespace Oqtane.Migrations.Tenant
|
||||||
|
{
|
||||||
|
[DbContext(typeof(TenantDBContext))]
|
||||||
|
[Migration("Tenant.05.00.01.00")]
|
||||||
|
public class AddSiteHybridEnabled : MultiDatabaseMigration
|
||||||
|
{
|
||||||
|
public AddSiteHybridEnabled(IDatabase database) : base(database)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
var siteEntityBuilder = new SiteEntityBuilder(migrationBuilder, ActiveDatabase);
|
||||||
|
siteEntityBuilder.AddBooleanColumn("HybridEnabled", true);
|
||||||
|
siteEntityBuilder.UpdateColumn("HybridEnabled", "0", "bool", ""); // default to false
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
var siteEntityBuilder = new SiteEntityBuilder(migrationBuilder, ActiveDatabase);
|
||||||
|
siteEntityBuilder.DropColumn("HybridEnabled");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user