Merge pull request #1895 from sbwalker/dev

added support for default alias specification, alias auto registration, alias redirect, alias line break delimiters
This commit is contained in:
Shaun Walker
2021-12-22 15:34:49 -05:00
committed by GitHub
11 changed files with 329 additions and 227 deletions

View File

@ -1,5 +1,6 @@
@namespace Oqtane.Modules.Admin.Site @namespace Oqtane.Modules.Admin.Site
@inherits ModuleBase @inherits ModuleBase
@using System.Text.RegularExpressions
@inject NavigationManager NavigationManager @inject NavigationManager NavigationManager
@inject ISiteService SiteService @inject ISiteService SiteService
@inject ITenantService TenantService @inject ITenantService TenantService
@ -22,81 +23,64 @@
</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="alias" HelpText="The aliases for the site. An alias can be a domain name (www.site.com) or a virtual folder (ie. www.site.com/folder). If a site has multiple aliases they should be separated by commas." ResourceKey="Aliases">Aliases: </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">
@if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host)) <FileManager FileId="@_logofileid" Filter="@Constants.ImageFiles" @ref="_logofilemanager" />
{
<textarea id="alias" class="form-control" @bind="@_urls" rows="3" required></textarea>
}
else
{
<textarea id="alias" class="form-control" @bind="@_urls" rows="3" readonly></textarea>
}
</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="isDeleted" HelpText="Is this site deleted?" ResourceKey="IsDeleted">Is Deleted? </Label> <Label Class="col-sm-3" For="favicon" HelpText="Specify a Favicon" ResourceKey="FavoriteIcon">Favicon: </Label>
<div class="col-sm-9"> <div class="col-sm-9">
<select id="isDeleted" class="form-select" @bind="@_isdeleted" required> <FileManager FileId="@_faviconfileid" Filter="ico" @ref="_faviconfilemanager" />
<option value="True">@SharedLocalizer["Yes"]</option> </div>
<option value="False">@SharedLocalizer["No"]</option> </div>
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="defaultTheme" HelpText="Select the sites default theme" ResourceKey="DefaultTheme">Default Theme: </Label>
<div class="col-sm-9">
<select id="defaultTheme" class="form-select" value="@_themetype" @onchange="(e => ThemeChanged(e))" required>
<option value="-">&lt;@Localizer["Theme.Select"]&gt;</option>
@foreach (var theme in _themes)
{
<option value="@theme.TypeName">@theme.Name</option>
}
</select> </select>
</div> </div>
</div> </div>
</div> <div class="row mb-1 align-items-center">
<Section Name="Appearance" Heading="Appearance" ResourceKey="Appearance"> <Label Class="col-sm-3" For="defaultContainer" HelpText="Select the default container for the site" ResourceKey="DefaultContainer">Default Container: </Label>
<div class="container"> <div class="col-sm-9">
<div class="row mb-1 align-items-center"> <select id="defaultContainer" class="form-select" @bind="@_containertype" required>
<Label Class="col-sm-3" For="logo" HelpText="Specify a logo for the site" ResourceKey="Logo">Logo: </Label> <option value="-">&lt;@Localizer["Container.Select"]&gt;</option>
<div class="col-sm-9"> @foreach (var container in _containers)
<FileManager FileId="@_logofileid" Filter="@Constants.ImageFiles" @ref="_logofilemanager" /> {
</div> <option value="@container.TypeName">@container.Name</option>
</div> }
<div class="row mb-1 align-items-center"> </select>
<Label Class="col-sm-3" For="favicon" HelpText="Specify a Favicon" ResourceKey="FavoriteIcon">Favicon: </Label>
<div class="col-sm-9">
<FileManager FileId="@_faviconfileid" Filter="ico" @ref="_faviconfilemanager" />
</div>
</div>
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="defaultTheme" HelpText="Select the sites default theme" ResourceKey="DefaultTheme">Default Theme: </Label>
<div class="col-sm-9">
<select id="defaultTheme" class="form-select" value="@_themetype" @onchange="(e => ThemeChanged(e))" required>
<option value="-">&lt;@Localizer["Theme.Select"]&gt;</option>
@foreach (var theme in _themes)
{
<option value="@theme.TypeName">@theme.Name</option>
}
</select>
</div>
</div>
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="defaultContainer" HelpText="Select the default container for the site" ResourceKey="DefaultContainer">Default Container: </Label>
<div class="col-sm-9">
<select id="defaultContainer" class="form-select" @bind="@_containertype" required>
<option value="-">&lt;@Localizer["Container.Select"]&gt;</option>
@foreach (var container in _containers)
{
<option value="@container.TypeName">@container.Name</option>
}
</select>
</div>
</div>
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="defaultAdminContainer" HelpText="Select the default admin container for the site" ResourceKey="DefaultAdminContainer">Default Admin Container: </Label>
<div class="col-sm-9">
<select id="defaultAdminContainer" class="form-select" @bind="@_admincontainertype" required>
<option value="-">&lt;@Localizer["Container.Select"]&gt;</option>
<option value="@Constants.DefaultAdminContainer">&lt;@Localizer["DefaultAdminContainer"]&gt;</option>
@foreach (var container in _containers)
{
<option value="@container.TypeName">@container.Name</option>
}
</select>
</div>
</div> </div>
</div> </div>
</Section> <div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="defaultAdminContainer" HelpText="Select the default admin container for the site" ResourceKey="DefaultAdminContainer">Default Admin Container: </Label>
<div class="col-sm-9">
<select id="defaultAdminContainer" class="form-select" @bind="@_admincontainertype" required>
<option value="-">&lt;@Localizer["Container.Select"]&gt;</option>
<option value="@Constants.DefaultAdminContainer">&lt;@Localizer["DefaultAdminContainer"]&gt;</option>
@foreach (var container in _containers)
{
<option value="@container.TypeName">@container.Name</option>
}
</select>
</div>
</div>
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="isDeleted" HelpText="Is this site deleted?" ResourceKey="IsDeleted">Deleted? </Label>
<div class="col-sm-9">
<select id="isDeleted" class="form-select" @bind="@_isdeleted" required>
<option value="True">@SharedLocalizer["Yes"]</option>
<option value="False">@SharedLocalizer["No"]</option>
</select>
</div>
</div>
</div>
<Section Name="SMTP" Heading="SMTP Settings" ResourceKey="SMTPSettings"> <Section Name="SMTP" Heading="SMTP Settings" ResourceKey="SMTPSettings">
<div class="container"> <div class="container">
<div class="row mb-1 align-items-center"> <div class="row mb-1 align-items-center">
@ -176,6 +160,27 @@
</Section> </Section>
@if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host)) @if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host))
{ {
<Section Name="Aliases" Heading="Aliases" ResourceKey="Aliases">
<div class="container">
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="alias" HelpText="The aliases for the site. An alias can be a domain name (www.site.com) or a virtual folder (ie. www.site.com/folder). If a site has multiple aliases they should be separated by commas." ResourceKey="Aliases">Aliases: </Label>
<div class="col-sm-9">
<textarea id="alias" class="form-control" @bind="@_urls" rows="3" required></textarea>
</div>
</div>
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="defaultalias" HelpText="The default alias for the site. Requests for non-default aliases will be redirected to the default alias." ResourceKey="DefaultAlias">Default Alias: </Label>
<div class="col-sm-9">
<select id="defaultalias" class="form-select" @bind="@_defaultalias" required>
@foreach (string name in _urls.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries))
{
<option value="@name">@name</option>
}
</select>
</div>
</div>
</div>
</Section>
<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">
@ -238,7 +243,8 @@
private List<ThemeControl> _themes = new List<ThemeControl>(); private List<ThemeControl> _themes = new List<ThemeControl>();
private List<ThemeControl> _containers = new List<ThemeControl>(); private List<ThemeControl> _containers = new List<ThemeControl>();
private string _name = string.Empty; private string _name = string.Empty;
private List<Alias> _aliasList; private List<Alias> _aliases;
private string _defaultalias = string.Empty;
private string _urls = string.Empty; private string _urls = string.Empty;
private string _runtime = ""; private string _runtime = "";
private string _prerender = ""; private string _prerender = "";
@ -288,13 +294,7 @@
if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host)) if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host))
{ {
_aliasList = await AliasService.GetAliasesAsync(); await GetAliases();
foreach (Alias alias in _aliasList.Where(item => item.SiteId == site.SiteId && item.TenantId == site.TenantId).ToList())
{
_urls += alias.Name + ",";
}
_urls = _urls.Substring(0, _urls.Length - 1);
} }
if (site.LogoFileId != null) if (site.LogoFileId != null)
@ -395,14 +395,16 @@
{ {
if (_name != string.Empty && _urls != string.Empty && _themetype != "-" && _containertype != "-") if (_name != string.Empty && _urls != string.Empty && _themetype != "-" && _containertype != "-")
{ {
_urls = Regex.Replace(_urls, @"\r\n?|\n", ","); // convert line breaks to commas
var unique = true; var unique = true;
if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host)) if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host))
{ {
foreach (string name in _urls.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries)) foreach (string name in _urls.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries).Select(sValue => sValue.Trim()).ToArray())
{ {
if (_aliasList.Exists(item => item.Name == name && item.SiteId != PageState.Alias.SiteId && item.TenantId != PageState.Alias.TenantId)) var alias = _aliases.Where(item => item.Name == name).FirstOrDefault();
if (alias != null && unique)
{ {
unique = false; unique = (alias.TenantId == PageState.Site.TenantId && alias.SiteId == PageState.Site.SiteId);
} }
} }
} }
@ -461,145 +463,173 @@
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
} }
site = await SiteService.UpdateSiteAsync(site); site = await SiteService.UpdateSiteAsync(site);
var settings = await SettingService.GetSiteSettingsAsync(site.SiteId); var settings = await SettingService.GetSiteSettingsAsync(site.SiteId);
SettingService.SetSetting(settings, "SMTPHost", _smtphost, false); SettingService.SetSetting(settings, "SMTPHost", _smtphost, false);
SettingService.SetSetting(settings, "SMTPPort", _smtpport, false); SettingService.SetSetting(settings, "SMTPPort", _smtpport, false);
SettingService.SetSetting(settings, "SMTPSSL", _smtpssl, false); SettingService.SetSetting(settings, "SMTPSSL", _smtpssl, false);
SettingService.SetSetting(settings, "SMTPUsername", _smtpusername, false); SettingService.SetSetting(settings, "SMTPUsername", _smtpusername, false);
SettingService.SetSetting(settings, "SMTPPassword", _smtppassword, false); SettingService.SetSetting(settings, "SMTPPassword", _smtppassword, false);
SettingService.SetSetting(settings, "SMTPSender", _smtpsender, false); SettingService.SetSetting(settings, "SMTPSender", _smtpsender, false);
await SettingService.UpdateSiteSettingsAsync(settings, site.SiteId); await SettingService.UpdateSiteSettingsAsync(settings, site.SiteId);
if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host)) if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host))
{ {
var names = _urls.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries); var names = _urls.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries)
foreach (Alias alias in _aliasList.Where(item => item.SiteId == site.SiteId && item.TenantId == site.TenantId).ToList()) .Select(sValue => sValue.Trim()).ToArray();
{ foreach (Alias alias in _aliases.Where(item => item.SiteId == site.SiteId && item.TenantId == site.TenantId).ToList())
if (!names.Contains(alias.Name)) {
{ if (!names.Contains(alias.Name))
await AliasService.DeleteAliasAsync(alias.AliasId); {
} await AliasService.DeleteAliasAsync(alias.AliasId);
} }
}
if (!names.Contains(_defaultalias)) { _defaultalias = names[0]; }
foreach (string name in names) foreach (string name in names)
{ {
if (!_aliasList.Exists(item => item.Name == name)) var alias = _aliases.Find(item => item.Name == name);
{ if (alias == null)
Alias alias = new Alias(); {
alias.Name = name; alias = new Alias();
alias.TenantId = site.TenantId; alias.Name = name;
alias.SiteId = site.SiteId; alias.TenantId = site.TenantId;
await AliasService.AddAliasAsync(alias); alias.SiteId = site.SiteId;
} alias.IsDefault = (name == _defaultalias);
} await AliasService.AddAliasAsync(alias);
} }
else
{
if (alias.IsDefault != (alias.Name == _defaultalias))
{
alias.IsDefault = (name == _defaultalias);
await AliasService.UpdateAliasAsync(alias);
}
}
}
await GetAliases();
}
await logger.LogInformation("Site Settings Saved {Site}", site); await logger.LogInformation("Site Settings Saved {Site}", site);
if (refresh) if (refresh)
{ {
NavigationManager.NavigateTo(NavigateUrl(true), reload); // refresh/reload NavigationManager.NavigateTo(NavigateUrl(true), reload); // refresh/reload
} }
else else
{ {
AddModuleMessage(Localizer["Success.Settings.SaveSite"], MessageType.Success); AddModuleMessage(Localizer["Success.Settings.SaveSite"], MessageType.Success);
} }
} }
} }
else else
{ {
AddModuleMessage(Localizer["Message.Aliases.Taken"], MessageType.Warning); AddModuleMessage(Localizer["Message.Aliases.Taken"], MessageType.Warning);
} }
} }
else else
{ {
AddModuleMessage(Localizer["Message.Required.SiteName"], MessageType.Warning); AddModuleMessage(Localizer["Message.Required.SiteName"], MessageType.Warning);
} }
} }
catch (Exception ex) catch (Exception ex)
{ {
await logger.LogError(ex, "Error Saving Site {SiteId} {Error}", PageState.Site.SiteId, ex.Message); await logger.LogError(ex, "Error Saving Site {SiteId} {Error}", PageState.Site.SiteId, ex.Message);
AddModuleMessage(Localizer["Error.SaveSite"], MessageType.Error); AddModuleMessage(Localizer["Error.SaveSite"], MessageType.Error);
} }
} }
else else
{ {
AddModuleMessage(SharedLocalizer["Message.InfoRequired"], MessageType.Warning); AddModuleMessage(SharedLocalizer["Message.InfoRequired"], MessageType.Warning);
} }
} }
private async Task DeleteSite() private async Task DeleteSite()
{ {
try try
{ {
var sites = await SiteService.GetSitesAsync(); var sites = await SiteService.GetSitesAsync();
if (sites.Count > 1) if (sites.Count > 1)
{ {
await SiteService.DeleteSiteAsync(PageState.Site.SiteId); await SiteService.DeleteSiteAsync(PageState.Site.SiteId);
await logger.LogInformation("Site Deleted {SiteId}", PageState.Site.SiteId); await logger.LogInformation("Site Deleted {SiteId}", PageState.Site.SiteId);
var aliases = await AliasService.GetAliasesAsync(); var aliases = await AliasService.GetAliasesAsync();
foreach (Alias a in aliases.Where(item => item.SiteId == PageState.Site.SiteId && item.TenantId == PageState.Site.TenantId)) foreach (Alias a in aliases.Where(item => item.SiteId == PageState.Site.SiteId && item.TenantId == PageState.Site.TenantId))
{ {
await AliasService.DeleteAliasAsync(a.AliasId); await AliasService.DeleteAliasAsync(a.AliasId);
} }
NavigationManager.NavigateTo(NavigateUrl("admin/sites")); NavigationManager.NavigateTo(NavigateUrl("admin/sites"));
} }
else else
{ {
AddModuleMessage(Localizer["Message.FailAuth.DeleteSite"], MessageType.Warning); AddModuleMessage(Localizer["Message.FailAuth.DeleteSite"], MessageType.Warning);
} }
} }
catch (Exception ex) catch (Exception ex)
{ {
await logger.LogError(ex, "Error Deleting Site {SiteId} {Error}", PageState.Site.SiteId, ex.Message); await logger.LogError(ex, "Error Deleting Site {SiteId} {Error}", PageState.Site.SiteId, ex.Message);
AddModuleMessage(Localizer["Error.DeleteSite"], MessageType.Error); AddModuleMessage(Localizer["Error.DeleteSite"], MessageType.Error);
} }
} }
private async Task SendEmail() private async Task SendEmail()
{ {
if (_smtphost != "" && _smtpport != "" && _smtpsender != "") if (_smtphost != "" && _smtpport != "" && _smtpsender != "")
{ {
try try
{ {
var settings = await SettingService.GetSiteSettingsAsync(PageState.Site.SiteId); var settings = await SettingService.GetSiteSettingsAsync(PageState.Site.SiteId);
SettingService.SetSetting(settings, "SMTPHost", _smtphost); SettingService.SetSetting(settings, "SMTPHost", _smtphost);
SettingService.SetSetting(settings, "SMTPPort", _smtpport); SettingService.SetSetting(settings, "SMTPPort", _smtpport);
SettingService.SetSetting(settings, "SMTPSSL", _smtpssl); SettingService.SetSetting(settings, "SMTPSSL", _smtpssl);
SettingService.SetSetting(settings, "SMTPUsername", _smtpusername); SettingService.SetSetting(settings, "SMTPUsername", _smtpusername);
SettingService.SetSetting(settings, "SMTPPassword", _smtppassword); SettingService.SetSetting(settings, "SMTPPassword", _smtppassword);
SettingService.SetSetting(settings, "SMTPSender", _smtpsender); SettingService.SetSetting(settings, "SMTPSender", _smtpsender);
await SettingService.UpdateSiteSettingsAsync(settings, PageState.Site.SiteId); await SettingService.UpdateSiteSettingsAsync(settings, PageState.Site.SiteId);
await logger.LogInformation("Site SMTP Settings Saved"); await logger.LogInformation("Site SMTP Settings Saved");
await NotificationService.AddNotificationAsync(new Notification(PageState.Site.SiteId, PageState.User.DisplayName, PageState.User.Email, PageState.User.DisplayName, PageState.User.Email, PageState.Site.Name + " SMTP Configuration Test", "SMTP Server Is Configured Correctly.")); await NotificationService.AddNotificationAsync(new Notification(PageState.Site.SiteId, PageState.User.DisplayName, PageState.User.Email, PageState.User.DisplayName, PageState.User.Email, PageState.Site.Name + " SMTP Configuration Test", "SMTP Server Is Configured Correctly."));
AddModuleMessage(Localizer["Info.Smtp.SaveSettings"], MessageType.Info); AddModuleMessage(Localizer["Info.Smtp.SaveSettings"], MessageType.Info);
} }
catch (Exception ex) catch (Exception ex)
{ {
await logger.LogError(ex, "Error Testing SMTP Configuration"); await logger.LogError(ex, "Error Testing SMTP Configuration");
AddModuleMessage(Localizer["Error.Smtp.TestConfig"], MessageType.Error); AddModuleMessage(Localizer["Error.Smtp.TestConfig"], MessageType.Error);
} }
} }
else else
{ {
AddModuleMessage(Localizer["Message.required.Smtp"], MessageType.Warning); AddModuleMessage(Localizer["Message.required.Smtp"], MessageType.Warning);
} }
} }
private async Task GetAliases()
{
_urls = string.Empty;
_defaultalias = string.Empty;
_aliases = await AliasService.GetAliasesAsync();
foreach (Alias alias in _aliases.Where(item => item.SiteId == PageState.Site.SiteId && item.TenantId == PageState.Site.TenantId).ToList())
{
_urls += (_urls == string.Empty) ? alias.Name : ", " + alias.Name;
if (alias.IsDefault && string.IsNullOrEmpty(_defaultalias))
{
_defaultalias = alias.Name;
}
}
}
} }

View File

@ -1,5 +1,6 @@
@namespace Oqtane.Modules.Admin.Sites @namespace Oqtane.Modules.Admin.Sites
@using Oqtane.Interfaces @using Oqtane.Interfaces
@using System.Text.RegularExpressions
@inherits ModuleBase @inherits ModuleBase
@inject NavigationManager NavigationManager @inject NavigationManager NavigationManager
@inject ITenantService TenantService @inject ITenantService TenantService
@ -282,6 +283,7 @@ else
{ {
if (_tenantid != "-" && _name != string.Empty && _urls != string.Empty && _themetype != "-" && _containertype != "-" && _sitetemplatetype != "-") if (_tenantid != "-" && _name != string.Empty && _urls != string.Empty && _themetype != "-" && _containertype != "-" && _sitetemplatetype != "-")
{ {
_urls = Regex.Replace(_urls, @"\r\n?|\n", ",");
var duplicates = new List<string>(); var duplicates = new List<string>();
var aliases = await AliasService.GetAliasesAsync(); var aliases = await AliasService.GetAliasesAsync();
foreach (string name in _urls.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries)) foreach (string name in _urls.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries))

View File

@ -43,7 +43,7 @@ else
_sites = new List<Alias>(); _sites = new List<Alias>();
foreach (Alias alias in aliases) foreach (Alias alias in aliases)
{ {
if (!_sites.Exists(item => item.TenantId == alias.TenantId && item.SiteId == alias.SiteId)) if (alias.IsDefault && !_sites.Exists(item => item.TenantId == alias.TenantId && item.SiteId == alias.SiteId))
{ {
_sites.Add(alias); _sites.Add(alias);
} }
@ -52,16 +52,11 @@ else
private void Edit(string name) private void Edit(string name)
{ {
if (name.Equals("*")) NavigationManager.NavigateTo(_scheme + name + "/admin/site", true);
{
var uri = new Uri(NavigationManager.Uri);
name = uri.Authority;
}
NavigationManager.NavigateTo(_scheme + name + "/admin/site/?reload");
} }
private void Browse(string name) private void Browse(string name)
{ {
NavigationManager.NavigateTo(_scheme + name + "/?reload"); NavigationManager.NavigateTo(_scheme + name, true);
} }
} }

View File

@ -129,9 +129,6 @@
<data name="DefaultContainer.Text" xml:space="preserve"> <data name="DefaultContainer.Text" xml:space="preserve">
<value>Default Container: </value> <value>Default Container: </value>
</data> </data>
<data name="Appearance.Heading" xml:space="preserve">
<value>Appearance</value>
</data>
<data name="DefaultAdminContainer" xml:space="preserve"> <data name="DefaultAdminContainer" xml:space="preserve">
<value>Default Admin Container</value> <value>Default Admin Container</value>
</data> </data>
@ -223,7 +220,7 @@
<value>Aliases: </value> <value>Aliases: </value>
</data> </data>
<data name="IsDeleted.Text" xml:space="preserve"> <data name="IsDeleted.Text" xml:space="preserve">
<value>Is Deleted? </value> <value>Deleted? </value>
</data> </data>
<data name="Logo.Text" xml:space="preserve"> <data name="Logo.Text" xml:space="preserve">
<value>Logo: </value> <value>Logo: </value>
@ -318,4 +315,13 @@
<data name="DeleteSite.Text" xml:space="preserve"> <data name="DeleteSite.Text" xml:space="preserve">
<value>Delete Site</value> <value>Delete Site</value>
</data> </data>
<data name="DefaultAlias.HelpText" xml:space="preserve">
<value>The default alias for the site. Requests for non-default aliases will be redirected to the default alias.</value>
</data>
<data name="DefaultAlias.Text" xml:space="preserve">
<value>Default Alias: </value>
</data>
<data name="Aliases.Heading" xml:space="preserve">
<value>Aliases</value>
</data>
</root> </root>

View File

@ -152,7 +152,7 @@ namespace Oqtane.Services
} }
else else
{ {
if (setting.SettingValue != kvp.Value) if (setting.SettingValue != value || setting.IsPublic != ispublic)
{ {
setting.SettingValue = value; setting.SettingValue = value;
setting.IsPublic = ispublic; setting.IsPublic = ispublic;

View File

@ -367,7 +367,9 @@ namespace Oqtane.Infrastructure
tenant = db.Tenant.FirstOrDefault(item => item.Name == install.TenantName); tenant = db.Tenant.FirstOrDefault(item => item.Name == install.TenantName);
} }
foreach (var aliasName in install.Aliases.Split(new[] {','}, StringSplitOptions.RemoveEmptyEntries)) var aliasNames = install.Aliases.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries).Select(sValue => sValue.Trim()).ToArray();
var firstAlias = aliasNames[0];
foreach (var aliasName in aliasNames)
{ {
if (tenant != null) if (tenant != null)
{ {
@ -376,6 +378,7 @@ namespace Oqtane.Infrastructure
Name = aliasName, Name = aliasName,
TenantId = tenant.TenantId, TenantId = tenant.TenantId,
SiteId = -1, SiteId = -1,
IsDefault = (aliasName == firstAlias),
CreatedBy = "", CreatedBy = "",
CreatedOn = DateTime.UtcNow, CreatedOn = DateTime.UtcNow,
ModifiedBy = "", ModifiedBy = "",
@ -558,7 +561,8 @@ namespace Oqtane.Infrastructure
{ {
// set the alias explicitly so the tenant can be resolved // set the alias explicitly so the tenant can be resolved
var aliases = scope.ServiceProvider.GetRequiredService<IAliasRepository>(); var aliases = scope.ServiceProvider.GetRequiredService<IAliasRepository>();
var firstAlias = install.Aliases.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries)[0]; var aliasNames = install.Aliases.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries).Select(sValue => sValue.Trim()).ToArray();
var firstAlias = aliasNames[0];
var alias = aliases.GetAliases().FirstOrDefault(item => item.Name == firstAlias); var alias = aliases.GetAliases().FirstOrDefault(item => item.Name == firstAlias);
var tenantManager = scope.ServiceProvider.GetRequiredService<ITenantManager>(); var tenantManager = scope.ServiceProvider.GetRequiredService<ITenantManager>();
tenantManager.SetAlias(alias); tenantManager.SetAlias(alias);
@ -650,7 +654,7 @@ namespace Oqtane.Infrastructure
} }
} }
foreach (var aliasName in install.Aliases.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries)) foreach (var aliasName in aliasNames)
{ {
alias = aliases.GetAliases().FirstOrDefault(item => item.Name == aliasName); alias = aliases.GetAliases().FirstOrDefault(item => item.Name == aliasName);
if (alias != null) if (alias != null)

View File

@ -0,0 +1,31 @@
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Oqtane.Databases.Interfaces;
using Oqtane.Migrations.EntityBuilders;
using Oqtane.Repository;
namespace Oqtane.Migrations.Master
{
[DbContext(typeof(MasterDBContext))]
[Migration("Master.03.00.02.01")]
public class AddAliasRedirect : MultiDatabaseMigration
{
public AddAliasRedirect(IDatabase database) : base(database)
{
}
protected override void Up(MigrationBuilder migrationBuilder)
{
//Add Column to Alias table
var aliasEntityBuilder = new AliasEntityBuilder(migrationBuilder, ActiveDatabase);
aliasEntityBuilder.AddBooleanColumn("IsDefault", true);
aliasEntityBuilder.UpdateColumn("IsDefault", "1", "bool", "");
}
protected override void Down(MigrationBuilder migrationBuilder)
{
var aliasEntityBuilder = new AliasEntityBuilder(migrationBuilder, ActiveDatabase);
aliasEntityBuilder.DropColumn("IsDefault");
}
}
}

View File

@ -18,6 +18,7 @@ using Microsoft.AspNetCore.Mvc;
using Microsoft.Net.Http.Headers; using Microsoft.Net.Http.Headers;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using System.Security.Claims; using System.Security.Claims;
using System.Net;
namespace Oqtane.Pages namespace Oqtane.Pages
{ {
@ -32,8 +33,9 @@ namespace Oqtane.Pages
private readonly IPageRepository _pages; private readonly IPageRepository _pages;
private readonly IUrlMappingRepository _urlMappings; private readonly IUrlMappingRepository _urlMappings;
private readonly IVisitorRepository _visitors; private readonly IVisitorRepository _visitors;
private readonly IAliasRepository _aliases;
public HostModel(IConfiguration configuration, ITenantManager tenantManager, ILocalizationManager localizationManager, ILanguageRepository languages, IAntiforgery antiforgery, ISiteRepository sites, IPageRepository pages, IUrlMappingRepository urlMappings, IVisitorRepository visitors) public HostModel(IConfiguration configuration, ITenantManager tenantManager, ILocalizationManager localizationManager, ILanguageRepository languages, IAntiforgery antiforgery, ISiteRepository sites, IPageRepository pages, IUrlMappingRepository urlMappings, IVisitorRepository visitors, IAliasRepository aliases)
{ {
_configuration = configuration; _configuration = configuration;
_tenantManager = tenantManager; _tenantManager = tenantManager;
@ -44,6 +46,7 @@ namespace Oqtane.Pages
_pages = pages; _pages = pages;
_urlMappings = urlMappings; _urlMappings = urlMappings;
_visitors = visitors; _visitors = visitors;
_aliases = aliases;
} }
public string AntiForgeryToken = ""; public string AntiForgeryToken = "";
@ -77,11 +80,25 @@ namespace Oqtane.Pages
var alias = _tenantManager.GetAlias(); var alias = _tenantManager.GetAlias();
if (alias != null) if (alias != null)
{ {
Route route = new Route(HttpContext.Request.GetEncodedUrl(), alias.Path); var url = WebUtility.UrlDecode(HttpContext.Request.GetEncodedUrl());
// redirect non-default alias
if (!alias.IsDefault)
{
var redirect = _aliases.GetAliases()
.Where(item => item.TenantId == alias.TenantId && item.SiteId == alias.SiteId && item.IsDefault)
.FirstOrDefault();
if (redirect != null)
{
return RedirectPermanent(url.Replace(alias.Name, redirect.Name));
}
}
var site = _sites.GetSite(alias.SiteId); var site = _sites.GetSite(alias.SiteId);
if (site != null) if (site != null)
{ {
Route route = new Route(url, alias.Path);
if (!string.IsNullOrEmpty(site.Runtime)) if (!string.IsNullOrEmpty(site.Runtime))
{ {
Runtime = site.Runtime; Runtime = site.Runtime;
@ -128,7 +145,7 @@ namespace Oqtane.Pages
else else
{ {
// page does not exist // page does not exist
var url = route.SiteUrl + "/" + route.PagePath; url = route.SiteUrl + "/" + route.PagePath;
var urlMapping = _urlMappings.GetUrlMapping(site.SiteId, url); var urlMapping = _urlMappings.GetUrlMapping(site.SiteId, url);
if (urlMapping != null && !string.IsNullOrEmpty(urlMapping.MappedUrl)) if (urlMapping != null && !string.IsNullOrEmpty(urlMapping.MappedUrl))
{ {

View File

@ -4,6 +4,7 @@ using System.Linq;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Caching.Memory; using Microsoft.Extensions.Caching.Memory;
using Oqtane.Models; using Oqtane.Models;
using Oqtane.Shared;
namespace Oqtane.Repository namespace Oqtane.Repository
{ {
@ -72,7 +73,7 @@ namespace Oqtane.Repository
int start = segments.Length; int start = segments.Length;
for (int i = 0; i < segments.Length; i++) for (int i = 0; i < segments.Length; i++)
{ {
if (segments[i] == "api" || segments[i] == "pages" || segments[i] == "*") if (segments[i] == "api" || segments[i] == "pages" || segments[i] == Constants.ModuleDelimiter)
{ {
start = i; start = i;
break; break;
@ -89,8 +90,18 @@ namespace Oqtane.Repository
} }
} }
// return fallback alias if none found // auto register alias if there is only a single tenant/site
return alias ?? aliases.Find(item => item.Name.Equals("*")); if (alias == null && aliases.Select(item => new { item.TenantId, item.SiteId }).Distinct().Count() == 1)
{
alias = new Alias();
alias.TenantId = aliases.First().TenantId;
alias.SiteId = aliases.First().SiteId;
alias.Name = url;
alias.IsDefault = false;
alias = AddAlias(alias);
}
return alias;
} }
public void DeleteAlias(int aliasId) public void DeleteAlias(int aliasId)

View File

@ -30,6 +30,11 @@ namespace Oqtane.Models
/// </summary> /// </summary>
public int SiteId { get; set; } public int SiteId { get; set; }
/// <summary>
/// Specifies if the alias is the default for the tenant/site. Requests for non-default aliases are redirected to the default alias.
/// </summary>
public bool IsDefault { get; set; }
/// <inheritdoc /> /// <inheritdoc />
public string CreatedBy { get; set; } public string CreatedBy { get; set; }
@ -62,5 +67,6 @@ namespace Oqtane.Models
} }
} }
} }
} }
} }

View File

@ -30,7 +30,7 @@ namespace Oqtane.Models
Action = ""; Action = "";
UrlParameters = ""; UrlParameters = "";
if (AliasPath.Length != 0) if (AliasPath.Length != 0 && PagePath.StartsWith("/" + AliasPath))
{ {
PagePath = PagePath.Substring(AliasPath.Length + 1); PagePath = PagePath.Substring(AliasPath.Length + 1);
} }